diff --git a/site/astro.config.ts b/site/astro.config.ts index 0737ddf1..d6f6d19f 100644 --- a/site/astro.config.ts +++ b/site/astro.config.ts @@ -19,7 +19,6 @@ import icon from "astro-icon"; import rehypeFigure from "@microflash/rehype-figure"; import remarkGfm from "remark-gfm-no-autolink"; import cooklang from './cooklang-astro'; - // https://astro.build/config export default defineConfig({ site: "https://briansunter.com", diff --git a/site/cooklang-astro.ts b/site/cooklang-astro.ts index e4d3f6a4..01874c32 100644 --- a/site/cooklang-astro.ts +++ b/site/cooklang-astro.ts @@ -39,6 +39,7 @@ const stepItemSchema = z.union([ export const recipeSchema = { slug: z.string().optional(), ingredients: z.array(ingredientSchema).default([]), + cooklang: z.string(), cookwares: z.array(cookwareSchema).default([]), metadata: z.object({ title: z.string(), @@ -97,6 +98,7 @@ function getEntryInfo({ fileUrl, contents }: EntryInfoInput): EntryInfoOutput { metadata, shoppingList, steps, + cooklang: contents, }; return { slug, diff --git a/site/package-lock.json b/site/package-lock.json index dd64d5e8..5aa1457c 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -1533,9 +1533,9 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.0.tgz", - "integrity": "sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.2.tgz", + "integrity": "sha512-GXbTyNP6HlxpyWMR4eirW54Cxp84nVuivcV5hGVBgKnIl+UmD4AJgCX1uXuNRcFFAw58lB3HqryuezIc0iCLgw==" }, "node_modules/@swc/helpers": { "version": "0.5.7", @@ -9008,11 +9008,11 @@ } }, "node_modules/shiki": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.0.tgz", - "integrity": "sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.2.tgz", + "integrity": "sha512-nqazfFgrU+DBLqk4+WjmGQz8sVWkcUcGriHqSM2zGk0GhjirVz4FyJ3AABEx91OpjGiKpuKBg2diYfRfQG3Fbg==", "dependencies": { - "@shikijs/core": "1.2.0" + "@shikijs/core": "1.2.2" } }, "node_modules/signal-exit": { diff --git a/site/src/components/CopyCodeButton.astro b/site/src/components/CopyCodeButton.astro new file mode 100644 index 00000000..33ecd89d --- /dev/null +++ b/site/src/components/CopyCodeButton.astro @@ -0,0 +1,37 @@ +--- +export interface Props { + code: string; +} + +const { code } = Astro.props; +--- + + + \ No newline at end of file diff --git a/site/src/content/recipes/avocado-toast.cook b/site/src/content/recipes/avocado-toast.cook index d75a2a93..88528a6e 100644 --- a/site/src/content/recipes/avocado-toast.cook +++ b/site/src/content/recipes/avocado-toast.cook @@ -23,4 +23,4 @@ Optionally, Drizzle with the @chili oil{} Add a generous pinch of flaky sea salt -Tear some basil and sprinkle on top \ No newline at end of file +Tear some @basil and sprinkle on top \ No newline at end of file diff --git a/site/src/content/recipes/banana-pancakes.cook b/site/src/content/recipes/banana-pancakes.cook index f96c8ac4..615cec51 100644 --- a/site/src/content/recipes/banana-pancakes.cook +++ b/site/src/content/recipes/banana-pancakes.cook @@ -1,6 +1,6 @@ >> title: Banana Pancakes >> description: You can make healthy pancakes out of a fluffy banana egg mixture. ->>source: https://www.amazon.com/Joshua-Weissman-Unapologetic-Cookbook/dp/1615649980?&_encoding=UTF8&tag=bsunter-20&linkCode=ur2&linkId=5300e43d60cfb8b95202ef018574abce&camp=1789&creative=9325 +>> source: https://www.amazon.com/Joshua-Weissman-Unapologetic-Cookbook/dp/1615649980?&_encoding=UTF8&tag=bsunter-20&linkCode=ur2&linkId=5300e43d60cfb8b95202ef018574abce&camp=1789&creative=9325 >> tags: breakfast, vegetarian Combine @flour{120%g}, @white sugar{13%g}, @baking powder{2%tsp}, and @salt{1%g} in a bowl. Mix together @egg{3}, @milk{240%g}, @avocado oil{26%g}, and @bananas{3} in a second bowl. diff --git a/site/src/content/recipes/kalbi-marinate.cook b/site/src/content/recipes/kalbi-marinate.cook index 449366ac..cbec43db 100644 --- a/site/src/content/recipes/kalbi-marinate.cook +++ b/site/src/content/recipes/kalbi-marinate.cook @@ -1,23 +1,13 @@ ->> title: Kalbi Marinate ->> description: For barbecued and braised meats, roasted vegetables, and salads. A classic marinade for kalbi, or grilled short ribs. It's garlicky, sweet, and savory with a nutty sesame flavor. Can be used as a braising sauce or salad dressing as well. ->> source: https://masterclass.com ->> tags: korean, asian, sauce +>> title: Kalbi Marinade +>> description: A versatile marinade that can be used for BBQ Kalbi and BBQ Mixed Vegetables. You can swap in pears or pineapple for the kiwifruit. +>> yield: About 6 cups +>> prep time: 5 minutes +>> tags: marinade, sauce, korean - It’s one of those recipes that every practitioner makes a little differently, but it’s always You’ll use this sauce when making the BBQ Kalbi (see page 20) and BBQ Mixed Vegetables (see page 21), but it’s not just a marinade; it . -Roy blends in the tangy flesh of the kiwifruit here, but -you can swap in pears or pineapple or whatever fruits are available. Mirin, a sweet Japanese rice wine, can be found at Asian markets or online; if you don’t have a bottle, use apple juice instead. - INGREDIENTS -1 large onion, peeled and roughly chopped -1 cup peeled garlic cloves -1 bunch scallions (about 8 scallions), roughly chopped -5 tbsp toasted sesame seeds 1 cup sugar -2 large kiwifruits -2 cups soy sauce -1 cup mirin, apple juice, or water 2 cups fresh orange juice -1 cup toasted sesame oil -METHOD - 17 +Add the @onion{1%large}, peeled and roughly chopped, @peeled garlic cloves{1%cup}, @scallions{1%bunch} (about 8 scallions), roughly chopped, @toasted sesame seeds{5%tbsp}, and @sugar{1%cup} to a clean #blender jar. -Add the onion, garlic, scallions, sesame seeds, and sugar to a clean blender jar. +Halve the @kiwifruits{2%large}, and use a #spoon to scoop out the flesh. Add the flesh to the #blender, and toss out the skins. -Halve the kiwifruits, and use a spoon to scoop out the flesh. Add the flesh to the blender, and toss out the skins. Add the remain- ing ingredients, and purée the mix- ture on high speed just until a smooth sauce forms, about 10 sec- onds. Pour the marinade into a sealable jar or container, and store it in the refrigerator for up to 1 week. \ No newline at end of file +Add the @soy sauce{2%cups}, @mirin{1%cup}, @apple juice{1%cup}, or @water{1%cup}, @fresh orange juice{2%cups}, and @toasted sesame oil{1%cup}, and purée the mixture on high speed just until a smooth sauce forms, about ~{10%seconds}. + +Pour the marinade into a #sealable jar{} or container, and store it in the #refrigerator for up to 1 week. \ No newline at end of file diff --git a/site/src/content/recipes/kombucha.cook b/site/src/content/recipes/kombucha.cook deleted file mode 100644 index cb963314..00000000 --- a/site/src/content/recipes/kombucha.cook +++ /dev/null @@ -1,3 +0,0 @@ ->>source: https://www.amazon.com/Joshua-Weissman-Unapologetic-Cookbook/dp/1615649980?&_encoding=UTF8&tag=bsunter-20&linkCode=ur2&linkId=5300e43d60cfb8b95202ef018574abce&camp=1789&creative=9325 ->>title: Lacto Fermented Vegetables ->>description: These are really funky pickled vegetables made with just salt and water (no added vinegar). The vinegar comes from natural fermentation. diff --git a/site/src/content/recipes/lechon-kawali-sisig.cook b/site/src/content/recipes/lechon-kawali-sisig.cook index 14325ae3..e2489965 100644 --- a/site/src/content/recipes/lechon-kawali-sisig.cook +++ b/site/src/content/recipes/lechon-kawali-sisig.cook @@ -1,9 +1,9 @@ >>source: https://www.soulsousvide.com/home/lechon-kawali >>title: Sous Vide Lechon Kawali Sisig >>description: Crispy fried pork belly with stir fried onions ->>tags:main, filipino +>>tags:main, filipino, asian - In a #vacuum sealable bag{}, combine @pork belly{4%lbs}, @pounded garlic{1/2%head}, @salt{1%tbsp}, @cracked peppercorns{1.5%tsp}, and @bay leaves {} . +In a #vacuum sealable bag{}, combine @pork belly{4%lbs}, @pounded garlic{1/2%head}, @salt{1%tbsp}, @cracked peppercorns{1.5%tsp}, and @bay leaves{} . #Sous vide{} for ~{24%hours} at 154° F. @@ -19,7 +19,7 @@ Dice @yellow onions{2} Chop pork belley into ½-inch pieces and place it into a large mixing bowl. -Add @lemon juice{1/4%cup} , @cane vinegar{1/4%cup} and @soy sauce{1/2%cup} , and @black pepper{1%tsp} . +Add @lemon juice{1/4%cup}, @cane vinegar{1/4%cup}, and @soy sauce{1/2%cup} , and @black pepper{1%tsp} . Heat onions and pork in a large cast iron plate at 450°F for 10 to 15 minutes. diff --git a/site/src/content/recipes/lime-soda.cook b/site/src/content/recipes/lime-soda.cook index 4651c299..a5b5710c 100644 --- a/site/src/content/recipes/lime-soda.cook +++ b/site/src/content/recipes/lime-soda.cook @@ -9,7 +9,7 @@ Make simple syrup by first boiling together equal parts sugar and water, then letting it cool. -Fill a glass with ice cubes, then add @simple syrup{30%ml} (2 tbsp) and @freshly squeezed lime juice{20%ml} (1.5%tbsp) (use regular supermarket persian limes). +Fill a glass with ice cubes, then add @simple syrup{30%ml} (2 tbsp) and @freshly squeezed lime juice{20%ml} (1.5 tbsp) (use regular supermarket persian limes). Add @soda water{240%ml} (1 cup). diff --git a/site/src/content/recipes/lumpia-shanghai.cook b/site/src/content/recipes/lumpia-shanghai.cook index e261d99d..6ddfaf43 100644 --- a/site/src/content/recipes/lumpia-shanghai.cook +++ b/site/src/content/recipes/lumpia-shanghai.cook @@ -5,12 +5,15 @@ Combine @ground pork (30%fat) {680%g}, @minced carrot{1}, @minced red onion{1/2}, @minced celery stalks{3}, @garlic{6%cloves}, @egg{1}, @soy sauce{2%tbsp}, @salt{1%tsp}, @ground black pepper{1.5%tsp}, @salt{1%tsp}, @sugar{1%tsp} in a #big bowl{}. Use a spatula to mix it in a circular motion, until everything comes together and the pork feels elastic. It might take a few minutes to get this consistency. -Place the @lumpia wrapper{}s on a plate and cover with a damp towel to prevent from drying out. -Take a wrapper and lay it in front of you with the pointed edge facing up. Take a heaping tablespoon of filling and place it a few inches from the corner closest to you. Shape the filling into a 4 to 5" (10 to 12 cm) strip. Fold the bottom corner over the filling, then fold the two side corners. Roll the filling parcel up so the folded edges are tucked in. Gently use your finger to press out any air bubbles and make sure the filling is sealed in tightly. Continue rolling up the filling until just before it is completely sealed. Dab the upper corner with a small amount of egg wash with your finger, then fold the filling over to finish wrapping the lumpia. Set aside. Repeat with the remaining filling and wrappers. +Place the @lumpia wrapper{}s on a plate and cover with a damp towel to prevent from drying out. Take a wrapper and lay it in front of you with the pointed edge facing up. -Once you wrap the lumpia, you can store them uncooked in a large Ziplock bag with as much air squeezed out as possible, for up to 3 months in the freezer. +Take a heaping tablespoon of filling and place it a few inches from the corner closest to you. Shape the filling into a 4 to 5" (10 to 12 cm) strip. + +Fold the bottom corner over the filling, then fold the two side corners. Roll the filling parcel up so the folded edges are tucked in. Gently use your finger to press out any air bubbles and make sure the filling is sealed in tightly. Continue rolling up the filling until just before it is completely sealed. Dab the upper corner with a small amount of egg wash with your finger, then fold the filling over to finish wrapping the lumpia. Set aside. -To cook the frozen lumpia, cook them according to the instructions below without thawing. The cooking time will take slightly longer. +Repeat with the remaining filling and wrappers. + +Once you wrap the lumpia, you can store them uncooked in a large Ziplock bag with as much air squeezed out as possible, for up to 3 months in the freezer. Add the @peanut oil{} to a #medium deep pan{} or pot so that it comes about 1" (2.5cm) up. Heat over medium-high heat until it reaches 350°F (176°C). diff --git a/site/src/content/recipes/milk-bread.cook b/site/src/content/recipes/milk-bread.cook index 2d22c32d..3e589192 100644 --- a/site/src/content/recipes/milk-bread.cook +++ b/site/src/content/recipes/milk-bread.cook @@ -39,4 +39,4 @@ Sweat garlic for ~{30%s} and then remove from the heat and let the residual heat Remove buns from oven and immediately add the garlic butter. -Add maldon salt to the top. \ No newline at end of file +Add @maldon flaky sea salt{} to the top. \ No newline at end of file diff --git a/site/src/content/recipes/pork-dumplings.cook b/site/src/content/recipes/pork-dumplings.cook index d0ecef3e..63ae6a13 100644 --- a/site/src/content/recipes/pork-dumplings.cook +++ b/site/src/content/recipes/pork-dumplings.cook @@ -1,6 +1,7 @@ >>title: Pork Dumplings ->>description: Fried pork dumplings ->>tags: chinese, pork, dumplings +>>description: +>>description: Delicious and savory pork dumplings, pan-fried to perfection. +>>tags: chinese, pork, dumplings, pan-fried Mix together the @pork{1/2%lb}, @sliced scallions{1%cup}, @grated ginger{1%tbsp}, @soy sauce{2%tbsp}, @sesame oil{1%tbsp}, @re-hydrated chinese mushrooms{4}, @cilantro{1%tbsp} and @bamboo shoots or water chestnuts{1/2%cup}. diff --git a/site/src/content/recipes/quick-pickled-vegetables.cook b/site/src/content/recipes/quick-pickled-vegetables.cook index e2267a66..34303e44 100644 --- a/site/src/content/recipes/quick-pickled-vegetables.cook +++ b/site/src/content/recipes/quick-pickled-vegetables.cook @@ -1,6 +1,6 @@ >>source: https://www.amazon.com/Joshua-Weissman-Unapologetic-Cookbook/dp/1615649980?&_encoding=UTF8&tag=bsunter-20&linkCode=ur2&linkId=5300e43d60cfb8b95202ef018574abce&camp=1789&creative=9325 >>title: Quick Pickled Vegetables ->>description: I like quick pickled vegetables just as much as naturally fermented personally. This recipe is easy, works with many vegetables, and keeps forever. +>>description: I like quick pickled vegetables just as much as naturally fermented. This recipe is easy, works with many vegetables, and keeps for months. >>tags:fermented, sauce, side Fill a large jar with the desired vegetables and any fresh herbs and garlic (for example, cucumbers and sprigs of sage), leaving a bit of headspace. diff --git a/site/src/content/recipes/ratatouille.cook b/site/src/content/recipes/ratatouille.cook index 9a07f189..cab00100 100644 --- a/site/src/content/recipes/ratatouille.cook +++ b/site/src/content/recipes/ratatouille.cook @@ -7,7 +7,7 @@ Preheat the oven for 375˚F (190˚C). Slice the @eggplant{2}, @tomatoes,{6} @squash{2}, and @zucchini{2} into approximately ¹⁄₁₆-inch (1-mm) rounds, then set aside. -For the sauce: Heat the @olive oil{2%tbsp} in a 12-inch (30-cm) oven-safe pan over medium-high heat. +For the sauce: Heat the @olive oil{2%tbsp} in a #12-inch (30-cm) oven-safe pan{} over medium-high heat. Sauté the @diced onion{1}, @minced garlic{4%cloves}, @diced bell pepper{2}, until soft, about ~{10%minutes}. Season with @salt and @pepper, then add the @crushed tomatoes{28%oz}. diff --git a/site/src/lib/cooklangSyntax.json b/site/src/lib/cooklangSyntax.json new file mode 100644 index 00000000..093173b6 --- /dev/null +++ b/site/src/lib/cooklangSyntax.json @@ -0,0 +1,534 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "scopeName": "source.cook", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#metadata" + }, + { + "include": "#step" + } + ], + "repository": { + "$self": { + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#metadata" + }, + { + "include": "#step" + } + ] + }, + "$base": { + "patterns": [] + }, + "metadata": { + "patterns": [ + { + "match": "(^>>)$", + "name": "keyword.cook.punctuation.section.mapping.begin.cook" + }, + { + "match": "(^>>)([^:]+?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.section.mapping.begin.cook" + }, + "2": { + "name": "invalid.illegal.expected-mapping-key.cook" + } + } + }, + { + "match": "(^>>)([^:]+?)(:\\s*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.section.mapping.begin.cook" + }, + "2": { + "name": "entity.name.tag.metadata.cook string.unquoted.plain.out.cook" + }, + "3": { + "name": "invalid.illegal.expected-mapping-key.cook" + } + } + }, + { + "match": "(^>>)([^:]+?)(:)(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.section.mapping.begin.cook" + }, + "2": { + "name": "entity.name.tag.metadata.cook string.unquoted.plain.out.cook" + }, + "3": { + "name": "keyword.cook.punctuation.separator.mapping.key-value.cook" + }, + "4": { + "name": "value.metadata.cook string.unquoted.plain.out.cook" + } + } + } + ] + }, + "step": { + "patterns": [ + { + "include": "#ingredient" + }, + { + "include": "#equipment" + }, + { + "include": "#timer" + } + ] + }, + "ingredient": { + "patterns": [ + { + "match": "(@)(\\()([^)]*?)(\\))", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "name": "keyword.cook.punctuation.definition.ingredient.modifier.begin.cook" + }, + "3": { + "name": "variable.cook.constant.string.ingredient.modifier.cook" + }, + "4": { + "name": "keyword.cook.punctuation.definition.ingredient.modifier.end.cook" + } + } + }, + { + "match": "(@)(\\()(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "name": "invalid.illegal.expected-end-of-modifier.cook" + }, + "3": { + "name": "invalid.illegal.expected-end-of-modifier.cook" + } + } + }, + { + "match": "(@)(\\w*?|[^@#~]+?)({)([^}]*?)(})(\\()([^)]*?)(\\))", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "patterns": [ + { + "include": "#ingredient_word" + } + ] + }, + "3": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.begin.cook" + }, + "4": { + "patterns": [ + { + "include": "#ingredient_amount" + } + ] + }, + "5": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.end.cook" + }, + "6": { + "name": "keyword.cook.punctuation.definition.ingredient.modifier.begin.cook" + }, + "7": { + "name": "variable.cook.constant.string.ingredient.modifier.cook" + }, + "8": { + "name": "keyword.cook.punctuation.definition.ingredient.modifier.end.cook" + } + } + }, + { + "match": "(@)(\\w*?|[^@#~]+?)({)([^}]*?)(})(\\()(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "patterns": [ + { + "include": "#ingredient_word" + } + ] + }, + "3": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.begin.cook" + }, + "4": { + "patterns": [ + { + "include": "#ingredient_amount" + } + ] + }, + "5": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.end.cook" + }, + "6": { + "name": "invalid.illegal.expected-end-of-modifier.cook" + }, + "7": { + "name": "invalid.illegal.expected-end-of-modifier.cook" + } + } + }, + { + "match": "(@)(\\w*?|[^@#~]+?)({)([^}]*?)(})", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "patterns": [ + { + "include": "#ingredient_word" + } + ] + }, + "3": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.begin.cook" + }, + "4": { + "patterns": [ + { + "include": "#ingredient_amount" + } + ] + }, + "5": { + "name": "keyword.cook.punctuation.definition.ingredient.amount.end.cook" + } + } + }, + { + "match": "(@)(\\w*?|[^@#~]+?)({)([^}]*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "patterns": [ + { + "include": "#ingredient_word" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-end-of-amount.cook" + }, + "4": { + "name": "invalid.illegal.expected-end-of-amount.cook" + } + } + }, + { + "match": "(@)(.+?)\\b", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.ingredient.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.ingredient.one-word.cook" + } + } + }, + { + "match": "(@)", + "name": "keyword.cook.punctuation.definition.ingredient.cook" + } + ] + }, + "ingredient_word": { + "patterns": [ + { + "match": "(\\w*?)$", + "name": "entity.name.tag.css.cook.entity.name.tag.ingredient.one-word.cook" + }, + { + "match": "([^@#~]+?)$", + "name": "entity.name.tag.css.cook.entity.name.tag.ingredient.multi-word.cook" + } + ] + }, + "ingredient_amount": { + "patterns": [ + { + "match": "([^}%]+?(?=}|%|$))", + "name": "variable.cook.constant.string.ingredient.amount.cook" + }, + { + "match": "(%)", + "name": "keyword.cook.punctuation.definition.ingredient.amount.separator.cook" + } + ] + }, + "equipment": { + "patterns": [ + { + "match": "(#)(\\w*?)({)[^}]*?(})", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.equipment.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.equipment.one-word.cook" + }, + "3": { + "name": "keyword.cook.punctuation.definition.equipment.details.begin.cook" + }, + "4": { + "name": "keyword.cook.punctuation.definition.equipment.details.end.cook" + } + } + }, + { + "match": "(#)(\\w*?)({)(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.equipment.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.equipment.one-word.cook" + }, + "3": { + "name": "invalid.illegal.expected-end-of-details.cook" + }, + "4": { + "name": "invalid.illegal.expected-end-of-details.cook" + } + } + }, + { + "match": "(#)([^@#~]+?)({)[^{]*?(})", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.equipment.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.equipment.multi-word.cook" + }, + "3": { + "name": "keyword.cook.punctuation.definition.equipment.details.begin.cook" + }, + "4": { + "name": "keyword.cook.punctuation.definition.equipment.details.end.cook" + } + } + }, + { + "match": "(#)([^@#~]+?)({)(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.equipment.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.equipment.multi-word.cook" + }, + "3": { + "name": "invalid.illegal.expected-end-of-details.cook" + }, + "4": { + "name": "invalid.illegal.expected-end-of-details.cook" + } + } + }, + { + "match": "(#)(.+?)\\b", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.equipment.cook" + }, + "2": { + "name": "entity.name.tag.css.cook.entity.name.tag.equipment.one-word.cook" + } + } + }, + { + "match": "(#)", + "name": "keyword.cook.punctuation.definition.equipment.cook" + } + ] + }, + "timer": { + "patterns": [ + { + "match": "(~)$", + "name": "keyword.cook.punctuation.definition.timer.cook" + }, + { + "match": "(~)(\\w*?|[^@#~]+?)({\\s*?})", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.timer.cook" + }, + "2": { + "patterns": [ + { + "include": "#timer_word" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-duration.cook" + } + } + }, + { + "match": "(~)(\\w*?|[^@#~]+?)({)([^}]*?)(})", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.timer.cook" + }, + "2": { + "patterns": [ + { + "include": "#timer_word" + } + ] + }, + "3": { + "name": "keyword.cook.punctuation.definition.timer.duration.begin.cook" + }, + "4": { + "patterns": [ + { + "include": "#timer_duration" + } + ] + }, + "5": { + "name": "keyword.cook.punctuation.definition.timer.duration.end.cook" + } + } + }, + { + "match": "(~)(\\w*?|[^@#~]+?)({)(.*?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.timer.cook" + }, + "2": { + "patterns": [ + { + "include": "#timer_word" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-end-of-duration.cook" + }, + "4": { + "name": "invalid.illegal.expected-end-of-duration.cook" + } + } + }, + { + "match": "(~)([^@#~]+?)$", + "captures": { + "1": { + "name": "keyword.cook.punctuation.definition.timer.cook" + }, + "2": { + "name": "invalid.illegal.expected-begining-of-duration.cook" + } + } + } + ] + }, + "timer_word": { + "patterns": [ + { + "match": "(\\w+?)$", + "name": "entity.name.tag.css.cook.entity.name.tag.timer.one-word.cook" + }, + { + "match": "([^@#~]+?)$", + "name": "entity.name.tag.css.cook.entity.name.tag.timer.multi-word.cook" + } + ] + }, + "timer_duration": { + "patterns": [ + { + "match": "([^}%]+?)", + "name": "variable.cook.constant.string.timer.duration.cook" + }, + { + "match": "(%)", + "name": "keyword.cook.punctuation.definition.timer.duration.separator.cook" + } + ] + }, + "comments": { + "patterns": [ + { + "name": "comment.block.cook", + "begin": "(\\[-)(?:\\s*((@)internal)(?=\\s|(-])))?", + "beginCaptures": { + "1": { + "name": "punctuation.definition.comment.cook" + }, + "2": { + "name": "storage.type.internaldeclaration.cook" + }, + "3": { + "name": "punctuation.decorator.internaldeclaration.cook" + } + }, + "end": "-]", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.cook" + } + } + }, + { + "begin": "(^[ \\t]+)?((--)(?:\\s*((@)internal)(?=\\s|$))?)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.cook" + }, + "2": { + "name": "comment.line.double-dash.cook" + }, + "3": { + "name": "punctuation.definition.comment.cook" + }, + "4": { + "name": "storage.type.internaldeclaration.cook" + }, + "5": { + "name": "punctuation.decorator.internaldeclaration.cook" + } + }, + "end": "(?=$)", + "contentName": "comment.line.double-dash.cook" + } + ] + } + } + } \ No newline at end of file diff --git a/site/src/pages/recipes/[...recipe].astro b/site/src/pages/recipes/[...recipe].astro index 24d48e98..b85e7811 100644 --- a/site/src/pages/recipes/[...recipe].astro +++ b/site/src/pages/recipes/[...recipe].astro @@ -5,6 +5,20 @@ import Header from "../../components/Header.astro"; import Footer from "../../components/Footer.astro"; import { Schema } from "astro-seo-schema"; import type { CollectionEntry } from "astro:content"; +import { Code } from "astro/components"; +import type { LanguageRegistration } from "shiki"; +import type { b } from "@shikijs/core/chunk-tokens.d.mts"; +import CopyCodeButton from "../../components/CopyCodeButton.astro"; +import cooklangLang from "../../lib/cooklangSyntax.json"; + +type IRawRepository = b["repository"]; + +const cooklangRegistration: LanguageRegistration = { + name: "cook", + scopeName: "source.cook", + patterns: cooklangLang.patterns, + repository: cooklangLang.repository as IRawRepository, +}; export async function getStaticPaths() { const blogEntries = await getCollection("recipes"); @@ -18,7 +32,7 @@ export async function getStaticPaths() { const { entry } = Astro.props; const { ingredients, cookwares, metadata, steps, shoppingList } = entry.data; -type Ingredient = CollectionEntry<'recipes'>["data"]["ingredients"][0]; +type Ingredient = CollectionEntry<"recipes">["data"]["ingredients"][0]; const combinedIngredients: Ingredient[] = ingredients.reduce( (acc, curr) => { @@ -40,7 +54,10 @@ const combinedIngredients: Ingredient[] = ingredients.reduce( - + ( totalTime: metadata?.prepTime ? `PT${metadata.prepTime}` : metadata?.cookTime - ? `PT${metadata.cookTime}` - : undefined, + ? `PT${metadata.cookTime}` + : undefined, }} /> @@ -97,114 +114,174 @@ const combinedIngredients: Ingredient[] = ingredients.reduce(

{metadata?.title}

- {metadata?.description && ( -

{metadata.description}

- )} -{ (metadata?.author || metadata?.servings || metadata?.tags) && ( + { + metadata?.description && ( +

+ {metadata.description} +

+ ) + } + { + (metadata?.author || metadata?.servings || metadata?.tags) && ( +
+
+ {metadata?.author && ( +
+ + + + {metadata.author} +
+ )} -
-
- {metadata?.author && ( -
- - - - {metadata.author} -
- )} + {metadata?.servings && ( +
+ + + + + {metadata.servings} servings + +
+ )} - {metadata?.servings && ( -
- - - - {metadata.servings} servings -
- )} - - {metadata?.source && !metadata.source.startsWith('http') && ( -
- - - - {metadata.source} -
- )} -
- {metadata?.tags && ( -
- Tags: - {metadata.tags.map((tag) => ( - - {tag.trim()} - - ))} -
- )} -
-)} + {metadata?.source && !metadata.source.startsWith("http") && ( +
+ + + + {metadata.source} +
+ )} +
+ {metadata?.tags && ( +
+ Tags: + {metadata.tags.map((tag) => ( + + {tag.trim()} + + ))} +
+ )} +
+ ) + }

Ingredients

    - {combinedIngredients.map(({ name, quantity, units }) => ( -
  • - {quantity !== 1 && units ? `${quantity} ${units}` : units ? `1 ${units}` : ""} {name} -
  • - ))} + { + combinedIngredients.map(({ name, quantity, units }) => ( +
  • + {quantity !== 1 && units + ? `${quantity} ${units}` + : units + ? `1 ${units}` + : ""}{" "} + {name} +
  • + )) + }
- {cookwares.length > 0 && ( -
-

Cookware

-
    - {cookwares.map(({ name }) => ( -
  • {name}
  • - ))} -
-
- )} + { + cookwares.length > 0 && ( +
+

Cookware

+
    + {cookwares.map(({ name }) => ( +
  • {name}
  • + ))} +
+
+ ) + }

Steps

- {steps.map((step, index) => ( -
-

Step {index + 1}

-

- {step.map((stepItem) => { - if (stepItem.type === "text") { - return {stepItem.value}; - } else if (stepItem.type === "ingredient") { - return ( - - {stepItem.quantity !== 1 && stepItem.units - ? `${stepItem.quantity} ${stepItem.units}` - : stepItem.units - ? `1 ${stepItem.units}` - : ""}{" "} - {stepItem.name} - - ); - } else if (stepItem.type === "timer") { - return ( - - {stepItem.quantity} {stepItem.units} - - ); - } else if (stepItem.type === "cookware") { - return ( - - {stepItem.name} - - ); - } - })} -

+ { + steps.map((step, index) => ( +
+

Step {index + 1}

+

+ {step.map((stepItem) => { + if (stepItem.type === "text") { + return {stepItem.value}; + } else if (stepItem.type === "ingredient") { + return ( + + {stepItem.quantity !== 1 && stepItem.units + ? `${stepItem.quantity} ${stepItem.units}` + : stepItem.units + ? `1 ${stepItem.units}` + : ""}{" "} + {stepItem.name} + + ); + } else if (stepItem.type === "timer") { + return ( + + {stepItem.quantity} {stepItem.units} + + ); + } else if (stepItem.type === "cookware") { + return ( + + {stepItem.name} + + ); + } + })} +

+
+ )) + } +
+
+
+ Cooklang Recipe +
+ Copy Recipe +
- ))} +