diff --git a/.gitignore b/.gitignore index b9a9c74..5ba760e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.firebase/ +.DS_Store +.firebase/* \ No newline at end of file diff --git a/js/global.js b/js/global.js index 48ac3f1..a1b1ce7 100644 --- a/js/global.js +++ b/js/global.js @@ -71,6 +71,20 @@ const VALID_HTML_ITEM_TYPES = new Set([ const VALID_BUBBLE_SUBTYPES = new Set(['tip', 'warning', 'note']) const VALID_INLINE_IMAGE_FLOWS = new Set(['around', 'over', 'under']) const VALID_SPACER_VARIANTS = new Set(['blank', 'line', 'page', 'container']) +/** Alternate JSON `type` strings from older exports or external templates → spacer variant default */ +const IMPORT_SPACER_TYPE_ALIASES = new Map([ + ['separator', 'line'], + ['hr', 'line'], + ['horizontal_rule', 'line'], + ['horizontalrule', 'line'], + ['rule', 'line'], + ['pagebreak', 'page'], + ['page_break', 'page'], + ['whitespace', 'blank'], + ['empty', 'blank'], + ['gap', 'blank'], + ['space', 'blank'] +]) const VALID_CONTAINER_LAYOUTS = new Set(['flow', 'grid']) const DEFAULT_RECIPE_SETTINGS = Object.freeze({ ...recipeData.settings }) const PRINT_MODAL_ACTION_PRINT = 'print' @@ -91,6 +105,30 @@ const BUILTIN_TEMPLATE_SLOTS = Object.freeze([ name: 'All Item Types', subtitle: 'One sample for every item type', path: 'templates/default/all-items.cookie' + }, + { + slot: 2, + name: "Grandma & Co's Basic Cake Recipe", + subtitle: 'A simple recipe with a title and description', + path: 'templates/default/cake.cookie' + }, + { + slot: 3, + name: 'Assignment', + subtitle: 'A simple essay builder assignment template with references', + path: 'templates/default/assignment.cookie' + }, + { + slot: 4, + name: 'Safety Plan', + subtitle: 'A quick 2 page safety plan that covers essentials...', + path: 'templates/default/safeplan.cookie' + }, + { + slot: 5, + name: 'Basic Email with Signature', + subtitle: 'A basic email with a signature and some template details.', + path: 'templates/default/emailsig.cookie' } ]) let printModalAction = PRINT_MODAL_ACTION_PRINT @@ -417,6 +455,13 @@ function normalizeImportedImageItem (rawItem, normalized) { return normalized } +function normalizeImportedItemTypeKey (rawType) { + return toStringOrFallback(rawType, '') + .trim() + .toLowerCase() + .replace(/-/g, '_') +} + function normalizeImportedSpacerItem (rawItem, normalized) { let variant = toStringOrFallback(rawItem.variant, 'blank') if (!VALID_SPACER_VARIANTS.has(variant)) variant = 'blank' @@ -438,35 +483,46 @@ function normalizeImportedSpacerItem (rawItem, normalized) { function normalizeImportedItem (rawItem, fallbackId) { if (!rawItem || typeof rawItem !== 'object') return null - const type = toStringOrFallback(rawItem.type, '') - if (!VALID_ITEM_TYPES.has(type)) return null + let typeKey = normalizeImportedItemTypeKey(rawItem.type) + let spacerVariantWhenAlias = null + const aliasDefaultVariant = IMPORT_SPACER_TYPE_ALIASES.get(typeKey) + if (aliasDefaultVariant !== undefined) { + typeKey = 'spacer' + spacerVariantWhenAlias = aliasDefaultVariant + } + if (!VALID_ITEM_TYPES.has(typeKey)) return null const normalized = { ...rawItem, id: rawItem.id ?? fallbackId, - type + type: typeKey } - if (type === 'image') { + if (typeKey === 'image') { return normalizeImportedImageItem(rawItem, normalized) } - if (type === 'spacer') { - return normalizeImportedSpacerItem(rawItem, normalized) + if (typeKey === 'spacer') { + const rawVariant = toStringOrFallback(rawItem.variant, '') + const spacerSource = + spacerVariantWhenAlias != null && !VALID_SPACER_VARIANTS.has(rawVariant) + ? { ...rawItem, variant: spacerVariantWhenAlias } + : rawItem + return normalizeImportedSpacerItem(spacerSource, normalized) } normalized.content = toStringOrFallback(rawItem.content, '') - if (type === 'bubble') { + if (typeKey === 'bubble') { normalized.subtype = VALID_BUBBLE_SUBTYPES.has(rawItem.subtype) ? rawItem.subtype : 'note' } - if (type === 'link') { + if (typeKey === 'link') { normalized.href = toStringOrFallback(rawItem.href, '') } applyImportedParentId(normalized, rawItem.parentId) - if (type === 'button') { + if (typeKey === 'button') { normalized.href = toStringOrFallback(rawItem.href, '') const VALID_BUTTON_STYLES = new Set([ 'primary', @@ -478,13 +534,13 @@ function normalizeImportedItem (rawItem, fallbackId) { ? rawItem.buttonStyle : 'primary' } - if (type === 'navmenu') { + if (typeKey === 'navmenu') { normalized.links = toStringOrFallback(rawItem.links, '[]') } - if (type === 'dropdown') { + if (typeKey === 'dropdown') { normalized.options = toStringOrFallback(rawItem.options, '') } - if (type === 'frame') { + if (typeKey === 'frame') { normalized.src = toStringOrFallback(rawItem.src, '') normalized.frameHeight = toFiniteNumberOrFallback(rawItem.frameHeight, 400) } diff --git a/templates/.DS_Store b/templates/.DS_Store new file mode 100644 index 0000000..8055967 Binary files /dev/null and b/templates/.DS_Store differ diff --git a/templates/default/all-items.cookie b/templates/default/all-items.cookie index e118848..e9c003d 100644 --- a/templates/default/all-items.cookie +++ b/templates/default/all-items.cookie @@ -87,6 +87,36 @@ "type": "codescript", "content": "", "scale": 100 + }, + { + "id": 1013, + "type": "spacer", + "variant": "blank", + "size": 48, + "scale": 100 + }, + { + "id": 1014, + "type": "spacer", + "variant": "line", + "size": 0, + "scale": 100 + }, + { + "id": 1015, + "type": "spacer", + "variant": "page", + "size": 0, + "scale": 100 + }, + { + "id": 1016, + "type": "spacer", + "variant": "container", + "size": 0, + "containerLayout": "flow", + "containerColumns": 2, + "scale": 100 } ], "settings": { diff --git a/templates/default/assignment.cookie b/templates/default/assignment.cookie new file mode 100644 index 0000000..355faa5 --- /dev/null +++ b/templates/default/assignment.cookie @@ -0,0 +1,84 @@ +{ + "app": "CookieCut", + "format": "cookie_document", + "version": 1, + "exportedAt": "2026-05-03T04:29:38.753Z", + "recipeData": { + "title": "A great assignment!", + "description": "The story of the seagull that could.", + "items": [ + { + "id": 1777782163624, + "type": "heading", + "content": "Introducing our friends", + "scale": 105, + "inlineWidth": 673, + "inlineMinHeight": 48 + }, + { + "id": 1777782216640, + "type": "text", + "content": "The undisputed kings of the boardwalk, seagulls are less \"majestic seabirds\" and more \"highly organized snack-heist specialists.\" With an uncanny ability to hear a chip bag opening from three miles away, they patrol the coastline with a piercing gaze and zero respect for personal boundaries. Whether they’re performing a mid-air acrobatic feat to intercept a tourist’s fry or simply standing on a pier post looking vaguely judgmental, the seagull remains the ocean’s most chaotic—and loud—ambassador.", + "scale": 80, + "inlineWidth": 667 + }, + { + "id": 1777782278373, + "type": "bullet", + "content": "The Rain Dance (Worm Charming)If you ever see a seagull frantically tapping its feet on a patch of grass like it’s practicing for a talent show, it isn't \"dancing\"—it's simulating a rainstorm. By rapidly drumming their feet, gulls create vibrations in the soil that mimic the sound of falling rain. This tricks earthworms into thinking a flood is coming, driving them to the surface to avoid drowning. Once the worms pop up to \"safety,\" the seagull has a gourmet dinner served without ever having to dig.", + "scale": 65, + "inlineWidth": 604 + }, + { + "id": 1777782344239, + "type": "bullet", + "content": "They Track Your Taste Buds: Recent studies have shown that urban seagulls are more likely to steal food that they have specifically seen a human handling. If you’re eating a blue bag of chips, they are significantly more likely to target a blue bag than a green one. They don't just want food; they want your food.", + "scale": 65 + }, + { + "id": 1777782374505, + "type": "bullet", + "content": "Built-in Desalinization: Seagulls can drink salt water. They have specialized supraorbital glands located above their eyes that act like tiny desalination plants, filtering the salt out of their blood and \"sneezing\" it out through their nostrils.", + "scale": 65 + }, + { + "id": 1777782399555, + "type": "text", + "content": "Grudge Holders: They are highly intelligent and can recognize individual human faces. If you’ve shooed one away aggressively or, conversely, shared a fry, they are likely to remember you the next time you walk down the boardwalk.", + "scale": 80, + "inlineWidth": 653, + "inlineMinHeight": 98 + }, + { + "id": 1777782520904, + "type": "heading", + "content": "References", + "scale": 100 + }, + { + "id": 1777782461187, + "type": "text", + "content": "Gulliver, Finnegan. The Coastal Heist: Behavioral Patterns of Urban Laridae. Seaside University Press, 2024.\n\nLarson, Sandra. \"Vibrational Foraging: The Mechanics of the Seagull Rain Dance.\" Journal of Avian Intelligence, vol. 42, no. 3, 2025, pp. 112-128.\n", + "scale": 100, + "inlineWidth": 631, + "inlineMinHeight": 171 + } + ], + "settings": { + "fontStyle": "serif", + "fontApplyToText": false, + "fontApplyToTips": false, + "editorMode": "inline", + "showHtmlTools": false, + "previewMode": "paged", + "fileName": "assignment", + "hideTitle": false, + "hideDescription": false + } + }, + "marketplaceTemplate": { + "isTemplate": true, + "title": "Assignment", + "summary": "A simple essay builder assignment template with references." + } +} \ No newline at end of file diff --git a/templates/default/cake.cookie b/templates/default/cake.cookie new file mode 100644 index 0000000..07dcc50 --- /dev/null +++ b/templates/default/cake.cookie @@ -0,0 +1,174 @@ +{ + "app": "CookieCut", + "format": "cookie_document", + "version": 1, + "exportedAt": "2026-05-03T04:56:02.539Z", + "recipeData": { + "title": ":cake: Grandma & Co's Basic Cake Recipe", + "description": ":favorite: Hey grandbaby, it's your sweet old Grandma here, and I am going to teach you how to make my secret recipe for grandma's amazing cake.\n\nThis recipe yields a light, airy, sponge with a tender crumb-perfect for any occasion from birthday parties to tea parties.", + "items": [ + { + "id": 1777783253797, + "type": "heading", + "content": "Ingredients", + "scale": 100 + }, + { + "id": 1777783528645, + "type": "text", + "content": ":cookie: Dry Goods;", + "scale": 100 + }, + { + "id": 1777783489979, + "type": "bullet", + "content": ":check: 2 1/2 cups all-purpose flour", + "scale": 100 + }, + { + "id": 1777783500046, + "type": "bullet", + "content": ":check: 1 1/2 cups granulated sugar", + "scale": 100 + }, + { + "id": 1777783509630, + "type": "bullet", + "content": ":check: 1 tablespoon baking powder", + "scale": 100 + }, + { + "id": 1777783518522, + "type": "bullet", + "content": ":check: 1/2 teaspoon salt", + "scale": 100 + }, + { + "id": 1777783543512, + "type": "text", + "content": ":kitchen: Cold Goods; ", + "scale": 100 + }, + { + "id": 1777783551265, + "type": "bullet", + "content": ":coffee: 1 cup whole milk [y[**(at room temperature)**]]", + "scale": 100 + }, + { + "id": 1777783565110, + "type": "bullet", + "content": "1/2 cup unsalted butter [y[(**softened**)]]", + "scale": 100 + }, + { + "id": 1777783617195, + "type": "bullet", + "content": ":egg: 2 large eggs", + "scale": 100 + }, + { + "id": 1777783622708, + "type": "bullet", + "content": ":coffee: 2 teaspoons pure vanilla extract", + "scale": 100 + }, + { + "id": 1777783637708, + "type": "image", + "src": "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%20120%20120%22%3E%3Crect%20width%3D%22120%22%20height%3D%22120%22%20fill%3D%22%23fff7ed%22/%3E%3Cellipse%20cx%3D%2260%22%20cy%3D%2288%22%20rx%3D%2238%22%20ry%3D%2212%22%20fill%3D%22%23d1d5db%22/%3E%3Crect%20x%3D%2230%22%20y%3D%2250%22%20width%3D%2260%22%20height%3D%2230%22%20rx%3D%226%22%20fill%3D%22%23f59e0b%22/%3E%3Cpath%20d%3D%22M34%2055c6-10%2014-14%2026-14s20%204%2026%2014v8H34z%22%20fill%3D%22%23fef3c7%22/%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2265%22%20r%3D%223%22%20fill%3D%22%23ef4444%22/%3E%3Ccircle%20cx%3D%2260%22%20cy%3D%2261%22%20r%3D%223%22%20fill%3D%22%23ef4444%22/%3E%3Ccircle%20cx%3D%2272%22%20cy%3D%2266%22%20r%3D%223%22%20fill%3D%22%23ef4444%22/%3E%3Crect%20x%3D%2257%22%20y%3D%2230%22%20width%3D%226%22%20height%3D%2218%22%20fill%3D%22%232563eb%22/%3E%3Cpath%20d%3D%22M60%2024c4%206%204%2010%200%2014-4-4-4-8%200-14z%22%20fill%3D%22%23f97316%22/%3E%3C/svg%3E", + "alt": "cake illustration", + "size": "100", + "inlineWidth": 100, + "inlineImageFlow": "around" + }, + { + "id": 1777783665792, + "type": "heading", + "content": ":countertops: Preparation", + "scale": 100 + }, + { + "id": 1777783727198, + "type": "bubble", + "subtype": "note", + "content": ":microwave: Preheat your oven to 180°C. \nGrease and flour two 8-inch round cake pans to ensure a clean release.", + "scale": 100 + }, + { + "id": 1777783779375, + "type": "step", + "content": ":fastfood: In a large bowl, beat the softened butter and sugar together until the mixture is pale and fluffy **[y[(:timer:about 3-5 minutes)]]**.", + "scale": 100 + }, + { + "id": 1777783944243, + "type": "bubble", + "subtype": "tip", + "content": "Ensure eggs and milk are at room temperature before starting. This helps the batter emulsify properly, which results in a much smoother texture.", + "scale": 100 + }, + { + "id": 1777783821468, + "type": "step", + "content": "Add eggs one at a time, beating well after each addition. Stir in the vanilla extract.", + "scale": 100 + }, + { + "id": 1777784004520, + "type": "bubble", + "subtype": "warning", + "content": "Don't over-mix! Once you add the flour, mix only until combined. Overworking the gluten can make the cake too tough.", + "scale": 100 + }, + { + "id": 1777783843969, + "type": "step", + "content": ":map: Whisk the flour, baking powder, and salt together.\nGradually add the dry ingredients to the butter mixture, alternating with the milk. Start and end with the flour.", + "scale": 100 + }, + { + "id": 1777783884685, + "type": "step", + "content": ":grass: Divide the batter evenly between the pans. Baking for 30-35 minutes, or until a toothpick inserted comes out clean.", + "scale": 100 + }, + { + "id": 1777784051923, + "type": "bubble", + "subtype": "tip", + "content": ":eco: If your cake has a large dome use a serrated knife to gently shave off some of the top for a flat surface before frosting.", + "scale": 100 + }, + { + "id": 1777783909818, + "type": "step", + "content": ":thermostat: Allow the cake to cool in the pan for around *:timer: 10 minutes* before transferring them to a wire rack for complete cooling.", + "scale": 100 + }, + { + "id": 1777784083337, + "type": "bubble", + "subtype": "tip", + "content": ":science: Gently press the centre of the cake with your finger; if it springs back, it's ready to serve.", + "scale": 100 + } + ], + "settings": { + "fontStyle": "display", + "fontApplyToText": false, + "fontApplyToTips": false, + "editorMode": "classic", + "showHtmlTools": false, + "previewMode": "paged", + "fileName": "cake", + "hideTitle": false, + "hideDescription": false + } + }, + "marketplaceTemplate": { + "isTemplate": true, + "title": "Grandma & Co's Basic Cake Recipe", + "summary": "A simple cake recipe to show you how CookieCut can be used to make cooking instructions." + } +} \ No newline at end of file diff --git a/templates/default/emailsig.cookie b/templates/default/emailsig.cookie new file mode 100644 index 0000000..03e6ac2 --- /dev/null +++ b/templates/default/emailsig.cookie @@ -0,0 +1,80 @@ +{ + "app": "CookieCut", + "format": "cookie_document", + "version": 1, + "exportedAt": "2026-05-03T04:38:59.315Z", + "recipeData": { + "title": "", + "description": "", + "items": [ + { + "id": 1777782639804, + "type": "image", + "src": "https://raw.githubusercontent.com/SillyLittleTech/lander/refs/heads/main/assetlab/raster/text_alt.png", + "alt": "SLT Logo", + "size": 180, + "inlineWidth": 180, + "inlineImageFlow": "around", + "inlineMinHeight": 173 + }, + { + "id": 1777782764602, + "type": "heading", + "content": "**Capitulation Agreement**\n\n{16{SillyLittleTech}}\n{21{*May 3rd 2026*}}", + "scale": 160, + "inlineWidth": 351 + }, + { + "id": 1777782874051, + "type": "spacer", + "variant": "line", + "size": 0, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 48, + "inlineWidth": 670 + }, + { + "id": 1777782889951, + "type": "text", + "content": "Dear X,\n\nWhile we would love to meet and talk about your bountiful layers of cake, \nI am afraid we are going to have to decline, as we have a shareholder meeting at 12:00 PM PT that same say.\n\nWe would like to reach out and meet another time, send us your calendar and we will take a lookies. \n\n<3 XOXO \nThe totally real development team!\ntrustus@SillyLittleTech.org", + "scale": 100, + "inlineWidth": 673, + "inlineMinHeight": 341 + }, + { + "id": 1777783051999, + "type": "spacer", + "variant": "blank", + "size": 60, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 60, + "inlineWidth": 650 + }, + { + "id": 1777783010715, + "type": "text", + "content": "Disclaimer, this is not a real email, nor does it match our internal design docs for such emails. It is simply a mockup example used to show what could be done with this tool.", + "scale": 60, + "inlineWidth": 642 + } + ], + "settings": { + "fontStyle": "sans", + "fontApplyToText": false, + "fontApplyToTips": false, + "editorMode": "inline", + "showHtmlTools": false, + "previewMode": "paged", + "fileName": "emailsig", + "hideTitle": true, + "hideDescription": true + } + }, + "marketplaceTemplate": { + "isTemplate": true, + "title": "Email with Signature", + "summary": "A company email with a signature and some template details." + } +} \ No newline at end of file diff --git a/templates/default/safeplan.cookie b/templates/default/safeplan.cookie new file mode 100644 index 0000000..99f97a3 --- /dev/null +++ b/templates/default/safeplan.cookie @@ -0,0 +1,226 @@ +{ + "app": "CookieCut", + "format": "cookie_document", + "version": 1, + "exportedAt": "2026-05-03T04:19:16.359Z", + "recipeData": { + "title": "Safety Plan", + "description": "", + "items": [ + { + "id": 1777778371532, + "type": "spacer", + "variant": "container", + "size": 255, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 255, + "inlineWidth": 303 + }, + { + "id": 1777778393597, + "type": "text", + "content": "Warning Signs:", + "scale": 100, + "parentId": 1777778371532 + }, + { + "id": 1777778454650, + "type": "step", + "content": "     ", + "scale": 100, + "parentId": 1777778371532 + }, + { + "id": 1777778516820, + "type": "step", + "content": "   ", + "scale": 100, + "parentId": 1777778371532 + }, + { + "id": 1777778525974, + "type": "step", + "content": "  ", + "scale": 100, + "parentId": 1777778371532 + }, + { + "id": 1777778536234, + "type": "spacer", + "variant": "container", + "size": 318, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 318, + "inlineWidth": 313 + }, + { + "id": 1777778581032, + "type": "text", + "content": "What I can:", + "scale": 100, + "parentId": 1777778536234 + }, + { + "id": 1777778604582, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777778536234 + }, + { + "id": 1777778615082, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777778536234 + }, + { + "id": 1777778622834, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777778536234 + }, + { + "id": 1777781015054, + "type": "text", + "content": "**Emergency Checklist**", + "scale": 90, + "parentId": 1777778644967, + "inlineWidth": 539, + "inlineMinHeight": 48 + }, + { + "id": 1777781044986, + "type": "bullet", + "content": "Copies of important documents. Like  Passport/Driver's license", + "scale": 95, + "parentId": 1777778644967, + "inlineWidth": 545, + "inlineMinHeight": 48 + }, + { + "id": 1777781070154, + "type": "bullet", + "content": "Cash, Debit, and Credit cards or things like prescription drugs", + "scale": 90, + "parentId": 1777778644967, + "inlineWidth": 558 + }, + { + "id": 1777781101902, + "type": "bullet", + "content": "Phone and Charger and any keys to properties you need", + "scale": 80, + "parentId": 1777778644967, + "inlineWidth": 556 + }, + { + "id": 1777781341784, + "type": "text", + "content": "People I can call for help:", + "scale": 100, + "parentId": 1777781333734 + }, + { + "id": 1777781359917, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777781333734 + }, + { + "id": 1777781367033, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777781333734 + }, + { + "id": 1777781373383, + "type": "step", + "content": " ", + "scale": 100, + "parentId": 1777781333734 + }, + { + "id": 1777781395016, + "type": "text", + "content": "Identify your boundaries:", + "scale": 130, + "parentId": 1777781390366 + }, + { + "id": 1777781410649, + "type": "text", + "content": "To calm the situation down, I will give them what they want until:", + "scale": 80, + "parentId": 1777781390366 + }, + { + "id": 1777781441520, + "type": "text", + "content": "I know I am unsafe when:", + "scale": 95, + "parentId": 1777781390366, + "inlineWidth": 308, + "inlineMinHeight": 48 + }, + { + "id": 1777781451899, + "type": "text", + "content": "To protect myself and my mental health I will:", + "scale": 75, + "parentId": 1777781390366 + }, + { + "id": 1777781333734, + "type": "spacer", + "variant": "container", + "size": 262, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 262, + "inlineWidth": 633 + }, + { + "id": 1777781390366, + "type": "spacer", + "variant": "container", + "size": 48, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 48, + "inlineWidth": 630 + }, + { + "id": 1777778644967, + "type": "spacer", + "variant": "container", + "size": 48, + "containerLayout": "flow", + "containerColumns": 2, + "inlineMinHeight": 48, + "inlineWidth": 632 + } + ], + "settings": { + "fontStyle": "sans", + "fontApplyToText": false, + "fontApplyToTips": false, + "editorMode": "inline", + "showHtmlTools": false, + "previewMode": "paged", + "fileName": "safeplan", + "hideTitle": false, + "hideDescription": true + } + }, + "marketplaceTemplate": { + "isTemplate": true, + "title": "Safety Plan", + "summary": "A quick 2 page safety plan that covers essentials like triggers, what makes us feel safe, emergency contacts. And a second page with boundaries and important things to keep with you." + } +} \ No newline at end of file