From f298c343339a06c1c710463248140513b89270a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?batuhan=20i=C3=A7=C3=B6z?= Date: Sun, 8 Nov 2026 23:28:38 +0000 Subject: [PATCH] Remove the recipe gallery and its publishing Recipes now live and publish from their own repository. This repository keeps only framework and migration examples. Deleted the remaining recipes/ folders, the manifest builder, the catalog test, the publish-directory validator, and the manifest, deploy-manifest, discover-recipes, and publish-recipes workflow jobs together with the Pages and id-token permissions they needed. The workflow is now "Validate examples" and only runs the examples build matrix. --- .github/workflows/publish.yml | 176 +-- README.md | 24 +- recipes/2048/README.md | 24 - recipes/2048/meta.json | 26 - recipes/2048/prompt.md | 59 - recipes/2048/site/game.js | 446 ------ recipes/2048/site/index.html | 84 -- recipes/2048/site/style.css | 366 ----- recipes/README.md | 26 - recipes/TEMPLATE.md | 74 - recipes/app/README.md | 33 - recipes/app/meta.json | 32 - recipes/app/prompt.md | 91 -- recipes/app/site/.gitignore | 3 - recipes/app/site/bun.lock | 248 ---- recipes/app/site/index.html | 25 - recipes/app/site/package.json | 19 - recipes/app/site/public/_redirects | 1 - recipes/app/site/public/favicon.svg | 6 - recipes/app/site/src/App.jsx | 224 ---- recipes/app/site/src/components/Avatar.jsx | 24 - recipes/app/site/src/components/Badges.jsx | 21 - recipes/app/site/src/components/Board.jsx | 89 -- recipes/app/site/src/components/Card.jsx | 81 -- recipes/app/site/src/components/ListView.jsx | 131 -- .../app/site/src/components/TaskDrawer.jsx | 330 ----- recipes/app/site/src/components/Toolbar.jsx | 154 --- recipes/app/site/src/components/icons.jsx | 98 -- recipes/app/site/src/lib/constants.js | 37 - recipes/app/site/src/lib/seed.js | 245 ---- recipes/app/site/src/lib/storage.js | 32 - recipes/app/site/src/lib/util.js | 61 - recipes/app/site/src/main.jsx | 10 - recipes/app/site/src/styles.css | 623 --------- recipes/app/site/vite.config.js | 9 - recipes/arcade/README.md | 11 - recipes/arcade/meta.json | 31 - recipes/arcade/prompt.md | 59 - recipes/arcade/site/game.js | 382 ------ recipes/arcade/site/index.html | 475 ------- recipes/arcade/site/styles.css | 724 ---------- recipes/arcade/site/ui.js | 42 - recipes/band/README.md | 3 - recipes/band/meta.json | 33 - recipes/band/prompt.md | 62 - recipes/band/site/assets/app.js | 260 ---- recipes/band/site/assets/styles.css | 462 ------- recipes/band/site/index.html | 263 ---- recipes/barber/README.md | 9 - recipes/barber/meta.json | 31 - recipes/barber/prompt.md | 73 - recipes/barber/site/index.html | 407 ------ recipes/barber/site/script.js | 147 -- recipes/barber/site/styles.css | 627 --------- recipes/blog/README.md | 26 - recipes/blog/meta.json | 30 - recipes/blog/prompt.md | 82 -- recipes/blog/site/.gitignore | 3 - recipes/blog/site/astro.config.mjs | 15 - recipes/blog/site/package.json | 14 - recipes/blog/site/public/favicon.svg | 8 - .../blog/site/src/components/PostList.astro | 33 - recipes/blog/site/src/content.config.ts | 18 - .../src/content/posts/a-tiny-state-machine.md | 112 -- .../posts/css-that-outlives-frameworks.md | 131 -- .../deploying-static-without-the-yak-shave.md | 103 -- .../posts/the-underrated-details-element.md | 115 -- .../posts/the-web-used-to-be-lighter.md | 100 -- .../content/posts/why-i-write-in-markdown.md | 86 -- recipes/blog/site/src/layouts/Layout.astro | 142 -- recipes/blog/site/src/lib/site.ts | 45 - recipes/blog/site/src/pages/about.astro | 78 -- recipes/blog/site/src/pages/index.astro | 44 - .../blog/site/src/pages/posts/[...slug].astro | 78 -- recipes/blog/site/src/pages/rss.xml.js | 52 - recipes/blog/site/src/pages/tags/[tag].astro | 37 - recipes/blog/site/src/pages/tags/index.astro | 34 - recipes/blog/site/src/styles/global.css | 661 --------- recipes/blog/site/tsconfig.json | 5 - recipes/calories/README.md | 13 - recipes/calories/meta.json | 32 - recipes/calories/prompt.md | 83 -- recipes/calories/site/app.js | 509 ------- recipes/calories/site/index.html | 206 --- recipes/calories/site/styles.css | 309 ----- recipes/changelog/README.md | 3 - recipes/changelog/meta.json | 30 - recipes/changelog/prompt.md | 84 -- recipes/changelog/site/app.js | 173 --- recipes/changelog/site/feed.xml | 77 -- recipes/changelog/site/index.html | 503 ------- recipes/changelog/site/styles.css | 374 ------ recipes/cli/README.md | 16 - recipes/cli/meta.json | 33 - recipes/cli/prompt.md | 86 -- recipes/cli/site/.gitignore | 3 - recipes/cli/site/astro.config.mjs | 9 - recipes/cli/site/package.json | 14 - recipes/cli/site/public/favicon.svg | 5 - .../cli/site/src/components/Benchmark.astro | 184 --- .../cli/site/src/components/CodeExample.astro | 107 -- .../cli/site/src/components/Community.astro | 173 --- .../cli/site/src/components/Features.astro | 109 -- recipes/cli/site/src/components/Footer.astro | 132 -- recipes/cli/site/src/components/Hero.astro | 274 ---- .../cli/site/src/components/HowItWorks.astro | 92 -- recipes/cli/site/src/components/Nav.astro | 135 -- recipes/cli/site/src/components/UsedBy.astro | 102 -- recipes/cli/site/src/data/site.ts | 186 --- recipes/cli/site/src/layouts/Layout.astro | 44 - recipes/cli/site/src/pages/index.astro | 30 - recipes/cli/site/src/styles/global.css | 241 ---- recipes/cli/site/tsconfig.json | 5 - recipes/conference/README.md | 13 - recipes/conference/meta.json | 34 - recipes/conference/prompt.md | 79 -- recipes/conference/site/.gitignore | 3 - recipes/conference/site/astro.config.mjs | 8 - recipes/conference/site/package.json | 14 - recipes/conference/site/public/favicon.svg | 12 - .../site/src/components/About.astro | 129 -- .../conference/site/src/components/Faq.astro | 149 --- .../site/src/components/Footer.astro | 267 ---- .../conference/site/src/components/Hero.astro | 283 ---- .../conference/site/src/components/Nav.astro | 226 ---- .../site/src/components/Schedule.astro | 160 --- .../site/src/components/Speakers.astro | 199 --- .../site/src/components/Sponsors.astro | 136 -- .../site/src/components/Tickets.astro | 178 --- .../site/src/components/Venue.astro | 171 --- recipes/conference/site/src/data/conf.ts | 355 ----- .../conference/site/src/layouts/Layout.astro | 44 - recipes/conference/site/src/pages/index.astro | 32 - recipes/conference/site/src/styles/global.css | 259 ---- recipes/conference/site/tsconfig.json | 5 - recipes/dashboard/README.md | 15 - recipes/dashboard/meta.json | 33 - recipes/dashboard/prompt.md | 77 -- recipes/dashboard/site/app.js | 482 ------- recipes/dashboard/site/index.html | 189 --- recipes/dashboard/site/styles.css | 444 ------ recipes/deck/README.md | 12 - recipes/deck/meta.json | 33 - recipes/deck/prompt.md | 63 - recipes/deck/site/assets/deck.js | 66 - recipes/deck/site/assets/style.css | 261 ---- recipes/deck/site/index.html | 315 ----- recipes/docs/README.md | 50 - recipes/docs/meta.json | 34 - recipes/docs/prompt.md | 87 -- recipes/docs/site/.gitignore | 3 - recipes/docs/site/astro.config.mjs | 12 - recipes/docs/site/package.json | 14 - recipes/docs/site/public/favicon.svg | 11 - .../docs/site/src/components/CodeBlock.astro | 21 - recipes/docs/site/src/components/Footer.astro | 26 - recipes/docs/site/src/components/Search.astro | 142 -- .../docs/site/src/components/Sidebar.astro | 40 - recipes/docs/site/src/components/TopNav.astro | 153 --- recipes/docs/site/src/data/site.ts | 118 -- recipes/docs/site/src/layouts/Base.astro | 52 - recipes/docs/site/src/layouts/Docs.astro | 126 -- recipes/docs/site/src/pages/api.astro | 122 -- recipes/docs/site/src/pages/changelog.astro | 60 - .../docs/site/src/pages/getting-started.astro | 146 -- .../site/src/pages/guides/migrations.astro | 106 -- .../site/src/pages/guides/persistence.astro | 108 -- .../src/pages/guides/reactive-queries.astro | 122 -- recipes/docs/site/src/pages/index.astro | 184 --- recipes/docs/site/src/styles/global.css | 1190 ----------------- recipes/docs/site/tsconfig.json | 5 - recipes/foodtruck/README.md | 10 - recipes/foodtruck/meta.json | 31 - recipes/foodtruck/prompt.md | 57 - recipes/foodtruck/site/app.js | 220 --- recipes/foodtruck/site/index.html | 247 ---- recipes/foodtruck/site/styles.css | 269 ---- recipes/fundraiser/README.md | 3 - recipes/fundraiser/meta.json | 33 - recipes/fundraiser/prompt.md | 62 - recipes/fundraiser/site/assets/app.js | 324 ----- recipes/fundraiser/site/assets/styles.css | 343 ----- recipes/fundraiser/site/index.html | 287 ---- recipes/game/README.md | 3 - recipes/game/meta.json | 31 - recipes/game/prompt.md | 57 - recipes/game/site/audio.js | 72 - recipes/game/site/game.js | 954 ------------- recipes/game/site/index.html | 145 -- recipes/game/site/styles.css | 464 ------- recipes/garden/README.md | 15 - recipes/garden/meta.json | 34 - recipes/garden/prompt.md | 95 -- recipes/garden/site/app.js | 235 ---- recipes/garden/site/index.html | 575 -------- recipes/garden/site/styles.css | 940 ------------- recipes/generative-art/README.md | 15 - recipes/generative-art/meta.json | 31 - recipes/generative-art/prompt.md | 64 - recipes/generative-art/site/app.js | 446 ------ recipes/generative-art/site/field.js | 94 -- recipes/generative-art/site/index.html | 151 --- recipes/generative-art/site/styles.css | 467 ------- recipes/habits/README.md | 13 - recipes/habits/meta.json | 31 - recipes/habits/prompt.md | 58 - recipes/habits/site/.gitignore | 3 - recipes/habits/site/bun.lock | 248 ---- recipes/habits/site/index.html | 25 - recipes/habits/site/package.json | 19 - recipes/habits/site/public/favicon.svg | 14 - recipes/habits/site/src/App.jsx | 171 --- .../habits/site/src/components/HabitCard.jsx | 100 -- .../site/src/components/HabitDetail.jsx | 117 -- .../habits/site/src/components/HabitModal.jsx | 148 -- recipes/habits/site/src/components/Header.jsx | 35 - .../habits/site/src/components/Heatmap.jsx | 143 -- recipes/habits/site/src/components/Modal.jsx | 40 - .../site/src/components/MonthCalendar.jsx | 95 -- .../site/src/components/SettingsModal.jsx | 110 -- .../habits/site/src/components/TodayPanel.jsx | 144 -- recipes/habits/site/src/lib/color.js | 32 - recipes/habits/site/src/lib/date.js | 76 -- recipes/habits/site/src/lib/seed.js | 68 - recipes/habits/site/src/lib/stats.js | 84 -- recipes/habits/site/src/lib/storage.js | 36 - recipes/habits/site/src/main.jsx | 10 - recipes/habits/site/src/styles.css | 890 ------------ recipes/habits/site/vite.config.js | 9 - recipes/hackathon/README.md | 13 - recipes/hackathon/meta.json | 33 - recipes/hackathon/prompt.md | 74 - recipes/hackathon/site/app.js | 225 ---- recipes/hackathon/site/index.html | 504 ------- recipes/hackathon/site/styles.css | 419 ------ recipes/invitation/README.md | 13 - recipes/invitation/meta.json | 33 - recipes/invitation/prompt.md | 87 -- recipes/invitation/site/.gitignore | 3 - recipes/invitation/site/astro.config.mjs | 7 - recipes/invitation/site/package.json | 14 - recipes/invitation/site/public/favicon.svg | 5 - .../site/src/components/Details.astro | 136 -- .../invitation/site/src/components/Faq.astro | 86 -- .../site/src/components/Footer.astro | 52 - .../site/src/components/Gallery.astro | 75 -- .../invitation/site/src/components/Hero.astro | 214 --- .../invitation/site/src/components/Nav.astro | 191 --- .../site/src/components/Registry.astro | 75 -- .../invitation/site/src/components/Rsvp.astro | 304 ----- .../site/src/components/Story.astro | 118 -- .../site/src/components/Travel.astro | 64 - recipes/invitation/site/src/data/wedding.ts | 139 -- .../invitation/site/src/layouts/Layout.astro | 37 - recipes/invitation/site/src/pages/index.astro | 32 - recipes/invitation/site/src/styles/global.css | 188 --- recipes/invitation/site/tsconfig.json | 5 - recipes/links/README.md | 3 - recipes/links/meta.json | 31 - recipes/links/prompt.md | 59 - recipes/links/site/index.html | 563 -------- recipes/listing/README.md | 12 - recipes/listing/meta.json | 32 - recipes/listing/prompt.md | 80 -- recipes/listing/site/bun.lock | 248 ---- recipes/listing/site/index.html | 30 - recipes/listing/site/package.json | 19 - recipes/listing/site/src/App.jsx | 186 --- .../listing/site/src/components/FloorPlan.jsx | 138 -- .../listing/site/src/components/Gallery.jsx | 116 -- .../src/components/MortgageCalculator.jsx | 123 -- .../listing/site/src/components/TourForm.jsx | 183 --- recipes/listing/site/src/data.js | 155 --- recipes/listing/site/src/main.jsx | 10 - recipes/listing/site/src/styles.css | 1048 --------------- recipes/listing/site/vite.config.mjs | 8 - recipes/memorial/README.md | 13 - recipes/memorial/meta.json | 30 - recipes/memorial/prompt.md | 60 - recipes/memorial/site/index.html | 765 ----------- recipes/menu/README.md | 11 - recipes/menu/meta.json | 31 - recipes/menu/prompt.md | 80 -- recipes/menu/site/app.js | 169 --- recipes/menu/site/index.html | 461 ------- recipes/menu/site/styles.css | 606 --------- recipes/meta.example.json | 16 - recipes/newsletter/README.md | 13 - recipes/newsletter/meta.json | 30 - recipes/newsletter/prompt.md | 78 -- recipes/newsletter/site/about.html | 139 -- recipes/newsletter/site/archive.html | 238 ---- recipes/newsletter/site/index.html | 187 --- recipes/newsletter/site/issues/issue-85.html | 194 --- recipes/newsletter/site/issues/issue-86.html | 191 --- recipes/newsletter/site/issues/issue-87.html | 194 --- recipes/newsletter/site/script.js | 72 - recipes/newsletter/site/styles.css | 390 ------ recipes/newsletter/site/subscribe.html | 141 -- recipes/notes/README.md | 36 - recipes/notes/meta.json | 30 - recipes/notes/prompt.md | 84 -- recipes/notes/site/.gitignore | 8 - recipes/notes/site/astro.config.mjs | 17 - recipes/notes/site/bun.lock | 727 ---------- recipes/notes/site/package.json | 18 - recipes/notes/site/public/favicon.svg | 7 - recipes/notes/site/src/components/Graph.astro | 88 -- .../site/src/components/StatusBadge.astro | 13 - recipes/notes/site/src/content.config.ts | 24 - .../site/src/content/notes/atomic-notes.md | 30 - .../notes/site/src/content/notes/backlinks.md | 30 - .../site/src/content/notes/digital-gardens.md | 33 - .../site/src/content/notes/evergreen-notes.md | 33 - .../src/content/notes/learning-in-public.md | 28 - .../src/content/notes/spaced-repetition.md | 29 - .../site/src/content/notes/tending-notes.md | 30 - .../src/content/notes/why-this-glasshouse.md | 28 - .../notes/working-with-the-garage-door-up.md | 25 - .../site/src/content/notes/zettelkasten.md | 29 - recipes/notes/site/src/layouts/Layout.astro | 79 -- recipes/notes/site/src/lib/garden.ts | 256 ---- .../notes/site/src/lib/remark-wikilinks.mjs | 48 - recipes/notes/site/src/lib/site.ts | 13 - recipes/notes/site/src/lib/slugify.mjs | 12 - recipes/notes/site/src/pages/index.astro | 205 --- recipes/notes/site/src/pages/notes/[id].astro | 118 -- recipes/notes/site/src/styles/global.css | 900 ------------- recipes/notes/site/tsconfig.json | 5 - recipes/party/README.md | 17 - recipes/party/meta.json | 29 - recipes/party/prompt.md | 60 - recipes/party/site/.gitignore | 13 - recipes/party/site/astro.config.mjs | 7 - recipes/party/site/package.json | 14 - recipes/party/site/public/favicon.svg | 14 - .../party/site/src/components/Details.astro | 138 -- .../party/site/src/components/Footer.astro | 50 - recipes/party/site/src/components/Hero.astro | 297 ---- .../party/site/src/components/Location.astro | 100 -- recipes/party/site/src/components/Nav.astro | 95 -- recipes/party/site/src/components/Rsvp.astro | 389 ------ .../party/site/src/components/Schedule.astro | 85 -- recipes/party/site/src/data/party.ts | 106 -- recipes/party/site/src/layouts/Layout.astro | 41 - recipes/party/site/src/pages/index.astro | 26 - recipes/party/site/src/styles/global.css | 195 --- recipes/party/site/tsconfig.json | 5 - recipes/photos/README.md | 14 - recipes/photos/meta.json | 31 - recipes/photos/prompt.md | 74 - recipes/photos/site/.gitignore | 3 - recipes/photos/site/astro.config.mjs | 8 - recipes/photos/site/bun.lock | 603 --------- recipes/photos/site/package.json | 14 - recipes/photos/site/public/favicon.svg | 5 - .../site/src/components/AboutTeaser.astro | 34 - .../photos/site/src/components/Contact.astro | 111 -- .../photos/site/src/components/Footer.astro | 26 - recipes/photos/site/src/components/Hero.astro | 23 - recipes/photos/site/src/components/Nav.astro | 26 - .../photos/site/src/components/Prints.astro | 30 - .../site/src/components/WorkIndex.astro | 42 - recipes/photos/site/src/data/portfolio.ts | 296 ---- recipes/photos/site/src/layouts/Layout.astro | 78 -- recipes/photos/site/src/pages/about.astro | 62 - recipes/photos/site/src/pages/index.astro | 22 - .../photos/site/src/pages/series/[slug].astro | 157 --- recipes/photos/site/src/styles/global.css | 823 ------------ recipes/photos/site/tsconfig.json | 5 - recipes/portfolio/README.md | 3 - recipes/portfolio/meta.json | 32 - recipes/portfolio/prompt.md | 76 -- recipes/portfolio/site/index.html | 878 ------------ recipes/proposal/README.md | 37 - recipes/proposal/meta.json | 30 - recipes/proposal/prompt.md | 87 -- recipes/proposal/site/.gitignore | 3 - recipes/proposal/site/astro.config.mjs | 10 - recipes/proposal/site/bun.lock | 603 --------- recipes/proposal/site/package.json | 14 - recipes/proposal/site/public/favicon.svg | 4 - recipes/proposal/site/src/data/proposal.ts | 225 ---- .../proposal/site/src/layouts/Layout.astro | 44 - recipes/proposal/site/src/pages/index.astro | 650 --------- recipes/proposal/site/src/styles/global.css | 1163 ---------------- recipes/qr/README.md | 13 - recipes/qr/meta.json | 32 - recipes/qr/prompt.md | 91 -- recipes/qr/site/assets/app.js | 149 --- recipes/qr/site/assets/styles.css | 538 -------- recipes/qr/site/index.html | 210 --- recipes/recipes/README.md | 3 - recipes/recipes/meta.json | 30 - recipes/recipes/prompt.md | 58 - recipes/recipes/site/bun.lock | 248 ---- recipes/recipes/site/index.html | 30 - recipes/recipes/site/package.json | 19 - recipes/recipes/site/src/App.jsx | 698 ---------- recipes/recipes/site/src/data.js | 305 ----- recipes/recipes/site/src/format.js | 60 - recipes/recipes/site/src/main.jsx | 10 - recipes/recipes/site/src/styles.css | 1098 --------------- recipes/recipes/site/vite.config.mjs | 8 - recipes/report/README.md | 17 - recipes/report/meta.json | 33 - recipes/report/prompt.md | 81 -- recipes/report/site/.gitignore | 3 - recipes/report/site/astro.config.mjs | 9 - recipes/report/site/package.json | 14 - recipes/report/site/public/favicon.svg | 12 - .../report/site/src/components/BarChart.astro | 122 -- .../site/src/components/DivergingBars.astro | 162 --- .../site/src/components/DonutChart.astro | 157 --- .../site/src/components/HBarChart.astro | 108 -- .../site/src/components/HostingBars.astro | 144 -- .../site/src/components/PainPoints.astro | 114 -- .../report/site/src/components/ToolBars.astro | 139 -- recipes/report/site/src/data/survey.ts | 216 --- recipes/report/site/src/layouts/Layout.astro | 99 -- recipes/report/site/src/pages/index.astro | 696 ---------- recipes/report/site/src/styles/global.css | 297 ---- recipes/resume/README.md | 15 - recipes/resume/meta.json | 30 - recipes/resume/prompt.md | 76 -- recipes/resume/site/assets/script.js | 31 - recipes/resume/site/assets/styles.css | 420 ------ recipes/resume/site/index.html | 272 ---- recipes/revenue-review/README.md | 11 - recipes/revenue-review/meta.json | 32 - recipes/revenue-review/prompt.md | 80 -- recipes/revenue-review/site/index.html | 31 - recipes/revenue-review/site/package.json | 19 - recipes/revenue-review/site/src/App.jsx | 365 ----- recipes/revenue-review/site/src/charts.jsx | 395 ------ recipes/revenue-review/site/src/data.js | 219 --- recipes/revenue-review/site/src/format.js | 54 - recipes/revenue-review/site/src/main.jsx | 10 - recipes/revenue-review/site/src/styles.css | 766 ----------- recipes/revenue-review/site/vite.config.mjs | 8 - recipes/roi/README.md | 28 - recipes/roi/meta.json | 33 - recipes/roi/prompt.md | 76 -- recipes/roi/site/assets/app.js | 309 ----- recipes/roi/site/assets/styles.css | 236 ---- recipes/roi/site/index.html | 287 ---- recipes/saas/README.md | 3 - recipes/saas/meta.json | 34 - recipes/saas/prompt.md | 63 - recipes/saas/site/.gitignore | 3 - recipes/saas/site/astro.config.mjs | 8 - recipes/saas/site/package.json | 14 - recipes/saas/site/public/favicon.svg | 15 - .../site/src/components/ProductMock.astro | 399 ------ recipes/saas/site/src/data/site.ts | 211 --- recipes/saas/site/src/layouts/Base.astro | 39 - recipes/saas/site/src/pages/index.astro | 812 ----------- recipes/saas/site/src/styles/global.css | 245 ---- recipes/saas/site/tsconfig.json | 5 - recipes/savings/README.md | 3 - recipes/savings/meta.json | 31 - recipes/savings/prompt.md | 82 -- recipes/savings/site/app.js | 696 ---------- recipes/savings/site/index.html | 305 ----- recipes/savings/site/styles.css | 354 ----- recipes/soon/README.md | 12 - recipes/soon/meta.json | 28 - recipes/soon/prompt.md | 77 -- recipes/soon/site/assets/app.js | 143 -- recipes/soon/site/assets/styles.css | 398 ------ recipes/soon/site/index.html | 137 -- recipes/status/README.md | 3 - recipes/status/meta.json | 31 - recipes/status/prompt.md | 58 - recipes/status/site/app.js | 264 ---- recipes/status/site/index.html | 225 ---- recipes/status/site/styles.css | 541 -------- recipes/studio/README.md | 13 - recipes/studio/meta.json | 33 - recipes/studio/prompt.md | 77 -- recipes/studio/site/app.js | 156 --- recipes/studio/site/index.html | 440 ------ recipes/studio/site/styles.css | 279 ---- recipes/technical-plan/README.md | 15 - recipes/technical-plan/meta.json | 27 - recipes/technical-plan/prompt.md | 103 -- recipes/technical-plan/site/index.html | 499 ------- recipes/technical-plan/site/styles.css | 893 ------------- recipes/tip/README.md | 3 - recipes/tip/meta.json | 32 - recipes/tip/prompt.md | 69 - recipes/tip/site/app.js | 251 ---- recipes/tip/site/index.html | 211 --- recipes/tip/site/styles.css | 324 ----- recipes/word-game/README.md | 11 - recipes/word-game/meta.json | 30 - recipes/word-game/prompt.md | 78 -- recipes/word-game/site/app.js | 478 ------- recipes/word-game/site/index.html | 101 -- recipes/word-game/site/style.css | 448 ------- recipes/word-game/site/words.js | 570 -------- recipes/zine/README.md | 16 - recipes/zine/meta.json | 32 - recipes/zine/prompt.md | 70 - recipes/zine/site/index.html | 508 ------- scripts/build-manifest.mjs | 93 -- scripts/catalog.test.mjs | 160 --- scripts/validate-publish-dir.mjs | 40 - 508 files changed, 7 insertions(+), 74059 deletions(-) delete mode 100644 recipes/2048/README.md delete mode 100644 recipes/2048/meta.json delete mode 100644 recipes/2048/prompt.md delete mode 100644 recipes/2048/site/game.js delete mode 100644 recipes/2048/site/index.html delete mode 100644 recipes/2048/site/style.css delete mode 100644 recipes/README.md delete mode 100644 recipes/TEMPLATE.md delete mode 100644 recipes/app/README.md delete mode 100644 recipes/app/meta.json delete mode 100644 recipes/app/prompt.md delete mode 100644 recipes/app/site/.gitignore delete mode 100644 recipes/app/site/bun.lock delete mode 100644 recipes/app/site/index.html delete mode 100644 recipes/app/site/package.json delete mode 100644 recipes/app/site/public/_redirects delete mode 100644 recipes/app/site/public/favicon.svg delete mode 100644 recipes/app/site/src/App.jsx delete mode 100644 recipes/app/site/src/components/Avatar.jsx delete mode 100644 recipes/app/site/src/components/Badges.jsx delete mode 100644 recipes/app/site/src/components/Board.jsx delete mode 100644 recipes/app/site/src/components/Card.jsx delete mode 100644 recipes/app/site/src/components/ListView.jsx delete mode 100644 recipes/app/site/src/components/TaskDrawer.jsx delete mode 100644 recipes/app/site/src/components/Toolbar.jsx delete mode 100644 recipes/app/site/src/components/icons.jsx delete mode 100644 recipes/app/site/src/lib/constants.js delete mode 100644 recipes/app/site/src/lib/seed.js delete mode 100644 recipes/app/site/src/lib/storage.js delete mode 100644 recipes/app/site/src/lib/util.js delete mode 100644 recipes/app/site/src/main.jsx delete mode 100644 recipes/app/site/src/styles.css delete mode 100644 recipes/app/site/vite.config.js delete mode 100644 recipes/arcade/README.md delete mode 100644 recipes/arcade/meta.json delete mode 100644 recipes/arcade/prompt.md delete mode 100644 recipes/arcade/site/game.js delete mode 100644 recipes/arcade/site/index.html delete mode 100644 recipes/arcade/site/styles.css delete mode 100644 recipes/arcade/site/ui.js delete mode 100644 recipes/band/README.md delete mode 100644 recipes/band/meta.json delete mode 100644 recipes/band/prompt.md delete mode 100644 recipes/band/site/assets/app.js delete mode 100644 recipes/band/site/assets/styles.css delete mode 100644 recipes/band/site/index.html delete mode 100644 recipes/barber/README.md delete mode 100644 recipes/barber/meta.json delete mode 100644 recipes/barber/prompt.md delete mode 100644 recipes/barber/site/index.html delete mode 100644 recipes/barber/site/script.js delete mode 100644 recipes/barber/site/styles.css delete mode 100644 recipes/blog/README.md delete mode 100644 recipes/blog/meta.json delete mode 100644 recipes/blog/prompt.md delete mode 100644 recipes/blog/site/.gitignore delete mode 100644 recipes/blog/site/astro.config.mjs delete mode 100644 recipes/blog/site/package.json delete mode 100644 recipes/blog/site/public/favicon.svg delete mode 100644 recipes/blog/site/src/components/PostList.astro delete mode 100644 recipes/blog/site/src/content.config.ts delete mode 100644 recipes/blog/site/src/content/posts/a-tiny-state-machine.md delete mode 100644 recipes/blog/site/src/content/posts/css-that-outlives-frameworks.md delete mode 100644 recipes/blog/site/src/content/posts/deploying-static-without-the-yak-shave.md delete mode 100644 recipes/blog/site/src/content/posts/the-underrated-details-element.md delete mode 100644 recipes/blog/site/src/content/posts/the-web-used-to-be-lighter.md delete mode 100644 recipes/blog/site/src/content/posts/why-i-write-in-markdown.md delete mode 100644 recipes/blog/site/src/layouts/Layout.astro delete mode 100644 recipes/blog/site/src/lib/site.ts delete mode 100644 recipes/blog/site/src/pages/about.astro delete mode 100644 recipes/blog/site/src/pages/index.astro delete mode 100644 recipes/blog/site/src/pages/posts/[...slug].astro delete mode 100644 recipes/blog/site/src/pages/rss.xml.js delete mode 100644 recipes/blog/site/src/pages/tags/[tag].astro delete mode 100644 recipes/blog/site/src/pages/tags/index.astro delete mode 100644 recipes/blog/site/src/styles/global.css delete mode 100644 recipes/blog/site/tsconfig.json delete mode 100644 recipes/calories/README.md delete mode 100644 recipes/calories/meta.json delete mode 100644 recipes/calories/prompt.md delete mode 100644 recipes/calories/site/app.js delete mode 100644 recipes/calories/site/index.html delete mode 100644 recipes/calories/site/styles.css delete mode 100644 recipes/changelog/README.md delete mode 100644 recipes/changelog/meta.json delete mode 100644 recipes/changelog/prompt.md delete mode 100644 recipes/changelog/site/app.js delete mode 100644 recipes/changelog/site/feed.xml delete mode 100644 recipes/changelog/site/index.html delete mode 100644 recipes/changelog/site/styles.css delete mode 100644 recipes/cli/README.md delete mode 100644 recipes/cli/meta.json delete mode 100644 recipes/cli/prompt.md delete mode 100644 recipes/cli/site/.gitignore delete mode 100644 recipes/cli/site/astro.config.mjs delete mode 100644 recipes/cli/site/package.json delete mode 100644 recipes/cli/site/public/favicon.svg delete mode 100644 recipes/cli/site/src/components/Benchmark.astro delete mode 100644 recipes/cli/site/src/components/CodeExample.astro delete mode 100644 recipes/cli/site/src/components/Community.astro delete mode 100644 recipes/cli/site/src/components/Features.astro delete mode 100644 recipes/cli/site/src/components/Footer.astro delete mode 100644 recipes/cli/site/src/components/Hero.astro delete mode 100644 recipes/cli/site/src/components/HowItWorks.astro delete mode 100644 recipes/cli/site/src/components/Nav.astro delete mode 100644 recipes/cli/site/src/components/UsedBy.astro delete mode 100644 recipes/cli/site/src/data/site.ts delete mode 100644 recipes/cli/site/src/layouts/Layout.astro delete mode 100644 recipes/cli/site/src/pages/index.astro delete mode 100644 recipes/cli/site/src/styles/global.css delete mode 100644 recipes/cli/site/tsconfig.json delete mode 100644 recipes/conference/README.md delete mode 100644 recipes/conference/meta.json delete mode 100644 recipes/conference/prompt.md delete mode 100644 recipes/conference/site/.gitignore delete mode 100644 recipes/conference/site/astro.config.mjs delete mode 100644 recipes/conference/site/package.json delete mode 100644 recipes/conference/site/public/favicon.svg delete mode 100644 recipes/conference/site/src/components/About.astro delete mode 100644 recipes/conference/site/src/components/Faq.astro delete mode 100644 recipes/conference/site/src/components/Footer.astro delete mode 100644 recipes/conference/site/src/components/Hero.astro delete mode 100644 recipes/conference/site/src/components/Nav.astro delete mode 100644 recipes/conference/site/src/components/Schedule.astro delete mode 100644 recipes/conference/site/src/components/Speakers.astro delete mode 100644 recipes/conference/site/src/components/Sponsors.astro delete mode 100644 recipes/conference/site/src/components/Tickets.astro delete mode 100644 recipes/conference/site/src/components/Venue.astro delete mode 100644 recipes/conference/site/src/data/conf.ts delete mode 100644 recipes/conference/site/src/layouts/Layout.astro delete mode 100644 recipes/conference/site/src/pages/index.astro delete mode 100644 recipes/conference/site/src/styles/global.css delete mode 100644 recipes/conference/site/tsconfig.json delete mode 100644 recipes/dashboard/README.md delete mode 100644 recipes/dashboard/meta.json delete mode 100644 recipes/dashboard/prompt.md delete mode 100644 recipes/dashboard/site/app.js delete mode 100644 recipes/dashboard/site/index.html delete mode 100644 recipes/dashboard/site/styles.css delete mode 100644 recipes/deck/README.md delete mode 100644 recipes/deck/meta.json delete mode 100644 recipes/deck/prompt.md delete mode 100644 recipes/deck/site/assets/deck.js delete mode 100644 recipes/deck/site/assets/style.css delete mode 100644 recipes/deck/site/index.html delete mode 100644 recipes/docs/README.md delete mode 100644 recipes/docs/meta.json delete mode 100644 recipes/docs/prompt.md delete mode 100644 recipes/docs/site/.gitignore delete mode 100644 recipes/docs/site/astro.config.mjs delete mode 100644 recipes/docs/site/package.json delete mode 100644 recipes/docs/site/public/favicon.svg delete mode 100644 recipes/docs/site/src/components/CodeBlock.astro delete mode 100644 recipes/docs/site/src/components/Footer.astro delete mode 100644 recipes/docs/site/src/components/Search.astro delete mode 100644 recipes/docs/site/src/components/Sidebar.astro delete mode 100644 recipes/docs/site/src/components/TopNav.astro delete mode 100644 recipes/docs/site/src/data/site.ts delete mode 100644 recipes/docs/site/src/layouts/Base.astro delete mode 100644 recipes/docs/site/src/layouts/Docs.astro delete mode 100644 recipes/docs/site/src/pages/api.astro delete mode 100644 recipes/docs/site/src/pages/changelog.astro delete mode 100644 recipes/docs/site/src/pages/getting-started.astro delete mode 100644 recipes/docs/site/src/pages/guides/migrations.astro delete mode 100644 recipes/docs/site/src/pages/guides/persistence.astro delete mode 100644 recipes/docs/site/src/pages/guides/reactive-queries.astro delete mode 100644 recipes/docs/site/src/pages/index.astro delete mode 100644 recipes/docs/site/src/styles/global.css delete mode 100644 recipes/docs/site/tsconfig.json delete mode 100644 recipes/foodtruck/README.md delete mode 100644 recipes/foodtruck/meta.json delete mode 100644 recipes/foodtruck/prompt.md delete mode 100644 recipes/foodtruck/site/app.js delete mode 100644 recipes/foodtruck/site/index.html delete mode 100644 recipes/foodtruck/site/styles.css delete mode 100644 recipes/fundraiser/README.md delete mode 100644 recipes/fundraiser/meta.json delete mode 100644 recipes/fundraiser/prompt.md delete mode 100644 recipes/fundraiser/site/assets/app.js delete mode 100644 recipes/fundraiser/site/assets/styles.css delete mode 100644 recipes/fundraiser/site/index.html delete mode 100644 recipes/game/README.md delete mode 100644 recipes/game/meta.json delete mode 100644 recipes/game/prompt.md delete mode 100644 recipes/game/site/audio.js delete mode 100644 recipes/game/site/game.js delete mode 100644 recipes/game/site/index.html delete mode 100644 recipes/game/site/styles.css delete mode 100644 recipes/garden/README.md delete mode 100644 recipes/garden/meta.json delete mode 100644 recipes/garden/prompt.md delete mode 100644 recipes/garden/site/app.js delete mode 100644 recipes/garden/site/index.html delete mode 100644 recipes/garden/site/styles.css delete mode 100644 recipes/generative-art/README.md delete mode 100644 recipes/generative-art/meta.json delete mode 100644 recipes/generative-art/prompt.md delete mode 100644 recipes/generative-art/site/app.js delete mode 100644 recipes/generative-art/site/field.js delete mode 100644 recipes/generative-art/site/index.html delete mode 100644 recipes/generative-art/site/styles.css delete mode 100644 recipes/habits/README.md delete mode 100644 recipes/habits/meta.json delete mode 100644 recipes/habits/prompt.md delete mode 100644 recipes/habits/site/.gitignore delete mode 100644 recipes/habits/site/bun.lock delete mode 100644 recipes/habits/site/index.html delete mode 100644 recipes/habits/site/package.json delete mode 100644 recipes/habits/site/public/favicon.svg delete mode 100644 recipes/habits/site/src/App.jsx delete mode 100644 recipes/habits/site/src/components/HabitCard.jsx delete mode 100644 recipes/habits/site/src/components/HabitDetail.jsx delete mode 100644 recipes/habits/site/src/components/HabitModal.jsx delete mode 100644 recipes/habits/site/src/components/Header.jsx delete mode 100644 recipes/habits/site/src/components/Heatmap.jsx delete mode 100644 recipes/habits/site/src/components/Modal.jsx delete mode 100644 recipes/habits/site/src/components/MonthCalendar.jsx delete mode 100644 recipes/habits/site/src/components/SettingsModal.jsx delete mode 100644 recipes/habits/site/src/components/TodayPanel.jsx delete mode 100644 recipes/habits/site/src/lib/color.js delete mode 100644 recipes/habits/site/src/lib/date.js delete mode 100644 recipes/habits/site/src/lib/seed.js delete mode 100644 recipes/habits/site/src/lib/stats.js delete mode 100644 recipes/habits/site/src/lib/storage.js delete mode 100644 recipes/habits/site/src/main.jsx delete mode 100644 recipes/habits/site/src/styles.css delete mode 100644 recipes/habits/site/vite.config.js delete mode 100644 recipes/hackathon/README.md delete mode 100644 recipes/hackathon/meta.json delete mode 100644 recipes/hackathon/prompt.md delete mode 100644 recipes/hackathon/site/app.js delete mode 100644 recipes/hackathon/site/index.html delete mode 100644 recipes/hackathon/site/styles.css delete mode 100644 recipes/invitation/README.md delete mode 100644 recipes/invitation/meta.json delete mode 100644 recipes/invitation/prompt.md delete mode 100644 recipes/invitation/site/.gitignore delete mode 100644 recipes/invitation/site/astro.config.mjs delete mode 100644 recipes/invitation/site/package.json delete mode 100644 recipes/invitation/site/public/favicon.svg delete mode 100644 recipes/invitation/site/src/components/Details.astro delete mode 100644 recipes/invitation/site/src/components/Faq.astro delete mode 100644 recipes/invitation/site/src/components/Footer.astro delete mode 100644 recipes/invitation/site/src/components/Gallery.astro delete mode 100644 recipes/invitation/site/src/components/Hero.astro delete mode 100644 recipes/invitation/site/src/components/Nav.astro delete mode 100644 recipes/invitation/site/src/components/Registry.astro delete mode 100644 recipes/invitation/site/src/components/Rsvp.astro delete mode 100644 recipes/invitation/site/src/components/Story.astro delete mode 100644 recipes/invitation/site/src/components/Travel.astro delete mode 100644 recipes/invitation/site/src/data/wedding.ts delete mode 100644 recipes/invitation/site/src/layouts/Layout.astro delete mode 100644 recipes/invitation/site/src/pages/index.astro delete mode 100644 recipes/invitation/site/src/styles/global.css delete mode 100644 recipes/invitation/site/tsconfig.json delete mode 100644 recipes/links/README.md delete mode 100644 recipes/links/meta.json delete mode 100644 recipes/links/prompt.md delete mode 100644 recipes/links/site/index.html delete mode 100644 recipes/listing/README.md delete mode 100644 recipes/listing/meta.json delete mode 100644 recipes/listing/prompt.md delete mode 100644 recipes/listing/site/bun.lock delete mode 100644 recipes/listing/site/index.html delete mode 100644 recipes/listing/site/package.json delete mode 100644 recipes/listing/site/src/App.jsx delete mode 100644 recipes/listing/site/src/components/FloorPlan.jsx delete mode 100644 recipes/listing/site/src/components/Gallery.jsx delete mode 100644 recipes/listing/site/src/components/MortgageCalculator.jsx delete mode 100644 recipes/listing/site/src/components/TourForm.jsx delete mode 100644 recipes/listing/site/src/data.js delete mode 100644 recipes/listing/site/src/main.jsx delete mode 100644 recipes/listing/site/src/styles.css delete mode 100644 recipes/listing/site/vite.config.mjs delete mode 100644 recipes/memorial/README.md delete mode 100644 recipes/memorial/meta.json delete mode 100644 recipes/memorial/prompt.md delete mode 100644 recipes/memorial/site/index.html delete mode 100644 recipes/menu/README.md delete mode 100644 recipes/menu/meta.json delete mode 100644 recipes/menu/prompt.md delete mode 100644 recipes/menu/site/app.js delete mode 100644 recipes/menu/site/index.html delete mode 100644 recipes/menu/site/styles.css delete mode 100644 recipes/meta.example.json delete mode 100644 recipes/newsletter/README.md delete mode 100644 recipes/newsletter/meta.json delete mode 100644 recipes/newsletter/prompt.md delete mode 100644 recipes/newsletter/site/about.html delete mode 100644 recipes/newsletter/site/archive.html delete mode 100644 recipes/newsletter/site/index.html delete mode 100644 recipes/newsletter/site/issues/issue-85.html delete mode 100644 recipes/newsletter/site/issues/issue-86.html delete mode 100644 recipes/newsletter/site/issues/issue-87.html delete mode 100644 recipes/newsletter/site/script.js delete mode 100644 recipes/newsletter/site/styles.css delete mode 100644 recipes/newsletter/site/subscribe.html delete mode 100644 recipes/notes/README.md delete mode 100644 recipes/notes/meta.json delete mode 100644 recipes/notes/prompt.md delete mode 100644 recipes/notes/site/.gitignore delete mode 100644 recipes/notes/site/astro.config.mjs delete mode 100644 recipes/notes/site/bun.lock delete mode 100644 recipes/notes/site/package.json delete mode 100644 recipes/notes/site/public/favicon.svg delete mode 100644 recipes/notes/site/src/components/Graph.astro delete mode 100644 recipes/notes/site/src/components/StatusBadge.astro delete mode 100644 recipes/notes/site/src/content.config.ts delete mode 100644 recipes/notes/site/src/content/notes/atomic-notes.md delete mode 100644 recipes/notes/site/src/content/notes/backlinks.md delete mode 100644 recipes/notes/site/src/content/notes/digital-gardens.md delete mode 100644 recipes/notes/site/src/content/notes/evergreen-notes.md delete mode 100644 recipes/notes/site/src/content/notes/learning-in-public.md delete mode 100644 recipes/notes/site/src/content/notes/spaced-repetition.md delete mode 100644 recipes/notes/site/src/content/notes/tending-notes.md delete mode 100644 recipes/notes/site/src/content/notes/why-this-glasshouse.md delete mode 100644 recipes/notes/site/src/content/notes/working-with-the-garage-door-up.md delete mode 100644 recipes/notes/site/src/content/notes/zettelkasten.md delete mode 100644 recipes/notes/site/src/layouts/Layout.astro delete mode 100644 recipes/notes/site/src/lib/garden.ts delete mode 100644 recipes/notes/site/src/lib/remark-wikilinks.mjs delete mode 100644 recipes/notes/site/src/lib/site.ts delete mode 100644 recipes/notes/site/src/lib/slugify.mjs delete mode 100644 recipes/notes/site/src/pages/index.astro delete mode 100644 recipes/notes/site/src/pages/notes/[id].astro delete mode 100644 recipes/notes/site/src/styles/global.css delete mode 100644 recipes/notes/site/tsconfig.json delete mode 100644 recipes/party/README.md delete mode 100644 recipes/party/meta.json delete mode 100644 recipes/party/prompt.md delete mode 100644 recipes/party/site/.gitignore delete mode 100644 recipes/party/site/astro.config.mjs delete mode 100644 recipes/party/site/package.json delete mode 100644 recipes/party/site/public/favicon.svg delete mode 100644 recipes/party/site/src/components/Details.astro delete mode 100644 recipes/party/site/src/components/Footer.astro delete mode 100644 recipes/party/site/src/components/Hero.astro delete mode 100644 recipes/party/site/src/components/Location.astro delete mode 100644 recipes/party/site/src/components/Nav.astro delete mode 100644 recipes/party/site/src/components/Rsvp.astro delete mode 100644 recipes/party/site/src/components/Schedule.astro delete mode 100644 recipes/party/site/src/data/party.ts delete mode 100644 recipes/party/site/src/layouts/Layout.astro delete mode 100644 recipes/party/site/src/pages/index.astro delete mode 100644 recipes/party/site/src/styles/global.css delete mode 100644 recipes/party/site/tsconfig.json delete mode 100644 recipes/photos/README.md delete mode 100644 recipes/photos/meta.json delete mode 100644 recipes/photos/prompt.md delete mode 100644 recipes/photos/site/.gitignore delete mode 100644 recipes/photos/site/astro.config.mjs delete mode 100644 recipes/photos/site/bun.lock delete mode 100644 recipes/photos/site/package.json delete mode 100644 recipes/photos/site/public/favicon.svg delete mode 100644 recipes/photos/site/src/components/AboutTeaser.astro delete mode 100644 recipes/photos/site/src/components/Contact.astro delete mode 100644 recipes/photos/site/src/components/Footer.astro delete mode 100644 recipes/photos/site/src/components/Hero.astro delete mode 100644 recipes/photos/site/src/components/Nav.astro delete mode 100644 recipes/photos/site/src/components/Prints.astro delete mode 100644 recipes/photos/site/src/components/WorkIndex.astro delete mode 100644 recipes/photos/site/src/data/portfolio.ts delete mode 100644 recipes/photos/site/src/layouts/Layout.astro delete mode 100644 recipes/photos/site/src/pages/about.astro delete mode 100644 recipes/photos/site/src/pages/index.astro delete mode 100644 recipes/photos/site/src/pages/series/[slug].astro delete mode 100644 recipes/photos/site/src/styles/global.css delete mode 100644 recipes/photos/site/tsconfig.json delete mode 100644 recipes/portfolio/README.md delete mode 100644 recipes/portfolio/meta.json delete mode 100644 recipes/portfolio/prompt.md delete mode 100644 recipes/portfolio/site/index.html delete mode 100644 recipes/proposal/README.md delete mode 100644 recipes/proposal/meta.json delete mode 100644 recipes/proposal/prompt.md delete mode 100644 recipes/proposal/site/.gitignore delete mode 100644 recipes/proposal/site/astro.config.mjs delete mode 100644 recipes/proposal/site/bun.lock delete mode 100644 recipes/proposal/site/package.json delete mode 100644 recipes/proposal/site/public/favicon.svg delete mode 100644 recipes/proposal/site/src/data/proposal.ts delete mode 100644 recipes/proposal/site/src/layouts/Layout.astro delete mode 100644 recipes/proposal/site/src/pages/index.astro delete mode 100644 recipes/proposal/site/src/styles/global.css delete mode 100644 recipes/qr/README.md delete mode 100644 recipes/qr/meta.json delete mode 100644 recipes/qr/prompt.md delete mode 100644 recipes/qr/site/assets/app.js delete mode 100644 recipes/qr/site/assets/styles.css delete mode 100644 recipes/qr/site/index.html delete mode 100644 recipes/recipes/README.md delete mode 100644 recipes/recipes/meta.json delete mode 100644 recipes/recipes/prompt.md delete mode 100644 recipes/recipes/site/bun.lock delete mode 100644 recipes/recipes/site/index.html delete mode 100644 recipes/recipes/site/package.json delete mode 100644 recipes/recipes/site/src/App.jsx delete mode 100644 recipes/recipes/site/src/data.js delete mode 100644 recipes/recipes/site/src/format.js delete mode 100644 recipes/recipes/site/src/main.jsx delete mode 100644 recipes/recipes/site/src/styles.css delete mode 100644 recipes/recipes/site/vite.config.mjs delete mode 100644 recipes/report/README.md delete mode 100644 recipes/report/meta.json delete mode 100644 recipes/report/prompt.md delete mode 100644 recipes/report/site/.gitignore delete mode 100644 recipes/report/site/astro.config.mjs delete mode 100644 recipes/report/site/package.json delete mode 100644 recipes/report/site/public/favicon.svg delete mode 100644 recipes/report/site/src/components/BarChart.astro delete mode 100644 recipes/report/site/src/components/DivergingBars.astro delete mode 100644 recipes/report/site/src/components/DonutChart.astro delete mode 100644 recipes/report/site/src/components/HBarChart.astro delete mode 100644 recipes/report/site/src/components/HostingBars.astro delete mode 100644 recipes/report/site/src/components/PainPoints.astro delete mode 100644 recipes/report/site/src/components/ToolBars.astro delete mode 100644 recipes/report/site/src/data/survey.ts delete mode 100644 recipes/report/site/src/layouts/Layout.astro delete mode 100644 recipes/report/site/src/pages/index.astro delete mode 100644 recipes/report/site/src/styles/global.css delete mode 100644 recipes/resume/README.md delete mode 100644 recipes/resume/meta.json delete mode 100644 recipes/resume/prompt.md delete mode 100644 recipes/resume/site/assets/script.js delete mode 100644 recipes/resume/site/assets/styles.css delete mode 100644 recipes/resume/site/index.html delete mode 100644 recipes/revenue-review/README.md delete mode 100644 recipes/revenue-review/meta.json delete mode 100644 recipes/revenue-review/prompt.md delete mode 100644 recipes/revenue-review/site/index.html delete mode 100644 recipes/revenue-review/site/package.json delete mode 100644 recipes/revenue-review/site/src/App.jsx delete mode 100644 recipes/revenue-review/site/src/charts.jsx delete mode 100644 recipes/revenue-review/site/src/data.js delete mode 100644 recipes/revenue-review/site/src/format.js delete mode 100644 recipes/revenue-review/site/src/main.jsx delete mode 100644 recipes/revenue-review/site/src/styles.css delete mode 100644 recipes/revenue-review/site/vite.config.mjs delete mode 100644 recipes/roi/README.md delete mode 100644 recipes/roi/meta.json delete mode 100644 recipes/roi/prompt.md delete mode 100644 recipes/roi/site/assets/app.js delete mode 100644 recipes/roi/site/assets/styles.css delete mode 100644 recipes/roi/site/index.html delete mode 100644 recipes/saas/README.md delete mode 100644 recipes/saas/meta.json delete mode 100644 recipes/saas/prompt.md delete mode 100644 recipes/saas/site/.gitignore delete mode 100644 recipes/saas/site/astro.config.mjs delete mode 100644 recipes/saas/site/package.json delete mode 100644 recipes/saas/site/public/favicon.svg delete mode 100644 recipes/saas/site/src/components/ProductMock.astro delete mode 100644 recipes/saas/site/src/data/site.ts delete mode 100644 recipes/saas/site/src/layouts/Base.astro delete mode 100644 recipes/saas/site/src/pages/index.astro delete mode 100644 recipes/saas/site/src/styles/global.css delete mode 100644 recipes/saas/site/tsconfig.json delete mode 100644 recipes/savings/README.md delete mode 100644 recipes/savings/meta.json delete mode 100644 recipes/savings/prompt.md delete mode 100644 recipes/savings/site/app.js delete mode 100644 recipes/savings/site/index.html delete mode 100644 recipes/savings/site/styles.css delete mode 100644 recipes/soon/README.md delete mode 100644 recipes/soon/meta.json delete mode 100644 recipes/soon/prompt.md delete mode 100644 recipes/soon/site/assets/app.js delete mode 100644 recipes/soon/site/assets/styles.css delete mode 100644 recipes/soon/site/index.html delete mode 100644 recipes/status/README.md delete mode 100644 recipes/status/meta.json delete mode 100644 recipes/status/prompt.md delete mode 100644 recipes/status/site/app.js delete mode 100644 recipes/status/site/index.html delete mode 100644 recipes/status/site/styles.css delete mode 100644 recipes/studio/README.md delete mode 100644 recipes/studio/meta.json delete mode 100644 recipes/studio/prompt.md delete mode 100644 recipes/studio/site/app.js delete mode 100644 recipes/studio/site/index.html delete mode 100644 recipes/studio/site/styles.css delete mode 100644 recipes/technical-plan/README.md delete mode 100644 recipes/technical-plan/meta.json delete mode 100644 recipes/technical-plan/prompt.md delete mode 100644 recipes/technical-plan/site/index.html delete mode 100644 recipes/technical-plan/site/styles.css delete mode 100644 recipes/tip/README.md delete mode 100644 recipes/tip/meta.json delete mode 100644 recipes/tip/prompt.md delete mode 100644 recipes/tip/site/app.js delete mode 100644 recipes/tip/site/index.html delete mode 100644 recipes/tip/site/styles.css delete mode 100644 recipes/word-game/README.md delete mode 100644 recipes/word-game/meta.json delete mode 100644 recipes/word-game/prompt.md delete mode 100644 recipes/word-game/site/app.js delete mode 100644 recipes/word-game/site/index.html delete mode 100644 recipes/word-game/site/style.css delete mode 100644 recipes/word-game/site/words.js delete mode 100644 recipes/zine/README.md delete mode 100644 recipes/zine/meta.json delete mode 100644 recipes/zine/prompt.md delete mode 100644 recipes/zine/site/index.html delete mode 100644 scripts/build-manifest.mjs delete mode 100644 scripts/catalog.test.mjs delete mode 100644 scripts/validate-publish-dir.mjs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1121dff..c3e3e2b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -name: Validate examples and publish recipes +name: Validate examples on: pull_request: @@ -6,15 +6,9 @@ on: push: branches: [master] workflow_dispatch: - inputs: - only: - description: "Publish only this recipe slug (optional)" - required: false permissions: contents: read - pages: write - id-token: write concurrency: group: examples-${{ github.ref }} @@ -95,171 +89,3 @@ jobs: ;; esac test -s "$artifact" - - manifest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun test scripts/catalog.test.mjs - - uses: actions/configure-pages@v5 - if: github.event_name != 'pull_request' - - uses: actions/upload-pages-artifact@v3 - if: github.event_name != 'pull_request' - with: - path: dist - - deploy-manifest: - needs: manifest - if: github.event_name != 'pull_request' - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy canonical JSON feed - id: deployment - uses: actions/deploy-pages@v4 - - discover-recipes: - runs-on: ubuntu-latest - outputs: - slugs: ${{ steps.list.outputs.slugs }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - id: list - env: - ONLY: ${{ inputs.only }} - BEFORE: ${{ github.event.before }} - run: | - if [ -n "$ONLY" ]; then - test -d "recipes/$ONLY" - jq -cn --arg slug "$ONLY" '[ $slug ]' | sed 's/^/slugs=/' >> "$GITHUB_OUTPUT" - exit 0 - fi - - all_slugs() { - find recipes -mindepth 1 -maxdepth 1 -type d -print \ - | sed 's#^recipes/##' | sort | jq -R -s -c 'split("\n") | map(select(length > 0))' - } - - if [ "${{ github.event_name }}" = "pull_request" ] \ - || [ "${{ github.event_name }}" = "workflow_dispatch" ] \ - || [ -z "$BEFORE" ] \ - || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then - echo "slugs=$(all_slugs)" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # A pure rename (R100) moves a file without changing what it serves. - changed=$(git diff --name-status -M "$BEFORE" "${{ github.sha }}" \ - | awk -F'\t' '$1 != "R100" {print $NF}') - # A recipe directory that no longer exists has nothing to publish. - slugs=$(printf '%s\n' "$changed" | sed -n 's#^recipes/\([^/]*\)/site/.*#\1#p' \ - | sort -u | while read -r slug; do if [ -d "recipes/$slug" ]; then echo "$slug"; fi; done \ - | jq -R -s -c 'split("\n") | map(select(length > 0))') - echo "slugs=$slugs" >> "$GITHUB_OUTPUT" - - publish-recipes: - needs: discover-recipes - if: needs.discover-recipes.outputs.slugs != '[]' - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 4 - matrix: - slug: ${{ fromJson(needs.discover-recipes.outputs.slugs) }} - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - name: Install Spacefast CLI - run: bun add -g spacefast@0.8.0 - - name: Build - working-directory: recipes/${{ matrix.slug }} - run: | - runtime=$(jq -r '.runtime // "static"' meta.json) - if [ "$runtime" = "zero" ]; then - cd site - bun install --frozen-lockfile - if jq -e '.scripts.build' package.json >/dev/null; then - bun run build - fi - if [ -f dist/sf.jsonc ]; then - # Framework layout: the build script staged the capsule into - # dist/ and already ran `sf build dist` (e.g. astro-zero). - test -f dist/.spacefast/zero/public/index.html - else - sf build - test -f .spacefast/zero/public/index.html - fi - if jq -e '.scripts.test' package.json >/dev/null; then - bun run test - fi - elif [ "$runtime" = "functions" ]; then - cd site - bun install --frozen-lockfile - # Sanity build only: `sf publish` runs its own OpenNext build. - bun run build - elif [ -f site/package.json ]; then - cd site - bun install --frozen-lockfile || bun install - bun run build - fi - - name: Resolve and validate publish directory - id: dir - working-directory: recipes/${{ matrix.slug }} - run: | - runtime=$(jq -r '.runtime // "static"' meta.json) - if [ "$runtime" = "zero" ] && [ -f site/dist/sf.jsonc ]; then - dir=site/dist - elif [ "$runtime" = "zero" ] || [ "$runtime" = "functions" ]; then - dir=site - elif [ -d site/dist ]; then dir=site/dist - elif [ -d site/build ]; then dir=site/build - elif [ -d site/out ]; then dir=site/out - else dir=site - fi - echo "dir=$dir" >> "$GITHUB_OUTPUT" - if [ "$runtime" = "static" ]; then - bun ../../scripts/validate-publish-dir.mjs "${{ matrix.slug }}" "$dir" - fi - - name: Publish Spacefast space - if: github.event_name != 'pull_request' - working-directory: recipes/${{ matrix.slug }} - env: - SPACEFAST_TOKEN: ${{ secrets.SPACEFAST_DEPLOY_KEY }} - SPACEFAST_API_URL: https://api.spacefast.com - run: | - space_slug=$(jq -er '.publish_slug // .slug' meta.json) - runtime=$(jq -r '.runtime // "static"' meta.json) - publish_args=( - "${{ steps.dir.outputs.dir }}" - --team spacefast-examples - --source-type git - --git-repository "${{ github.repository }}" - --git-branch "${{ github.ref_name }}" - --git-commit "${{ github.sha }}" - --wait - --json - ) - if [ "$runtime" = "zero" ]; then - # The Build step already ran the framework build and `sf build`. - publish_args+=(--skip-build) - fi - - set +e - result=$(sf publish "${publish_args[@]}" --space "$space_slug" 2>&1) - exit_code=$? - set -e - printf '%s\n' "$result" - - if [ "$exit_code" -eq 0 ]; then - exit 0 - fi - if ! jq -e '.error.code == "not_found"' <<<"$result" >/dev/null; then - exit "$exit_code" - fi - - sf publish "${publish_args[@]}" --slug "$space_slug" diff --git a/README.md b/README.md index 6439781..743eea5 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ server. `astro-zero/` needs both: Astro's dev server for the pages and | [`wp-react/`](./wp-react/) | A Vite + React SPA reading WordPress posts in the browser, with the `_redirects` SPA fallback | | [`unfurl/`](./unfurl/) | Next.js SSR, a route handler, and middleware on the Functions runtime | | [`trailhead/`](./trailhead/) | A Next.js static export with `@spacefast/image` Site Accelerator images, `_redirects`, and `_headers` | -| [`recipes/`](./recipes/) | Canonical prompts, metadata, live outputs, and the public recipe feed | | [`kitchen-sink/`](./kitchen-sink/) | Zero, Pages, PHP, JavaScript, TypeScript, auth, mail, and routing in one project | | [`migrate-from-cloudflare-pages/`](./migrate-from-cloudflare-pages/) | Wrangler build settings and Pages convention files | | [`migrate-from-vercel/`](./migrate-from-vercel/) | `vercel.json` build, redirects, rewrites, headers, and cron import | @@ -47,22 +46,11 @@ server. `astro-zero/` needs both: Astro's dev server for the pages and Each example README calls out the important files, the expected build output, and any product boundary that cannot be translated automatically. -## Recipes integration - -`recipes/` is the source of truth for [spacefast.com/recipes](https://spacefast.com/recipes). -Each recipe contains its copy-paste prompt, gallery metadata, runnable output, -and live URL. GitHub Actions compiles those records into the historical public -feed at and publishes only -changed recipe outputs. - -Do not duplicate recipe prompts in another package or repository. Framework -examples teach an integration; recipes are user-facing starting points that an -agent can customize and publish. - ## Validation -The pull-request workflow builds every top-level project through `sf build`, -runs the Zero app tests, validates every recipe output, and builds the recipe -manifest, without creating a live version. `migrate-from-netlify` builds its -prebuilt output, because `sf build` has no flag to accept the Netlify features -that still need a port. +The pull-request workflow builds every top-level project through `sf build` +and runs the Zero app tests, without creating a live version. +`migrate-from-netlify` builds its prebuilt output, because `sf build` has no +flag to accept the Netlify features that still need a port. + +Recipe prompts live in their own repository and are published separately. diff --git a/recipes/2048/README.md b/recipes/2048/README.md deleted file mode 100644 index 1e83504..0000000 --- a/recipes/2048/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# 2048 - -A fully playable remake of the classic **2048** sliding-tile puzzle, built as a -no-build-step vanilla-JS app (`site/index.html` plus a CSS and JS file). Slide the -4×4 grid with the arrow keys or by swiping on a phone; matching tiles merge into -one (and only once per move), the score climbs by the value of each merge, and your -**best score persists across visits via `localStorage`**. Tiles slide via CSS -transforms, new tiles pop in, merges give a little bounce, and a floating "+N" rides -up over the score. Reaching the 2048 tile shows a win overlay with a "keep going" -option; running out of moves shows a game-over overlay. There's a New Game button, -a one-step Undo, and a live moves counter. Board size, winning tile, and the tile -palette are all driven by a few constants at the top of `game.js` (and CSS variables -in `style.css`), so the prompt's setup answers map onto real config. - -`prompt.md` is the copy-paste prompt that recreates the game from a few answers and -publishes it live to Spacefast. - -## Files - -- `site/index.html` — markup, board, overlays, and the Spacefast badge. -- `site/style.css` — theme variables, tile palette, layout, and animations. -- `site/game.js` — the game: grid state, move/merge engine, input, scoring, undo, and persistence. - -Open `site/index.html` directly in any browser — no install, no build. diff --git a/recipes/2048/meta.json b/recipes/2048/meta.json deleted file mode 100644 index ac8982d..0000000 --- a/recipes/2048/meta.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "2048", - "title": "2048", - "slug": "2048", - "vertical": "Tools, apps & games", - "tech": "Vanilla JS", - "summary": "The classic 2048 sliding-tile puzzle — arrow-key and swipe controls, smooth merge animations, score and persistent best score, and a win/lose overlay with restart and undo.", - "order": 300, - "live_url": "https://2048.view.fast/", - "setup_questions": [ - "What tile theme or color palette do you want?", - "Board size (default 4x4)?", - "What's the winning tile (default 2048)?" - ], - "photo_terms": [], - "sections": [ - "4x4 grid on a rounded board frame", - "Arrow-key + touch-swipe controls", - "Same-number tiles merge into one (once per move)", - "Score + best score persisted in localStorage", - "Sliding tile movement, spawn-in and merge-pop animations", - "Win overlay with 'keep going', game-over overlay, and restart", - "New Game button, one-step Undo, and moves counter" - ], - "based_on": "A faithful remake of Gabriele Cirulli's classic 2048 sliding-tile puzzle — the canonical evergreen single-file browser game." -} diff --git a/recipes/2048/prompt.md b/recipes/2048/prompt.md deleted file mode 100644 index c4e842b..0000000 --- a/recipes/2048/prompt.md +++ /dev/null @@ -1,59 +0,0 @@ -Build me a **fully playable 2048 sliding-tile puzzle game** as a vanilla-JS app -(a single `index.html` plus a small CSS and JS file — no framework, no build step). - -**Before you build, ask me these questions in one message and wait for my answers. If I skip anything, choose a sensible default and tell me what you chose:** - -1. What tile theme or color palette do you want? (e.g. the classic warm beige/orange, a neon dark mode, pastels — or describe your own.) -2. What board size should it be? (Default is 4×4; 5×5 plays slower and easier, 3×3 is frantic.) -3. What's the winning tile? (Default is 2048 — you can chase 1024 for a quicker win or 4096 for a longer grind.) - -**Then build a complete, polished, responsive game with:** - -- A square **board with a grid of cells** (sized from my board-size answer) sitting on a rounded board frame. -- **Arrow-key controls** (← ↑ → ↓) and **touch swipe** controls so it plays on phones too. Tiles slide as far as they can in the chosen direction. -- **Merging:** when two tiles with the same number collide they combine into one tile of double the value — and a tile can only merge once per move (so `2 2 2` becomes `4 2`, never `8`). -- A **score** that increases by the value of each merge, plus a **best score** that persists between visits via `localStorage`. -- **Smooth animations:** tiles slide via CSS transforms, new tiles pop in, and merged tiles give a little scale-up bounce. A small floating "+N" appears over the score on each merge. -- A **win overlay** when I reach the winning tile, with a "Keep going" option to chase a higher score, and a **game-over overlay** when no moves remain — both with a **restart** button. -- A **New Game** button, a one-step **Undo**, and a live moves counter. -- After every move, a new `2` (or occasionally a `4`) appears in a random empty cell. - -**Design & content notes:** - -- Default to the iconic 2048 look: a soft `#faf8ef` background, a `#bbada0` board, warm beige low tiles climbing through orange to a glowing gold `2048` tile — but honor my palette answer if I gave one. Use a clean sans like Inter, big bold tabular numerals, and a tasteful glow on the high-value tiles. -- This is a game, so there's no copy to write and **no images needed** — keep it clean, tactile, and fast. -- No build step — a single `index.html` with a linked CSS and JS file that opens straight in a browser. Make the game **actually work**: real keyboard and swipe input, correct merge rules, a real win/lose/restart loop, and best-score saved in `localStorage`. -- Make the board size, winning tile, and tile palette easy to change from a few constants/CSS variables at the top, so my answers map to real config. -- Keep it accessible (semantic HTML, a focusable board, visible focus ring, keyboard play, `prefers-reduced-motion` support, and good contrast). - -**Add this exact line right before `` so the site carries its badge:** - -```html - -``` - -**When the site is ready, publish it to Spacefast with the direct API — no account, install, or extra instructions needed:** - -1. Use `./site` as the publish folder. For no-build projects, put only the finished public files in that dedicated folder. Confirm it has `index.html` at its root and no secrets, source files, `.git`, or `.spacefast` state. -2. First publish: send every file as multipart form data. This Bash/Zsh block preserves relative paths and works for one file or a whole folder: - ```bash - PUBLISH_ROOT=./site - publish_files=() - while IFS= read -r -d "" file; do - relative=${file#"$PUBLISH_ROOT"/} - publish_files+=(-F "files=@$file;filename=$relative") - done < <(find "$PUBLISH_ROOT" -type f -print0) - curl -sS "${publish_files[@]}" "https://api.spacefast.com/v1/publish?wait=1" - ``` -3. From the `{ "data": ... }` receipt, give me `data.space.liveUrl`, `data.version.immutableUrl`, `data.claim.url`, and `data.claim.expiresAt`. Remind me to claim within 6 hours. Keep `data.claim.token` secret. -4. Save `data.space.id` and `data.claim.token` locally (for example in an ignored, mode-600 `.spacefast/state.json`). For a quick update, rebuild the `publish_files` array above and publish to the same space: - ```bash - SPACEFAST_SPACE_ID= - SPACEFAST_TOKEN= - curl -sS -H "Authorization: Bearer $SPACEFAST_TOKEN" \ - -F "spaceId=$SPACEFAST_SPACE_ID" "${publish_files[@]}" \ - "https://api.spacefast.com/v1/publish?wait=1" - ``` - If an update after claiming returns `space_claimed_credential_available`, exchange the saved claim token once at `POST https://api.spacefast.com/v1/anonymous-claim/exchange`, save `data.credential.accessToken`, and retry with that access token. - -**Optional shortcuts and reference only:** if the `sf` CLI is already installed, `sf publish ./site --wait` does the same job. A zip of the publish folder is also supported, but neither the CLI nor a zip is required. Docs: [direct agent/API publishing](https://spacefast.com/setup) · [files and folders](https://spacefast.com/help/publishing) · [claiming](https://spacefast.com/help/anonymous-publish) · [updates and rollback](https://spacefast.com/help/versions) diff --git a/recipes/2048/site/game.js b/recipes/2048/site/game.js deleted file mode 100644 index 71057ed..0000000 --- a/recipes/2048/site/game.js +++ /dev/null @@ -1,446 +0,0 @@ -/* 2048 — a faithful vanilla-JS remake. - * Tile-object model with stable ids so moves animate smoothly via CSS transforms. - * Controls: arrow keys + swipe. Score + best (localStorage). Win/lose + restart. Undo. */ - -(() => { - "use strict"; - - // ---- config (tweak these to re-theme / resize the board) ---- - const SIZE = 4; // board is SIZE x SIZE - const WIN_VALUE = 2048; // the tile you're chasing - const START_TILES = 2; // tiles seeded on a new game - const STORE_KEY = "spacefast-2048"; - - // ---- DOM ---- - const els = { - board: document.getElementById("board"), - gridBg: document.getElementById("gridBg"), - tiles: document.getElementById("tiles"), - score: document.getElementById("score"), - best: document.getElementById("best"), - scoreAdd: document.getElementById("scoreAdd"), - moves: document.getElementById("moves"), - goalLabel: document.getElementById("goalLabel"), - overlay: document.getElementById("overlay"), - overlayTitle: document.getElementById("overlayTitle"), - overlayText: document.getElementById("overlayText"), - newGame: document.getElementById("newGame"), - keepGoing: document.getElementById("keepGoing"), - tryAgain: document.getElementById("tryAgain"), - undoBtn: document.getElementById("undoBtn"), - }; - - document.documentElement.style.setProperty("--size", SIZE); - els.goalLabel.textContent = WIN_VALUE; - - // ---- state ---- - let grid; // 2D array [r][c] of tile objects or null - let tileEls; // Map - let nextId = 1; - let score = 0; - let best = loadBest(); - let moves = 0; - let won = false; // reached WIN_VALUE - let keepPlaying = false; - let over = false; - let history = null; // single-level undo snapshot - let busy = false; // ignore input mid-animation - - els.best.textContent = best; - - // build the static background cells - (function buildBg() { - const frag = document.createDocumentFragment(); - for (let i = 0; i < SIZE * SIZE; i++) { - const c = document.createElement("div"); - c.className = "cell"; - frag.appendChild(c); - } - els.gridBg.appendChild(frag); - })(); - - // ---------- helpers ---------- - function emptyGrid() { - return Array.from({ length: SIZE }, () => Array(SIZE).fill(null)); - } - - function cellsAvailable() { - for (let r = 0; r < SIZE; r++) - for (let c = 0; c < SIZE; c++) - if (!grid[r][c]) return true; - return false; - } - - function randomEmptyCell() { - const empties = []; - for (let r = 0; r < SIZE; r++) - for (let c = 0; c < SIZE; c++) - if (!grid[r][c]) empties.push({ r, c }); - if (!empties.length) return null; - return empties[Math.floor(Math.random() * empties.length)]; - } - - function addRandomTile() { - const cell = randomEmptyCell(); - if (!cell) return; - const value = Math.random() < 0.9 ? 2 : 4; - const tile = { id: nextId++, value, r: cell.r, c: cell.c, isNew: true, merged: false }; - grid[cell.r][cell.c] = tile; - } - - // ---------- rendering ---------- - function tileClass(tile) { - const len = String(tile.value).length; - let cls = `tile len${len}`; - if (tile.value > 2048) cls += " super"; - if (tile.isNew) cls += " new"; - if (tile.merged) cls += " merged"; - return cls; - } - - function positionStyle(tile) { - // translate in units of the tile's own size (100%) plus the gap between cells - const x = `calc(${tile.c} * 100% + ${tile.c} * var(--gap))`; - const y = `calc(${tile.r} * 100% + ${tile.r} * var(--gap))`; - return `translate(${x}, ${y})`; - } - - function createTileEl(tile) { - const el = document.createElement("div"); - el.style.width = `calc((100% - ${SIZE - 1} * var(--gap)) / ${SIZE})`; - el.style.height = `calc((100% - ${SIZE - 1} * var(--gap)) / ${SIZE})`; - const inner = document.createElement("div"); - inner.className = "tile-inner"; - el.appendChild(inner); - els.tiles.appendChild(el); - return el; - } - - function render() { - const seen = new Set(); - for (let r = 0; r < SIZE; r++) { - for (let c = 0; c < SIZE; c++) { - const tile = grid[r][c]; - if (!tile) continue; - seen.add(tile.id); - let el = tileEls.get(tile.id); - if (!el) { - el = createTileEl(tile); - tileEls.set(tile.id, el); - // place a brand-new tile at its cell WITHOUT animating in from the corner - el.style.transition = "none"; - el.style.transform = positionStyle(tile); - void el.offsetWidth; // force reflow so the no-transition placement sticks - el.style.transition = ""; - } - el.className = tileClass(tile); - el.dataset.val = tile.value; - el.querySelector(".tile-inner").textContent = tile.value; - el.style.transform = positionStyle(tile); - } - } - // remove DOM nodes for tiles that no longer exist (merged away) - for (const [id, el] of tileEls) { - if (!seen.has(id)) { - el.remove(); - tileEls.delete(id); - } - } - els.score.textContent = score; - els.best.textContent = best; - els.moves.textContent = moves; - els.undoBtn.disabled = !history; - } - - function flashScore(delta) { - if (delta <= 0) return; - els.scoreAdd.textContent = "+" + delta; - els.scoreAdd.classList.remove("run"); - // reflow to restart the animation - void els.scoreAdd.offsetWidth; - els.scoreAdd.classList.add("run"); - } - - // ---------- movement ---------- - // Build the ordered list of cell coordinates to traverse for a given direction. - const VECTORS = { - left: { dr: 0, dc: -1 }, - right: { dr: 0, dc: 1 }, - up: { dr: -1, dc: 0 }, - down: { dr: 1, dc: 0 }, - }; - - function traversals(dir) { - const rows = [...Array(SIZE).keys()]; - const cols = [...Array(SIZE).keys()]; - if (dir === "right") cols.reverse(); - if (dir === "down") rows.reverse(); - return { rows, cols }; - } - - function findFarthest(r, c, vec) { - let prev; - do { - prev = { r, c }; - r += vec.dr; - c += vec.dc; - } while (inBounds(r, c) && !grid[r][c]); - return { farthest: prev, next: inBounds(r, c) ? { r, c } : null }; - } - - function inBounds(r, c) { - return r >= 0 && r < SIZE && c >= 0 && c < SIZE; - } - - function move(dir) { - if (busy || over) return; - const vec = VECTORS[dir]; - const { rows, cols } = traversals(dir); - - // clear per-move flags - for (let r = 0; r < SIZE; r++) - for (let c = 0; c < SIZE; c++) { - if (grid[r][c]) { grid[r][c].isNew = false; grid[r][c].merged = false; } - } - - const snapshot = serialize(); - let moved = false; - let gained = 0; - - for (const r of rows) { - for (const c of cols) { - const tile = grid[r][c]; - if (!tile) continue; - const { farthest, next } = findFarthest(r, c, vec); - const target = next ? grid[next.r][next.c] : null; - - if (target && target.value === tile.value && !target.merged) { - // merge tile into target - target.value *= 2; - target.merged = true; - gained += target.value; - grid[tile.r][tile.c] = null; - // move the merging tile's element onto the target cell, then drop it - const movingEl = tileEls.get(tile.id); - if (movingEl) { - movingEl.style.transform = positionStyle({ r: next.r, c: next.c }); - movingEl.style.zIndex = "1"; - } - // retire the merged-away tile id after the slide completes - retireTile(tile.id); - if (target.value === WIN_VALUE && !won) { - won = true; - } - moved = true; - } else if (farthest.r !== r || farthest.c !== c) { - grid[r][c] = null; - tile.r = farthest.r; - tile.c = farthest.c; - grid[farthest.r][farthest.c] = tile; - moved = true; - } - } - } - - if (!moved) return; - - history = snapshot; - moves++; - if (gained > 0) { - score += gained; - if (score > best) { best = score; saveBest(best); } - flashScore(gained); - } - - busy = true; - render(); - - // after the slide animation, spawn a new tile and re-check end state - window.setTimeout(() => { - addRandomTile(); - render(); - busy = false; - checkEndState(); - }, 120); - } - - function retireTile(id) { - // element is removed on next render() once the id is gone from the grid, - // but we keep it during the slide so it visibly travels to the merge cell. - window.setTimeout(() => { - const el = tileEls.get(id); - if (el) { el.remove(); tileEls.delete(id); } - }, 120); - } - - // ---------- end-state ---------- - function movesAvailable() { - if (cellsAvailable()) return true; - for (let r = 0; r < SIZE; r++) { - for (let c = 0; c < SIZE; c++) { - const t = grid[r][c]; - if (!t) continue; - for (const key in VECTORS) { - const v = VECTORS[key]; - const nr = r + v.dr, nc = c + v.dc; - if (inBounds(nr, nc) && grid[nr][nc] && grid[nr][nc].value === t.value) - return true; - } - } - } - return false; - } - - function checkEndState() { - if (won && !keepPlaying) { - showOverlay("win"); - return; - } - if (!movesAvailable()) { - over = true; - showOverlay("lose"); - } - } - - function showOverlay(kind) { - els.overlay.hidden = false; - els.overlay.classList.remove("win", "lose"); - els.overlay.classList.add(kind); - if (kind === "win") { - els.overlayTitle.textContent = "You win! 🎉"; - els.overlayText.textContent = `You reached the ${WIN_VALUE} tile. Keep going for a higher score?`; - els.keepGoing.hidden = false; - } else { - els.overlayTitle.textContent = "Game over"; - els.overlayText.textContent = `No moves left. You scored ${score}.`; - els.keepGoing.hidden = true; - } - } - - function hideOverlay() { - els.overlay.hidden = true; - els.overlay.classList.remove("win", "lose"); - } - - // ---------- serialize / undo ---------- - function serialize() { - return { - grid: grid.map(row => row.map(t => (t ? { id: t.id, value: t.value, r: t.r, c: t.c } : null))), - score, moves, won, keepPlaying, over, nextId, - }; - } - - function restore(snap) { - grid = snap.grid.map(row => row.map(t => (t ? { ...t, isNew: false, merged: false } : null))); - score = snap.score; - moves = snap.moves; - won = snap.won; - keepPlaying = snap.keepPlaying; - over = snap.over; - nextId = snap.nextId; - } - - function undo() { - if (!history || busy) return; - // wipe current tile elements so positions re-seat cleanly - for (const [, el] of tileEls) el.remove(); - tileEls.clear(); - restore(history); - history = null; - over = false; - hideOverlay(); - render(); - } - - // ---------- lifecycle ---------- - function newGame() { - grid = emptyGrid(); - tileEls = new Map(); - nextId = 1; - score = 0; - moves = 0; - won = false; - keepPlaying = false; - over = false; - history = null; - busy = false; - // clear any lingering DOM - els.tiles.innerHTML = ""; - hideOverlay(); - for (let i = 0; i < START_TILES; i++) addRandomTile(); - render(); - els.board.focus(); - } - - // ---------- input ---------- - const KEY_DIR = { - ArrowLeft: "left", ArrowRight: "right", ArrowUp: "up", ArrowDown: "down", - a: "left", d: "right", w: "up", s: "down", - h: "left", l: "right", k: "up", j: "down", // vim - }; - - window.addEventListener("keydown", (e) => { - if (e.metaKey || e.ctrlKey || e.altKey) return; - const dir = KEY_DIR[e.key]; - if (dir) { - e.preventDefault(); - move(dir); - } else if (e.key.toLowerCase() === "u") { - undo(); - } else if (e.key.toLowerCase() === "r") { - newGame(); - } - }); - - // swipe - let touch = null; - els.board.addEventListener("touchstart", (e) => { - const t = e.changedTouches[0]; - touch = { x: t.clientX, y: t.clientY }; - }, { passive: true }); - - els.board.addEventListener("touchmove", (e) => { - // prevent the page from scrolling while swiping the board - if (touch) e.preventDefault(); - }, { passive: false }); - - els.board.addEventListener("touchend", (e) => { - if (!touch) return; - const t = e.changedTouches[0]; - const dx = t.clientX - touch.x; - const dy = t.clientY - touch.y; - const adx = Math.abs(dx), ady = Math.abs(dy); - const THRESH = 24; - if (Math.max(adx, ady) > THRESH) { - if (adx > ady) move(dx > 0 ? "right" : "left"); - else move(dy > 0 ? "down" : "up"); - } - touch = null; - }, { passive: true }); - - // buttons - els.newGame.addEventListener("click", newGame); - els.tryAgain.addEventListener("click", newGame); - els.undoBtn.addEventListener("click", undo); - els.keepGoing.addEventListener("click", () => { - keepPlaying = true; - hideOverlay(); - els.board.focus(); - }); - - // ---------- storage ---------- - function loadBest() { - try { - const raw = localStorage.getItem(STORE_KEY); - if (!raw) return 0; - const v = JSON.parse(raw); - return Number(v.best) || 0; - } catch { return 0; } - } - function saveBest(v) { - try { localStorage.setItem(STORE_KEY, JSON.stringify({ best: v })); } catch {} - } - - // boot - newGame(); -})(); diff --git a/recipes/2048/site/index.html b/recipes/2048/site/index.html deleted file mode 100644 index d25bf66..0000000 --- a/recipes/2048/site/index.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - 2048 — Join the tiles, reach 2048 - - - - - - - -
-
-
-

2048

-

Join the numbers to get to the 2048 tile!

-
- -
-
- Score - 0 - -
-
- Best - 0 -
-
-
- -
-

- How to play: use your ← ↑ → ↓ - arrow keys (or swipe) to slide the tiles. When two tiles with the same number touch, they - merge into one! -

- -
- -
-
- - - -
- - - -
-
- -
-
- - Moves: 0 - Goal: 2048 -
-

- A faithful remake of Gabriele Cirulli's 2048. Slide, merge, repeat. -

-
-
- -
-

Built as a Spacefast example · your score is saved in this browser only.

-
- - - - - diff --git a/recipes/2048/site/style.css b/recipes/2048/site/style.css deleted file mode 100644 index 6f3075c..0000000 --- a/recipes/2048/site/style.css +++ /dev/null @@ -1,366 +0,0 @@ -:root { - --bg: #faf8ef; - --text: #776e65; - --board: #bbada0; - --cell: #cdc1b4; - --accent: #8f7a66; - --accent-hover: #9f8b77; - --gap: 12px; - --radius: 8px; - --size: 4; /* board dimension, overridden by JS */ - - /* tile palette */ - --t-2: #eee4da; - --t-4: #ede0c8; - --t-8: #f2b179; - --t-16: #f59563; - --t-32: #f67c5f; - --t-64: #f65e3b; - --t-128: #edcf72; - --t-256: #edcc61; - --t-512: #edc850; - --t-1024: #edc53f; - --t-2048: #edc22e; - --t-super: #3c3a32; -} - -* { box-sizing: border-box; margin: 0; padding: 0; } - -html { -webkit-text-size-adjust: 100%; } - -body { - font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; - background: var(--bg); - color: var(--text); - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - padding: 24px 16px 8px; - -webkit-tap-highlight-color: transparent; - overscroll-behavior: none; -} - -.app { - width: 100%; - max-width: 480px; - flex: 1; -} - -/* ---------- masthead ---------- */ -.masthead { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16px; - margin-bottom: 18px; -} - -.logo { - font-size: clamp(48px, 14vw, 72px); - font-weight: 800; - line-height: 1; - color: var(--accent); - letter-spacing: -2px; -} - -.tagline { - margin-top: 6px; - font-size: 14px; - font-weight: 500; - color: var(--text); - max-width: 16ch; -} -.tagline strong { color: var(--accent); } - -.scores { - display: flex; - gap: 8px; - flex-shrink: 0; -} - -.score-box { - position: relative; - background: var(--board); - border-radius: var(--radius); - padding: 8px 16px; - min-width: 72px; - text-align: center; - color: #fff; -} - -.score-label { - display: block; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.08em; - color: #eee4da; -} - -.score-value { - display: block; - font-size: 22px; - font-weight: 800; - line-height: 1.2; - font-variant-numeric: tabular-nums; -} - -.score-add { - position: absolute; - left: 0; - right: 0; - top: 14px; - font-size: 18px; - font-weight: 800; - color: rgba(119, 110, 101, 0.95); - opacity: 0; - pointer-events: none; -} -.score-add.run { - animation: scoreFloat 0.6s ease-in forwards; -} -@keyframes scoreFloat { - 0% { opacity: 1; transform: translateY(0); } - 100% { opacity: 0; transform: translateY(-26px); } -} - -/* ---------- controls ---------- */ -.controls { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 18px; -} - -.how-to { - font-size: 13.5px; - line-height: 1.5; - color: var(--text); - flex: 1; -} -.how-to strong { color: var(--accent); } - -kbd { - display: inline-block; - font-family: inherit; - font-size: 11px; - font-weight: 700; - background: #fff; - border: 1px solid #d6cdc4; - border-bottom-width: 2px; - border-radius: 5px; - padding: 1px 5px; - color: var(--accent); -} - -.btn { - font-family: inherit; - font-weight: 700; - font-size: 14px; - border: none; - border-radius: var(--radius); - cursor: pointer; - transition: background 0.15s ease, transform 0.05s ease, color 0.15s ease; - white-space: nowrap; -} -.btn:active { transform: translateY(1px); } - -.btn-primary { - background: var(--accent); - color: #f9f6f2; - padding: 11px 18px; -} -.btn-primary:hover { background: var(--accent-hover); } - -.btn-ghost { - background: #eee4da; - color: var(--accent); - padding: 11px 18px; -} -.btn-ghost:hover { background: #e6dac8; } - -.btn-link { - background: transparent; - color: var(--accent); - padding: 6px 4px; - font-size: 13px; -} -.btn-link:hover:not(:disabled) { text-decoration: underline; } -.btn-link:disabled { opacity: 0.4; cursor: default; } - -/* ---------- board ---------- */ -.board-wrap { - width: 100%; -} - -.board { - position: relative; - width: 100%; - aspect-ratio: 1 / 1; - background: var(--board); - border-radius: var(--radius); - padding: var(--gap); - touch-action: none; - outline: none; -} -.board:focus-visible { - box-shadow: 0 0 0 3px rgba(143, 122, 102, 0.4); -} - -.grid-bg, -.tiles { - position: absolute; - inset: var(--gap); - display: grid; - grid-template-columns: repeat(var(--size), 1fr); - grid-template-rows: repeat(var(--size), 1fr); - gap: var(--gap); -} - -.grid-bg .cell { - background: var(--cell); - border-radius: 6px; -} - -.tiles { pointer-events: none; } - -/* a tile is absolutely positioned within .tiles using % offsets set in JS */ -.tile { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - border-radius: 6px; - font-weight: 800; - font-variant-numeric: tabular-nums; - background: var(--t-2); - color: var(--text); - transition: transform 0.12s ease-in-out; - will-change: transform; -} - -.tile-inner { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.tile[data-val="2"] { background: var(--t-2); } -.tile[data-val="4"] { background: var(--t-4); } -.tile[data-val="8"] { background: var(--t-8); color: #f9f6f2; } -.tile[data-val="16"] { background: var(--t-16); color: #f9f6f2; } -.tile[data-val="32"] { background: var(--t-32); color: #f9f6f2; } -.tile[data-val="64"] { background: var(--t-64); color: #f9f6f2; } -.tile[data-val="128"] { background: var(--t-128); color: #f9f6f2; box-shadow: 0 0 24px 4px rgba(243, 215, 116, 0.32); } -.tile[data-val="256"] { background: var(--t-256); color: #f9f6f2; box-shadow: 0 0 26px 6px rgba(243, 215, 116, 0.40); } -.tile[data-val="512"] { background: var(--t-512); color: #f9f6f2; box-shadow: 0 0 28px 8px rgba(243, 215, 116, 0.48); } -.tile[data-val="1024"] { background: var(--t-1024); color: #f9f6f2; box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0.55); } -.tile[data-val="2048"] { background: var(--t-2048); color: #f9f6f2; box-shadow: 0 0 32px 12px rgba(243, 215, 116, 0.65); } -.tile.super { background: var(--t-super); color: #f9f6f2; } - -/* font sizing by digit count */ -.tile.len1 .tile-inner { font-size: clamp(28px, 9vw, 42px); } -.tile.len2 .tile-inner { font-size: clamp(28px, 9vw, 42px); } -.tile.len3 .tile-inner { font-size: clamp(22px, 7vw, 34px); } -.tile.len4 .tile-inner { font-size: clamp(18px, 5.5vw, 28px); } -.tile.len5 .tile-inner { font-size: clamp(15px, 4.5vw, 22px); } - -/* spawn + merge pops */ -.tile.new .tile-inner { animation: appear 0.18s ease; } -.tile.merged .tile-inner { animation: pop 0.18s ease; } - -@keyframes appear { - 0% { transform: scale(0); } - 100% { transform: scale(1); } -} -@keyframes pop { - 0% { transform: scale(1); } - 50% { transform: scale(1.18); } - 100% { transform: scale(1); } -} - -/* ---------- overlay ---------- */ -.overlay { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(238, 228, 218, 0.78); - border-radius: var(--radius); - z-index: 10; - animation: fadeIn 0.4s ease; -} -.overlay[hidden] { display: none; } -.overlay.win { background: rgba(237, 194, 46, 0.55); } -.overlay.lose { background: rgba(238, 228, 218, 0.84); } - -@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } - -.overlay-card { text-align: center; padding: 24px; } -.overlay-title { - font-size: clamp(32px, 9vw, 48px); - font-weight: 800; - color: var(--accent); - margin-bottom: 6px; -} -.overlay-text { - font-size: 15px; - color: var(--text); - margin-bottom: 20px; -} -.overlay-actions { - display: flex; - gap: 10px; - justify-content: center; -} - -/* ---------- meta ---------- */ -.meta { margin-top: 18px; } -.meta-row { - display: flex; - align-items: center; - gap: 18px; - flex-wrap: wrap; - border-top: 1px solid #e3dacb; - padding-top: 14px; -} -.meta-stat { - font-size: 13px; - color: var(--text); -} -.meta-stat strong { color: var(--accent); font-variant-numeric: tabular-nums; } - -.credit { - margin-top: 12px; - font-size: 12.5px; - line-height: 1.5; - color: #a89c8d; -} -.credit em { font-style: italic; } - -.site-footer { - margin-top: 24px; - font-size: 12px; - color: #a89c8d; - text-align: center; -} - -/* ---------- responsive ---------- */ -@media (max-width: 420px) { - :root { --gap: 10px; } - body { padding: 16px 12px 8px; } - .controls { flex-direction: column; align-items: stretch; } - .controls .btn-primary { width: 100%; } -} - -@media (prefers-reduced-motion: reduce) { - .tile { transition: none; } - .tile.new .tile-inner, - .tile.merged .tile-inner, - .score-add.run, - .overlay { animation: none; } -} diff --git a/recipes/README.md b/recipes/README.md deleted file mode 100644 index 17297e8..0000000 --- a/recipes/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Recipes - -This directory powers the public Spacefast recipe gallery. A recipe is a prompt -an agent can customize, the finished site that proves the prompt, and metadata -for displaying both together. - -```text -recipes// - prompt.md # copy-paste instructions for an agent - meta.json # title, summary, setup questions, live URL, and publish mode - site/ # the finished static or Zero project - README.md # implementation notes and live link -``` - -Start with [`TEMPLATE.md`](./TEMPLATE.md) and -[`meta.example.json`](./meta.example.json). The directory name, metadata slug, -badge attribute, and default Spacefast space slug must agree. - -Run the catalog checks from the repository root: - -```sh -bun test scripts/catalog.test.mjs -``` - -On `master`, GitHub Actions rebuilds the public manifest and publishes only the -recipe outputs whose `site/` files changed. diff --git a/recipes/TEMPLATE.md b/recipes/TEMPLATE.md deleted file mode 100644 index 7490170..0000000 --- a/recipes/TEMPLATE.md +++ /dev/null @@ -1,74 +0,0 @@ -Build me a **{{one-line description of the site}}** as a {{tech: plain HTML & CSS / -vanilla-JS app / Astro site / Vite + React app / HTML5 canvas game}}. - -**Before you build, ask me these questions in one message and wait for my answers. -If I skip anything, choose a sensible default and tell me what you chose:** - -1. {{setup question 1 — e.g. "What's your band's name?"}} -2. {{setup question 2 — e.g. "What genre, in 2–3 words?"}} -3. {{setup question 3}} -4. {{setup question 4 (optional)}} - -**Then build a complete, polished, responsive site with:** - -- {{section / page 1}} -- {{section / page 2}} -- {{section / page 3}} -- {{...}} - -**Design & content notes:** - -- {{visual direction — palette, type, mood}} -- Use realistic content based on my answers — never "lorem ipsum". -- Use real images. Pull free photos that fit from Unsplash or Pexels, and add - descriptive `alt` text. -- {{tech specifics — e.g. "no build step" or "persist state in localStorage"}} -- Keep it accessible (semantic HTML, labels, keyboard support, good contrast). - -**Add this exact line right before `` so the site carries its badge:** - -```html - -``` - -**When the site is ready, publish it to Spacefast with the direct API — no -account, install, or extra instructions needed:** - -1. Use `./dist` as the publish folder. For no-build projects, put only the - finished public files in that dedicated folder. Confirm it has `index.html` - at its root and no secrets, source files, `.git`, or `.spacefast` state. -2. First publish: send every file as multipart form data. This Bash/Zsh block - preserves relative paths and works for one file or a whole folder: - ```bash - publish_files=() - while IFS= read -r -d '' file; do - relative=${file#./dist/} - publish_files+=(-F "files=@$file;filename=$relative") - done < <(find ./dist -type f -print0) - curl -sS "${publish_files[@]}" "https://api.spacefast.com/v1/publish?wait=1" - ``` -3. From the `{ "data": ... }` receipt, give me `data.space.liveUrl`, - `data.version.immutableUrl`, `data.claim.url`, and `data.claim.expiresAt`. - Remind me to claim within 6 hours. Keep `data.claim.token` secret. -4. Save `data.space.id` and `data.claim.token` locally (for example in an - ignored, mode-600 `.spacefast/state.json`). For a quick update, rebuild the - `publish_files` array above and publish to the same space: - ```bash - SPACEFAST_SPACE_ID= - SPACEFAST_TOKEN= - curl -sS -H "Authorization: Bearer $SPACEFAST_TOKEN" \ - -F "spaceId=$SPACEFAST_SPACE_ID" "${publish_files[@]}" \ - "https://api.spacefast.com/v1/publish?wait=1" - ``` - If an update after claiming returns `space_claimed_credential_available`, - exchange the saved claim token once at - `POST https://api.spacefast.com/v1/anonymous-claim/exchange`, save - `data.credential.accessToken`, and retry with that access token. - -**Optional shortcuts and reference only:** if the `sf` CLI is already installed, -`sf publish ./dist --wait` does the same job. A zip of the publish folder is also -supported, but neither the CLI nor a zip is required. Docs: -[agent setup](https://spacefast.com/setup) · -[files and folders](https://spacefast.com/help/publishing) · -[claiming](https://spacefast.com/help/anonymous-publish) · -[updates and rollback](https://spacefast.com/help/versions) diff --git a/recipes/app/README.md b/recipes/app/README.md deleted file mode 100644 index b8d47ba..0000000 --- a/recipes/app/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Tasklane - -A polished multi-view task tracker built as a minimal Vite + React app — no -backend, no accounts, everything saved in `localStorage`. It opens on a **kanban -board** whose cards drag between workflow lanes (Backlog → To Do → In Progress → -In Review → Done), each card showing labels, a priority dot, an assignee avatar, -a relative due date, a subtask progress bar, and a comment count. A **List view** -toggle shows the same tasks as a table you can sort by status, priority, or due -date and group by lane, collapsing cleanly on mobile. - -Clicking any card opens a **detail drawer** that slides in from the right: edit -the title inline, change status / assignee / priority / due date / labels, write -a description, add and check off subtasks (with a live progress bar), and post -comments to an activity thread. The toolbar adds full-text **search** and -**filters** by person, priority, and label, and the header carries live sprint -stats with a conic-gradient "cycle done" ring. - -It seeds a believable "Orbit — Q3 mobile sprint" with five teammates and a dozen -real tasks on first load, persists every edit (tasks, theme, and current view), -and ships a **reset** button plus a light/dark theme. Mobile-first, fully -keyboard-operable (Enter/Space opens a card, Esc closes the drawer), with good -contrast in both themes. Built from `prompt.md` and published to Spacefast — see -`meta.json` for the live link. - -## Run it locally - -```bash -cd site -bun install # or npm install -bun run dev # or npm run dev -``` - -Build the static bundle with `bun run build` — output lands in `site/dist/`. diff --git a/recipes/app/meta.json b/recipes/app/meta.json deleted file mode 100644 index d5008ff..0000000 --- a/recipes/app/meta.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Tasklane", - "title": "App prototype", - "slug": "app", - "vertical": "Tools, apps & games", - "tech": "Vite + React", - "summary": "A multi-view task app prototype — a draggable kanban board, a sortable list view, and a task detail drawer with subtasks, comments, filters, and search, all saved on your device.", - "order": 300, - "live_url": "https://app.view.fast/", - "setup_questions": [ - "What does the app track and who's it for (a product sprint, freelance client work, a content calendar)?", - "What are the workflow lanes/columns (e.g. Backlog → To Do → In Progress → In Review → Done)?", - "Who's on the team — 3–5 names with a role each — and what labels do you use (Design, Frontend, Bug, Growth)?", - "Seed it with a believable starter set of ~12 tasks across the lanes, or name a project and we'll invent realistic ones." - ], - "photo_terms": [ - "kanban board sticky notes overhead", - "team product planning workspace whiteboard", - "software team standup laptops desk", - "portrait headshot friendly colleague" - ], - "sections": [ - "Kanban board with draggable cards across workflow lanes", - "List view — sortable, grouped by status, mobile-friendly", - "Task detail drawer with inline edit, subtasks, and comments", - "Toolbar with full-text search and person/priority/label filters", - "Header with live sprint stats and a cycle-progress ring", - "Seeded sample data with a reset button", - "localStorage persistence and a light/dark theme" - ], - "based_on": "A realistic, evergreen example modeled on a common real-world need — a small product team's task tracker in the vein of Trello / Linear / Height." -} diff --git a/recipes/app/prompt.md b/recipes/app/prompt.md deleted file mode 100644 index c207120..0000000 --- a/recipes/app/prompt.md +++ /dev/null @@ -1,91 +0,0 @@ -Build me a **multi-view task-tracking app — a kanban board, a list view, and a -task detail drawer** as a **Vite + React app** that saves everything in the -browser (no backend, no accounts, all state in `localStorage`). - -**Before you build, ask me these questions in one message and wait for my answers. If I skip anything, choose a sensible default and tell me what you chose:** - -1. What does the app track and who's it for (e.g. "a product sprint for a small - mobile-app team", "my freelance client work", "a content calendar")? -2. What are the workflow lanes/columns (e.g. Backlog → To Do → In Progress → - In Review → Done)? -3. Who's on the team — 3–5 names with a role each — and what labels do you use - (e.g. Design, Frontend, Backend, Bug, Growth)? -4. Seed it with a believable starter set of ~12 tasks across the lanes, or tell - me a project and I'll invent realistic ones. - -**Then build a complete, polished, responsive app with:** - -- A **kanban board**: one column per lane, each with a colored status dot, a live - task count, and a quick "add task" button. Cards are **draggable between - columns** (HTML5 drag-and-drop) and the target column highlights as you hover. -- **Cards** that show the title, label chips, a priority dot, an assignee avatar, - a relative due-date pill (with "soon" and "overdue" states), a subtask - progress bar, and a comment count — plus an optional cover image. -- A **List view** toggle: the same tasks as a sortable table grouped by status, - sortable by status / priority / due date, collapsing to a clean stacked layout - on mobile. -- A **task detail drawer** that slides in from the right: edit the title inline, - change status / assignee / priority / due date / labels, edit the description, - add and check off **subtasks** (with a live progress bar), and post **comments** - to an activity thread. Includes a delete action. Closes on Esc or backdrop click. -- A **toolbar** with full-text **search** (matches title, description, and - subtasks) and **filters** by person, priority, and label, with a one-click - "clear filters" that shows how many tasks are visible. -- A **header** with at-a-glance stats: in-flight, shipped, overdue, and a - conic-gradient ring for "cycle done" percent. -- **Seeded sample data** on first load and a **reset** button to restore it. -- A **light/dark theme** toggle. - -**Design & content notes:** - -- Modern productivity-tool aesthetic — think Linear / Trello / Height: a deep, - slightly blue-tinted dark default with a soft radial glow, a violet accent - (`#6d5efc`), crisp cards with subtle lift on hover, and rounded corners. Use - **Sora** for headings and **Inter** for body. Ship a clean white light theme too. -- Use realistic content based on my answers — never "lorem ipsum". Invent - believable task titles, owners, priorities, and short comment threads if I - leave gaps. -- Use real images for the team avatars and the occasional card cover. Pull free - photos that fit, e.g. seeded `https://picsum.photos/seed/team-avatar-1/96/96` - (vary the seed) or Unsplash photos matching `kanban board sticky notes` / - `team product planning workspace`, and add descriptive `alt` text. -- Persist all state (tasks, theme, current view) in `localStorage` so edits - survive a refresh; reseed on reset. -- Keep it accessible: semantic HTML, labelled controls, keyboard support - (Enter/Space to open a card, Esc to close the drawer, focus-visible rings), - and good contrast in both themes. Mobile-first — the board stacks to a single - column and the drawer goes full-width on small screens. -- Vite + React with `base: "./"` so it works from any subpath, and a - `public/_redirects` file containing `/* /index.html 200`. - -**Add this exact line right before `` so the site carries its badge:** - -```html - -``` - -**When the site is ready, publish it to Spacefast with the direct API — no account, install, or extra instructions needed:** - -1. Use `./dist` as the publish folder. For no-build projects, put only the finished public files in that dedicated folder. Confirm it has `index.html` at its root and no secrets, source files, `.git`, or `.spacefast` state. -2. First publish: send every file as multipart form data. This Bash/Zsh block preserves relative paths and works for one file or a whole folder: - ```bash - PUBLISH_ROOT=./dist - publish_files=() - while IFS= read -r -d "" file; do - relative=${file#"$PUBLISH_ROOT"/} - publish_files+=(-F "files=@$file;filename=$relative") - done < <(find "$PUBLISH_ROOT" -type f -print0) - curl -sS "${publish_files[@]}" "https://api.spacefast.com/v1/publish?wait=1" - ``` -3. From the `{ "data": ... }` receipt, give me `data.space.liveUrl`, `data.version.immutableUrl`, `data.claim.url`, and `data.claim.expiresAt`. Remind me to claim within 6 hours. Keep `data.claim.token` secret. -4. Save `data.space.id` and `data.claim.token` locally (for example in an ignored, mode-600 `.spacefast/state.json`). For a quick update, rebuild the `publish_files` array above and publish to the same space: - ```bash - SPACEFAST_SPACE_ID= - SPACEFAST_TOKEN= - curl -sS -H "Authorization: Bearer $SPACEFAST_TOKEN" \ - -F "spaceId=$SPACEFAST_SPACE_ID" "${publish_files[@]}" \ - "https://api.spacefast.com/v1/publish?wait=1" - ``` - If an update after claiming returns `space_claimed_credential_available`, exchange the saved claim token once at `POST https://api.spacefast.com/v1/anonymous-claim/exchange`, save `data.credential.accessToken`, and retry with that access token. - -**Optional shortcuts and reference only:** if the `sf` CLI is already installed, `sf publish ./dist --wait` does the same job. A zip of the publish folder is also supported, but neither the CLI nor a zip is required. Docs: [direct agent/API publishing](https://spacefast.com/setup) · [files and folders](https://spacefast.com/help/publishing) · [claiming](https://spacefast.com/help/anonymous-publish) · [updates and rollback](https://spacefast.com/help/versions) diff --git a/recipes/app/site/.gitignore b/recipes/app/site/.gitignore deleted file mode 100644 index 0ca39c0..0000000 --- a/recipes/app/site/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -dist -.DS_Store diff --git a/recipes/app/site/bun.lock b/recipes/app/site/bun.lock deleted file mode 100644 index fd5ae81..0000000 --- a/recipes/app/site/bun.lock +++ /dev/null @@ -1,248 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 1, - "workspaces": { - "": { - "name": "tasklane", - "dependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1", - }, - "devDependencies": { - "@vitejs/plugin-react": "^4.3.4", - "vite": "^6.0.7", - }, - }, - }, - "packages": { - "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - - "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], - - "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], - - "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], - - "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], - - "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], - - "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw=="], - - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q=="], - - "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - - "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - - "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - - "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="], - - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="], - - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="], - - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="], - - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="], - - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], - - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], - - "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], - - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], - - "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - - "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], - - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.40", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw=="], - - "browserslist": ["browserslist@4.28.4", "", { "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", "electron-to-chromium": "^1.5.376", "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="], - - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.381", "", {}, "sha512-n9Wa6yB+vDsGuA8AKbl/0z7HbvWqt5jxIdvr1IUicd0ryPrk7/xzwqLv8D9AbbvZ6avVNtXYLTfmgFHkwkyelg=="], - - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], - - "node-releases": ["node-releases@2.0.50", "", {}, "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - - "postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], - - "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], - - "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], - - "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="], - - "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], - - "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - - "vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], - - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - } -} diff --git a/recipes/app/site/index.html b/recipes/app/site/index.html deleted file mode 100644 index f3a45da..0000000 --- a/recipes/app/site/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - Tasklane — Plan, track, ship - - -
- - - - diff --git a/recipes/app/site/package.json b/recipes/app/site/package.json deleted file mode 100644 index 5678b63..0000000 --- a/recipes/app/site/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "tasklane", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@vitejs/plugin-react": "^4.3.4", - "vite": "^6.0.7" - } -} diff --git a/recipes/app/site/public/_redirects b/recipes/app/site/public/_redirects deleted file mode 100644 index bbb3e7a..0000000 --- a/recipes/app/site/public/_redirects +++ /dev/null @@ -1 +0,0 @@ -/* /index.html 200 diff --git a/recipes/app/site/public/favicon.svg b/recipes/app/site/public/favicon.svg deleted file mode 100644 index feb3735..0000000 --- a/recipes/app/site/public/favicon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/recipes/app/site/src/App.jsx b/recipes/app/site/src/App.jsx deleted file mode 100644 index 1ef076d..0000000 --- a/recipes/app/site/src/App.jsx +++ /dev/null @@ -1,224 +0,0 @@ -import { useEffect, useMemo, useState, useCallback } from "react"; -import Toolbar from "./components/Toolbar.jsx"; -import Board from "./components/Board.jsx"; -import ListView from "./components/ListView.jsx"; -import TaskDrawer from "./components/TaskDrawer.jsx"; -import { loadState, saveState, resetState } from "./lib/storage.js"; -import { newId } from "./lib/util.js"; -import { IconFlame, IconReset } from "./components/icons.jsx"; - -export default function App() { - const [state, setState] = useState(() => loadState()); - const [view, setView] = useState(state.view || "board"); - const [query, setQuery] = useState(""); - const [filters, setFilters] = useState({ - assignee: null, - priority: null, - label: null, - }); - const [sort, setSort] = useState("status"); - const [openId, setOpenId] = useState(null); - - const { tasks, theme } = state; - - // Persist + reflect theme on . - useEffect(() => { - saveState({ ...state, view }); - document.documentElement.dataset.theme = theme; - }, [state, view, theme]); - - const setTasks = useCallback( - (updater) => - setState((s) => ({ - ...s, - tasks: typeof updater === "function" ? updater(s.tasks) : updater, - })), - [] - ); - - const patchTask = useCallback( - (id, fields) => - setTasks((ts) => ts.map((t) => (t.id === id ? { ...t, ...fields } : t))), - [setTasks] - ); - - const moveTask = useCallback( - (id, status) => patchTask(id, { status }), - [patchTask] - ); - - const deleteTask = useCallback( - (id) => { - setTasks((ts) => ts.filter((t) => t.id !== id)); - setOpenId(null); - }, - [setTasks] - ); - - const addTask = useCallback( - (status = "todo") => { - const id = newId("t"); - const task = { - id, - title: "", - description: "", - status, - priority: "medium", - assignee: null, - labels: [], - due: null, - subtasks: [], - comments: [], - cover: null, - createdAt: new Date().toISOString(), - }; - setTasks((ts) => [task, ...ts]); - setOpenId(id); - }, - [setTasks] - ); - - const toggleTheme = () => - setState((s) => ({ ...s, theme: s.theme === "dark" ? "light" : "dark" })); - - const onReset = () => { - if ( - window.confirm( - "Reset Tasklane to the sample sprint? Your changes will be cleared." - ) - ) { - const fresh = resetState(); - setState(fresh); - setView(fresh.view); - setOpenId(null); - setQuery(""); - setFilters({ assignee: null, priority: null, label: null }); - } - }; - - // Filter + search (search matches title, description, and subtask titles). - const filtered = useMemo(() => { - const q = query.trim().toLowerCase(); - return tasks.filter((t) => { - if (filters.assignee && t.assignee !== filters.assignee) return false; - if (filters.priority && t.priority !== filters.priority) return false; - if (filters.label && !t.labels.includes(filters.label)) return false; - if (q) { - const hay = [ - t.title, - t.description, - ...(t.subtasks || []).map((s) => s.title), - ] - .join(" ") - .toLowerCase(); - if (!hay.includes(q)) return false; - } - return true; - }); - }, [tasks, filters, query]); - - const openTask = openId ? tasks.find((t) => t.id === openId) : null; - - const stats = useMemo(() => { - const total = tasks.length; - const done = tasks.filter((t) => t.status === "done").length; - const active = tasks.filter( - (t) => t.status === "in_progress" || t.status === "review" - ).length; - const overdue = tasks.filter( - (t) => t.due && new Date(t.due) < new Date() && t.status !== "done" - ).length; - return { total, done, active, overdue, pct: total ? Math.round((done / total) * 100) : 0 }; - }, [tasks]); - - return ( -
-
-
-
- -
-
- {stats.active} - In flight -
-
- {stats.done} - Shipped -
-
- {stats.overdue} - Overdue -
-
-
- {stats.pct}% -
- Cycle done -
- -
-
- - addTask("todo")} - counts={{ shown: filtered.length, total: tasks.length }} - /> - -
- {filtered.length === 0 ? ( -
- -

Nothing matches

-

Try clearing a filter or your search to see more tasks.

-
- ) : view === "board" ? ( - - ) : ( - - )} -
- - {openTask && ( - setOpenId(null)} - onPatch={patchTask} - onDelete={deleteTask} - /> - )} -
- ); -} diff --git a/recipes/app/site/src/components/Avatar.jsx b/recipes/app/site/src/components/Avatar.jsx deleted file mode 100644 index 23f375e..0000000 --- a/recipes/app/site/src/components/Avatar.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import { memberById } from "../lib/util.js"; - -export default function Avatar({ id, size = 26, title }) { - const m = memberById(id); - if (!m) { - return ( - - ); - } - return ( - - {m.name} - - ); -} diff --git a/recipes/app/site/src/components/Badges.jsx b/recipes/app/site/src/components/Badges.jsx deleted file mode 100644 index 8764f1e..0000000 --- a/recipes/app/site/src/components/Badges.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import { priorityOf, labelOf } from "../lib/util.js"; - -export function PriorityDot({ id, withLabel = false }) { - const p = priorityOf(id); - return ( - - - {withLabel && {p.name}} - - ); -} - -export function LabelChip({ id }) { - const l = labelOf(id); - if (!l) return null; - return ( - - {l.name} - - ); -} diff --git a/recipes/app/site/src/components/Board.jsx b/recipes/app/site/src/components/Board.jsx deleted file mode 100644 index 5f40d44..0000000 --- a/recipes/app/site/src/components/Board.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import { useState } from "react"; -import Card from "./Card.jsx"; -import { COLUMNS } from "../lib/constants.js"; -import { IconPlus } from "./icons.jsx"; - -export default function Board({ tasks, onOpen, onMove, onAdd }) { - const [drag, setDrag] = useState(null); // dragging task id - const [over, setOver] = useState(null); // column being hovered - - const byColumn = (status) => tasks.filter((t) => t.status === status); - - const onDragStart = (e, task) => { - setDrag(task.id); - e.dataTransfer.effectAllowed = "move"; - try { - e.dataTransfer.setData("text/plain", task.id); - } catch { - /* some browsers are picky; state already holds the id */ - } - }; - const onDragEnd = () => { - setDrag(null); - setOver(null); - }; - const onDrop = (e, status) => { - e.preventDefault(); - if (drag) onMove(drag, status); - setDrag(null); - setOver(null); - }; - - return ( -
- {COLUMNS.map((col) => { - const items = byColumn(col.id); - return ( -
{ - e.preventDefault(); - setOver(col.id); - }} - onDragLeave={(e) => { - if (!e.currentTarget.contains(e.relatedTarget)) setOver(null); - }} - onDrop={(e) => onDrop(e, col.id)} - aria-label={`${col.name}, ${items.length} tasks`} - > -
-
- -

{col.name}

- {items.length} -
- -
- -
- {items.length === 0 && ( - - )} - {items.map((task) => ( - - ))} -
-
- ); - })} -
- ); -} diff --git a/recipes/app/site/src/components/Card.jsx b/recipes/app/site/src/components/Card.jsx deleted file mode 100644 index 30b8e2b..0000000 --- a/recipes/app/site/src/components/Card.jsx +++ /dev/null @@ -1,81 +0,0 @@ -import Avatar from "./Avatar.jsx"; -import { PriorityDot, LabelChip } from "./Badges.jsx"; -import { IconChat, IconChecklist, IconClock } from "./icons.jsx"; -import { dueInfo, progressOf } from "../lib/util.js"; - -export default function Card({ task, onOpen, onDragStart, onDragEnd, dragging }) { - const due = dueInfo(task.due); - const prog = progressOf(task); - const comments = task.comments?.length || 0; - - return ( -
onDragStart(e, task)} - onDragEnd={onDragEnd} - onClick={() => onOpen(task.id)} - onKeyDown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - onOpen(task.id); - } - }} - tabIndex={0} - role="button" - aria-label={`Open task: ${task.title}`} - > - {task.cover && ( -
- -
- )} - - {task.labels?.length > 0 && ( -
- {task.labels.map((l) => ( - - ))} -
- )} - -

{task.title}

- -
- -
- {prog && ( - - - {prog.done}/{prog.total} - - )} - {comments > 0 && ( - 1 ? "s" : ""}`}> - - {comments} - - )} -
-
- - {prog && ( - - )} - -
- {due ? ( - - - {due.label} - - ) : ( - No date - )} - -
-
- ); -} diff --git a/recipes/app/site/src/components/ListView.jsx b/recipes/app/site/src/components/ListView.jsx deleted file mode 100644 index 7bd9945..0000000 --- a/recipes/app/site/src/components/ListView.jsx +++ /dev/null @@ -1,131 +0,0 @@ -import { useState } from "react"; -import Avatar from "./Avatar.jsx"; -import { PriorityDot, LabelChip } from "./Badges.jsx"; -import { COLUMNS, PRIORITIES } from "../lib/constants.js"; -import { dueInfo, progressOf, priorityOf } from "../lib/util.js"; -import { IconChevron, IconClock } from "./icons.jsx"; - -const columnName = (id) => COLUMNS.find((c) => c.id === id)?.name || id; - -export default function ListView({ tasks, onOpen, sort, setSort }) { - const [collapsed, setCollapsed] = useState({}); - - const sorted = [...tasks].sort((a, b) => { - if (sort === "priority") - return priorityOf(a.priority).rank - priorityOf(b.priority).rank; - if (sort === "due") { - const av = a.due ? new Date(a.due).getTime() : Infinity; - const bv = b.due ? new Date(b.due).getTime() : Infinity; - return av - bv; - } - return 0; // "status" — keep board order, grouping handles it - }); - - // Group by status when sorting by status; otherwise one flat group. - const groups = - sort === "status" - ? COLUMNS.map((c) => ({ - key: c.id, - name: c.name, - items: sorted.filter((t) => t.status === c.id), - })).filter((g) => g.items.length) - : [{ key: "all", name: null, items: sorted }]; - - return ( -
-
- Sort by - {[ - ["status", "Status"], - ["priority", "Priority"], - ["due", "Due date"], - ].map(([id, label]) => ( - - ))} -
- - - - {groups.map((g) => ( -
- {g.name && ( - - )} - {!collapsed[g.key] && - g.items.map((t) => { - const due = dueInfo(t.due); - const prog = progressOf(t); - return ( - - ); - })} -
- ))} -
- ); -} diff --git a/recipes/app/site/src/components/TaskDrawer.jsx b/recipes/app/site/src/components/TaskDrawer.jsx deleted file mode 100644 index e62e88f..0000000 --- a/recipes/app/site/src/components/TaskDrawer.jsx +++ /dev/null @@ -1,330 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import Avatar from "./Avatar.jsx"; -import { MEMBERS } from "../lib/seed.js"; -import { - COLUMNS, - PRIORITY_ORDER, - LABEL_ORDER, - PRIORITIES, - LABELS, -} from "../lib/constants.js"; -import { newId, timeAgo, memberById, progressOf } from "../lib/util.js"; -import { - IconClose, - IconTrash, - IconCheck, - IconPlus, - IconChecklist, - IconChat, -} from "./icons.jsx"; - -export default function TaskDrawer({ task, onClose, onPatch, onDelete }) { - const [comment, setComment] = useState(""); - const [newSub, setNewSub] = useState(""); - const titleRef = useRef(null); - const closeRef = useRef(null); - - // Esc to close; focus the close button on open for keyboard users. - useEffect(() => { - const onKey = (e) => { - if (e.key === "Escape") onClose(); - }; - document.addEventListener("keydown", onKey); - closeRef.current?.focus(); - return () => document.removeEventListener("keydown", onKey); - }, [onClose]); - - if (!task) return null; - - const patch = (fields) => onPatch(task.id, fields); - const prog = progressOf(task); - - const toggleLabel = (id) => { - const has = task.labels.includes(id); - patch({ - labels: has ? task.labels.filter((l) => l !== id) : [...task.labels, id], - }); - }; - - const addSub = (e) => { - e.preventDefault(); - const title = newSub.trim(); - if (!title) return; - patch({ subtasks: [...task.subtasks, { id: newId("s"), title, done: false }] }); - setNewSub(""); - }; - const toggleSub = (id) => - patch({ - subtasks: task.subtasks.map((s) => - s.id === id ? { ...s, done: !s.done } : s - ), - }); - const removeSub = (id) => - patch({ subtasks: task.subtasks.filter((s) => s.id !== id) }); - - const addComment = (e) => { - e.preventDefault(); - const text = comment.trim(); - if (!text) return; - patch({ - comments: [ - ...task.comments, - { id: newId("c"), author: "m_dani", text, at: new Date().toISOString() }, - ], - }); - setComment(""); - }; - - return ( -
-
- - - - - - - - - - diff --git a/recipes/arcade/site/styles.css b/recipes/arcade/site/styles.css deleted file mode 100644 index 6691a68..0000000 --- a/recipes/arcade/site/styles.css +++ /dev/null @@ -1,724 +0,0 @@ -/* =========================================================== - Quarter Up Barcade — neon / CRT theme - =========================================================== */ -:root { - --bg: #07010f; - --bg-2: #0e0322; - --bg-3: #150633; - --panel: rgba(255, 255, 255, 0.04); - --ink: #f4ecff; - --ink-dim: #b6a8d6; - --ink-faint: #7d6fa3; - - --pink: #ff2db8; - --cyan: #21e6ff; - --lime: #b6ff3d; - --gold: #ffd23d; - --violet: #9b5cff; - - --line: rgba(155, 92, 255, 0.22); - --radius: 14px; - --maxw: 1180px; - --shadow-pink: 0 0 24px rgba(255, 45, 184, 0.45); - --shadow-cyan: 0 0 24px rgba(33, 230, 255, 0.4); - - --font-display: "Press Start 2P", system-ui, monospace; - --font-body: "Space Grotesk", system-ui, -apple-system, sans-serif; -} - -* { box-sizing: border-box; } - -html { scroll-behavior: smooth; } -@media (prefers-reduced-motion: reduce) { - html { scroll-behavior: auto; } - * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; } -} - -body { - margin: 0; - font-family: var(--font-body); - color: var(--ink); - background: - radial-gradient(900px 600px at 12% -10%, rgba(255, 45, 184, 0.18), transparent 60%), - radial-gradient(800px 600px at 95% 0%, rgba(33, 230, 255, 0.16), transparent 55%), - linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 55%, var(--bg) 100%); - background-attachment: fixed; - line-height: 1.6; - -webkit-font-smoothing: antialiased; - overflow-x: hidden; -} - -/* subtle scanline overlay */ -body::after { - content: ""; - position: fixed; - inset: 0; - pointer-events: none; - background: repeating-linear-gradient( - to bottom, - rgba(255, 255, 255, 0.025) 0px, - rgba(255, 255, 255, 0.025) 1px, - transparent 2px, - transparent 4px - ); - mix-blend-mode: overlay; - z-index: 1; -} - -img { max-width: 100%; display: block; } - -a { color: var(--cyan); text-decoration: none; } -a:hover { text-decoration: underline; } - -:focus-visible { - outline: 3px solid var(--lime); - outline-offset: 3px; - border-radius: 4px; -} - -.skip-link { - position: absolute; - left: -999px; - top: 0; - background: var(--lime); - color: #07010f; - padding: 0.6rem 1rem; - font-weight: 700; - z-index: 100; -} -.skip-link:focus { left: 12px; top: 12px; } - -/* ---------- buttons ---------- */ -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - font-family: var(--font-body); - font-weight: 700; - font-size: 0.95rem; - letter-spacing: 0.01em; - padding: 0.8rem 1.4rem; - border-radius: 999px; - border: 1.5px solid transparent; - cursor: pointer; - transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease; - text-decoration: none; -} -.btn:hover { transform: translateY(-2px); text-decoration: none; } -.btn:active { transform: translateY(0); } - -.btn--primary { - background: linear-gradient(120deg, var(--pink), var(--violet)); - color: #fff; - box-shadow: var(--shadow-pink); -} -.btn--primary:hover { box-shadow: 0 0 34px rgba(255, 45, 184, 0.65); } - -.btn--ghost { - background: transparent; - color: var(--cyan); - border-color: rgba(33, 230, 255, 0.5); -} -.btn--ghost:hover { background: rgba(33, 230, 255, 0.08); box-shadow: var(--shadow-cyan); } - -.btn--small { padding: 0.5rem 1rem; font-size: 0.85rem; } -.btn--block { width: 100%; } - -/* ---------- header ---------- */ -.site-header { - position: sticky; - top: 0; - z-index: 50; - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - padding: 0.9rem clamp(1rem, 4vw, 2.5rem); - background: rgba(7, 1, 15, 0.72); - backdrop-filter: blur(12px); - border-bottom: 1px solid var(--line); -} - -.brand { - display: inline-flex; - align-items: center; - gap: 0.55rem; - font-family: var(--font-display); - font-size: 0.95rem; - color: var(--ink); - text-decoration: none; - letter-spacing: 0.02em; -} -.brand:hover { text-decoration: none; } -.brand__mark { - color: var(--pink); - text-shadow: var(--shadow-pink); - font-size: 1rem; -} -.brand__name { white-space: nowrap; } -.brand__up { color: var(--cyan); text-shadow: var(--shadow-cyan); margin-left: 0.15em; } - -.site-nav { display: flex; align-items: center; gap: 1.5rem; } -.site-nav a { - color: var(--ink-dim); - font-weight: 600; - font-size: 0.95rem; -} -.site-nav a:hover { color: var(--ink); text-decoration: none; } -.site-nav .btn { color: #fff; } - -.nav-toggle { - display: none; - flex-direction: column; - gap: 5px; - background: none; - border: 0; - cursor: pointer; - padding: 8px; -} -.nav-toggle span { - width: 26px; height: 2.5px; - background: var(--ink); - border-radius: 2px; - transition: transform 0.2s ease, opacity 0.2s ease; -} - -.mobile-nav { - display: flex; - flex-direction: column; - background: var(--bg-2); - border-bottom: 1px solid var(--line); - padding: 0.5rem 1.25rem 1.25rem; - position: sticky; - top: 60px; - z-index: 40; -} -.mobile-nav a { - padding: 0.85rem 0.25rem; - color: var(--ink); - font-weight: 600; - border-bottom: 1px solid var(--line); -} -.mobile-nav a:hover { text-decoration: none; color: var(--pink); } - -/* ---------- hero ---------- */ -.hero { - max-width: var(--maxw); - margin: 0 auto; - padding: clamp(2rem, 6vw, 4.5rem) clamp(1rem, 4vw, 2.5rem) 2.5rem; - display: grid; - grid-template-columns: 1.05fr 0.95fr; - gap: clamp(1.5rem, 5vw, 3.5rem); - align-items: center; - position: relative; - z-index: 2; -} - -.eyebrow { - font-family: var(--font-display); - font-size: 0.62rem; - letter-spacing: 0.12em; - color: var(--cyan); - text-transform: uppercase; - margin: 0 0 1.1rem; -} - -.hero__title { - font-family: var(--font-display); - font-size: clamp(2.6rem, 9vw, 5.4rem); - line-height: 0.95; - margin: 0 0 1.2rem; - letter-spacing: 0.01em; -} -.hero__title .line { display: block; color: var(--ink); } -.hero__title .line--accent { - color: var(--pink); - text-shadow: 0 0 18px rgba(255, 45, 184, 0.6), 0 0 42px rgba(255, 45, 184, 0.3); - -webkit-text-stroke: 1px rgba(255, 45, 184, 0.5); -} - -.hero__tagline { - font-size: clamp(1.05rem, 2.4vw, 1.25rem); - color: var(--ink-dim); - max-width: 32ch; - margin: 0 0 1.8rem; -} - -.hero__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-bottom: 2.2rem; } - -.hero__stats { - display: flex; - gap: 2.4rem; - margin: 0; -} -.hero__stats div { display: flex; flex-direction: column; } -.hero__stats dt { - order: 2; - font-size: 0.72rem; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--ink-faint); -} -.hero__stats dd { - order: 1; - margin: 0; - font-family: var(--font-display); - font-size: 1.5rem; - color: var(--lime); - text-shadow: 0 0 14px rgba(182, 255, 61, 0.4); -} - -/* ---------- game cabinet ---------- */ -.hero__game { display: flex; justify-content: center; } - -.cabinet { - margin: 0; - width: 100%; - max-width: 440px; - background: linear-gradient(165deg, var(--bg-3), #0a0220); - border: 1px solid var(--line); - border-radius: 22px; - padding: 14px 14px 10px; - box-shadow: - 0 0 0 1px rgba(255, 45, 184, 0.12), - 0 30px 80px -30px rgba(255, 45, 184, 0.5), - inset 0 1px 0 rgba(255, 255, 255, 0.06); -} - -.cabinet__screen { - position: relative; - border-radius: 12px; - overflow: hidden; - background: #02000a; - box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.9), inset 0 0 0 2px rgba(33, 230, 255, 0.18); - aspect-ratio: 480 / 600; -} - -#game { - display: block; - width: 100%; - height: 100%; - background: #02000a; - touch-action: none; -} - -.overlay { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - background: radial-gradient(circle at 50% 40%, rgba(20, 6, 51, 0.82), rgba(2, 0, 10, 0.94)); - text-align: center; - padding: 1.5rem; - transition: opacity 0.25s ease; -} -.overlay[data-state="playing"] { display: none; } - -.overlay__card { max-width: 320px; } -.overlay__kicker { - font-family: var(--font-display); - font-size: 0.55rem; - color: var(--cyan); - letter-spacing: 0.16em; - margin: 0 0 0.9rem; -} -.overlay__title { - font-family: var(--font-display); - font-size: clamp(1.4rem, 6vw, 2rem); - color: var(--pink); - text-shadow: 0 0 20px rgba(255, 45, 184, 0.7); - margin: 0 0 0.8rem; - line-height: 1.1; -} -.overlay__sub { - color: var(--ink-dim); - font-size: 0.95rem; - margin: 0 0 1.4rem; - min-height: 2.6em; -} -.btn--start { - font-family: var(--font-display); - font-size: 0.78rem; - padding: 0.95rem 1.4rem; - letter-spacing: 0.04em; - animation: pulse 1.6s ease-in-out infinite; -} -@keyframes pulse { - 0%, 100% { box-shadow: 0 0 18px rgba(255, 45, 184, 0.5); } - 50% { box-shadow: 0 0 36px rgba(255, 45, 184, 0.9); } -} -.overlay__controls { - margin: 1.3rem 0 0; - font-size: 0.78rem; - color: var(--ink-faint); -} -kbd { - font-family: var(--font-body); - background: rgba(255, 255, 255, 0.08); - border: 1px solid var(--line); - border-radius: 5px; - padding: 0.1em 0.45em; - font-size: 0.85em; - color: var(--ink); -} - -.cabinet__hud { - display: flex; - justify-content: space-between; - gap: 0.5rem; - padding: 0.7rem 0.4rem 0.4rem; - font-family: var(--font-display); - font-size: 0.5rem; - color: var(--ink-faint); - letter-spacing: 0.06em; -} -.hud__item b { color: var(--lime); font-weight: 400; } -#hiscore { color: var(--gold); } - -.cabinet__caption { - font-size: 0.78rem; - color: var(--ink-faint); - text-align: center; - padding: 0.4rem 0.6rem 0.2rem; -} - -/* ---------- marquee ---------- */ -.marquee { - overflow: hidden; - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); - background: rgba(255, 45, 184, 0.06); - position: relative; - z-index: 2; -} -.marquee__track { - display: inline-flex; - gap: 2.5rem; - white-space: nowrap; - padding: 0.7rem 0; - font-family: var(--font-display); - font-size: 0.62rem; - color: var(--gold); - letter-spacing: 0.1em; - animation: scroll 26s linear infinite; -} -.marquee__track span { flex: none; } -@keyframes scroll { - from { transform: translateX(0); } - to { transform: translateX(-50%); } -} - -/* ---------- generic section ---------- */ -.section { - max-width: var(--maxw); - margin: 0 auto; - padding: clamp(3rem, 8vw, 5.5rem) clamp(1rem, 4vw, 2.5rem); - position: relative; - z-index: 2; -} -.section--alt { - max-width: none; - background: - linear-gradient(180deg, transparent, rgba(33, 230, 255, 0.04), transparent); - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); -} -.section--alt > * { max-width: var(--maxw); margin-left: auto; margin-right: auto; } - -.section__head { max-width: 56ch; margin-bottom: 2.5rem; } -.section__title { - font-family: var(--font-display); - font-size: clamp(1.5rem, 4.5vw, 2.5rem); - margin: 0 0 1rem; - line-height: 1.15; - color: var(--ink); -} -.section__lede { color: var(--ink-dim); font-size: 1.08rem; margin: 0; } - -/* ---------- cabinets grid ---------- */ -.cabinet-grid { - list-style: none; - margin: 0 0 2.5rem; - padding: 0; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); - gap: 1rem; -} -.cab-card { - position: relative; - background: var(--panel); - border: 1px solid var(--line); - border-radius: var(--radius); - padding: 1.4rem 1.2rem 1.2rem; - transition: transform 0.15s ease, border-color 0.2s ease, box-shadow 0.2s ease; -} -.cab-card:hover { - transform: translateY(-4px); - border-color: rgba(255, 45, 184, 0.5); - box-shadow: 0 16px 40px -20px rgba(255, 45, 184, 0.6); -} -.cab-card__badge { - font-family: var(--font-display); - font-size: 0.5rem; - color: var(--cyan); - letter-spacing: 0.08em; -} -.cab-card h3 { margin: 0.7rem 0 0.5rem; font-size: 1.3rem; color: var(--ink); } -.cab-card p { margin: 0 0 1rem; color: var(--ink-dim); font-size: 0.95rem; } -.cab-card__tag { - display: inline-block; - font-size: 0.72rem; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--lime); - border: 1px solid rgba(182, 255, 61, 0.35); - border-radius: 999px; - padding: 0.25rem 0.7rem; -} - -.floor-shot, .tap-shot, .map { margin: 0; } -.floor-shot, .tap-shot { - border-radius: var(--radius); - overflow: hidden; - border: 1px solid var(--line); -} -.floor-shot img, .tap-shot img { width: 100%; height: auto; object-fit: cover; } -.floor-shot figcaption { - padding: 0.8rem 1rem; - font-size: 0.85rem; - color: var(--ink-faint); - background: var(--panel); -} - -/* ---------- taps ---------- */ -.tap-layout { - display: grid; - grid-template-columns: 1.4fr 1fr; - gap: 2rem; - margin-bottom: 2.5rem; -} -.tap-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; } -.tap { - display: grid; - grid-template-columns: 1fr auto auto; - grid-template-areas: "name abv price" "meta abv price"; - align-items: center; - gap: 0 1rem; - padding: 0.85rem 0.4rem; - border-bottom: 1px dashed var(--line); -} -.tap__name { grid-area: name; font-weight: 700; font-size: 1.05rem; color: var(--ink); } -.tap__meta { grid-area: meta; font-size: 0.82rem; color: var(--ink-faint); } -.tap__abv { grid-area: abv; font-size: 0.8rem; color: var(--cyan); font-variant-numeric: tabular-nums; } -.tap__price { grid-area: price; font-family: var(--font-display); font-size: 0.8rem; color: var(--gold); } - -.cocktails { - background: var(--panel); - border: 1px solid var(--line); - border-radius: var(--radius); - padding: 1.5rem; - align-self: start; -} -.cocktails__head { - font-family: var(--font-display); - font-size: 0.85rem; - color: var(--pink); - margin: 0 0 1.2rem; -} -.cocktail-list { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: 1rem; } -.cocktail-list li { display: grid; gap: 0.2rem; } -.cocktail__name { font-weight: 700; color: var(--ink); } -.cocktail__desc { font-size: 0.9rem; color: var(--ink-dim); } -.cocktails__note { font-size: 0.82rem; color: var(--ink-faint); margin: 0; } - -/* ---------- events ---------- */ -.event-grid { - list-style: none; - margin: 0 0 2rem; - padding: 0; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); - gap: 1rem; -} -.event { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 1.1rem; - align-items: start; - background: var(--panel); - border: 1px solid var(--line); - border-radius: var(--radius); - padding: 1.3rem; - transition: border-color 0.2s ease, box-shadow 0.2s ease; -} -.event:hover { border-color: rgba(33, 230, 255, 0.5); box-shadow: var(--shadow-cyan); } -.event__day { - font-family: var(--font-display); - font-size: 0.7rem; - color: var(--cyan); - background: rgba(33, 230, 255, 0.1); - border-radius: 8px; - padding: 0.55rem 0.5rem; - letter-spacing: 0.04em; -} -.event__body h3 { margin: 0 0 0.35rem; font-size: 1.15rem; color: var(--ink); } -.event__body p { margin: 0 0 0.6rem; font-size: 0.92rem; color: var(--ink-dim); } -.event__prize { - display: inline-block; - font-size: 0.78rem; - color: var(--lime); - background: rgba(182, 255, 61, 0.08); - border: 1px solid rgba(182, 255, 61, 0.3); - border-radius: 999px; - padding: 0.2rem 0.65rem; -} -.event__time { font-family: var(--font-display); font-size: 0.6rem; color: var(--gold); white-space: nowrap; } - -.happy-hour { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 1.2rem; - background: linear-gradient(120deg, rgba(255, 45, 184, 0.12), rgba(155, 92, 255, 0.12)); - border: 1px solid rgba(255, 45, 184, 0.35); - border-radius: var(--radius); - padding: 1.6rem 1.8rem; -} -.happy-hour h3 { margin: 0 0 0.3rem; font-size: 1.3rem; color: var(--ink); } -.happy-hour p { margin: 0; color: var(--ink-dim); } - -/* ---------- visit ---------- */ -.visit-layout { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2.5rem; - align-items: start; -} -.visit-info { display: grid; gap: 2rem; } -.visit-block h3 { - font-family: var(--font-display); - font-size: 0.78rem; - color: var(--cyan); - margin: 0 0 0.9rem; - letter-spacing: 0.04em; -} -.visit-block address { font-style: normal; color: var(--ink); font-size: 1.1rem; line-height: 1.7; } -.visit-line { margin: 0.5rem 0 0; } -.visit-line a { font-size: 1.05rem; } - -.hours { width: 100%; border-collapse: collapse; max-width: 360px; } -.hours th, .hours td { text-align: left; padding: 0.55rem 0; border-bottom: 1px dashed var(--line); } -.hours th { color: var(--ink-dim); font-weight: 600; } -.hours td { color: var(--ink); text-align: right; font-variant-numeric: tabular-nums; } - -.policy { margin: 0; padding-left: 1.1rem; color: var(--ink-dim); display: grid; gap: 0.6rem; } -.policy strong { color: var(--ink); } - -.map { border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); } -.map img { width: 100%; height: auto; object-fit: cover; } -.map figcaption { - padding: 0.9rem 1.1rem; - font-size: 0.88rem; - color: var(--ink-faint); - background: var(--panel); - display: flex; - flex-wrap: wrap; - gap: 0.4rem 1rem; - justify-content: space-between; -} - -/* ---------- book ---------- */ -.book { } -.book__inner { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2.5rem; - align-items: start; - background: - radial-gradient(600px 400px at 100% 0%, rgba(33, 230, 255, 0.12), transparent 60%), - var(--panel); - border: 1px solid var(--line); - border-radius: 20px; - padding: clamp(1.6rem, 4vw, 3rem); -} -.book__perks { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: 0.7rem; } -.book__perks li { padding-left: 1.6rem; position: relative; color: var(--ink-dim); } -.book__perks li::before { - content: "▶"; - position: absolute; left: 0; - color: var(--pink); - font-size: 0.8rem; -} - -.book-form { display: grid; gap: 1rem; } -.field { display: grid; gap: 0.4rem; } -.field--row { grid-template-columns: 1fr 1fr; gap: 1rem; } -.field--row > div { display: grid; gap: 0.4rem; } -.book-form label { font-size: 0.85rem; font-weight: 600; color: var(--ink-dim); } -.book-form input, .book-form textarea { - font-family: var(--font-body); - font-size: 1rem; - color: var(--ink); - background: rgba(2, 0, 10, 0.6); - border: 1px solid var(--line); - border-radius: 10px; - padding: 0.75rem 0.85rem; - width: 100%; -} -.book-form input:focus, .book-form textarea:focus { - outline: none; - border-color: var(--cyan); - box-shadow: 0 0 0 3px rgba(33, 230, 255, 0.18); -} -.book-form textarea { resize: vertical; } -.book-form__success { - margin: 0; - padding: 0.9rem 1rem; - background: rgba(182, 255, 61, 0.1); - border: 1px solid rgba(182, 255, 61, 0.4); - border-radius: 10px; - color: var(--lime); - font-weight: 600; -} - -/* ---------- footer ---------- */ -.site-footer { - border-top: 1px solid var(--line); - background: rgba(2, 0, 10, 0.6); - padding: 2.5rem clamp(1rem, 4vw, 2.5rem) 2rem; - display: grid; - gap: 1.5rem; - position: relative; - z-index: 2; -} -.site-footer__brand { display: grid; gap: 0.4rem; } -.site-footer__brand .brand__name { font-family: var(--font-display); font-size: 1rem; } -.site-footer__brand p { margin: 0; color: var(--ink-faint); font-size: 0.9rem; } -.site-footer__links, .site-footer__social { display: flex; flex-wrap: wrap; gap: 1.2rem; } -.site-footer__links a { color: var(--ink-dim); font-weight: 600; } -.site-footer__social a { color: var(--cyan); font-weight: 600; } -.site-footer__legal { margin: 0; color: var(--ink-faint); font-size: 0.82rem; } - -/* =========================================================== - Responsive - =========================================================== */ -@media (max-width: 900px) { - .hero { grid-template-columns: 1fr; } - .hero__game { order: -1; } - .cabinet { max-width: 380px; } - .tap-layout, .visit-layout, .book__inner { grid-template-columns: 1fr; } - .site-nav { display: none; } - .nav-toggle { display: flex; } -} - -@media (max-width: 560px) { - .hero__stats { gap: 1.5rem; } - .field--row { grid-template-columns: 1fr; } - .event { grid-template-columns: auto 1fr; } - .event__time { grid-column: 2; justify-self: start; } - .happy-hour { flex-direction: column; align-items: flex-start; } - .cabinet__hud { font-size: 0.42rem; } -} diff --git a/recipes/arcade/site/ui.js b/recipes/arcade/site/ui.js deleted file mode 100644 index 3dac572..0000000 --- a/recipes/arcade/site/ui.js +++ /dev/null @@ -1,42 +0,0 @@ -/* Site chrome: mobile nav toggle + booking form success state. */ -(function () { - "use strict"; - - // --- mobile nav --- - var toggle = document.querySelector(".nav-toggle"); - var menu = document.getElementById("mobile-nav"); - if (toggle && menu) { - toggle.addEventListener("click", function () { - var open = toggle.getAttribute("aria-expanded") === "true"; - toggle.setAttribute("aria-expanded", String(!open)); - menu.hidden = open; - }); - menu.addEventListener("click", function (e) { - if (e.target.tagName === "A") { - menu.hidden = true; - toggle.setAttribute("aria-expanded", "false"); - } - }); - } - - // --- booking form --- - var form = document.getElementById("bookForm"); - var success = document.getElementById("bookSuccess"); - if (form && success) { - form.addEventListener("submit", function (e) { - e.preventDefault(); - if (!form.checkValidity()) { - form.reportValidity(); - return; - } - var name = (form.elements.name && form.elements.name.value || "").trim(); - success.hidden = false; - success.textContent = (name ? "Thanks, " + name + "! " : "Got it — thanks! ") + - "We'll email you within a day to lock in the back room. 🕹️"; - form.querySelectorAll("input, textarea, button").forEach(function (el) { - if (el.type !== "submit") el.value = el.defaultValue || ""; - }); - success.scrollIntoView({ behavior: "smooth", block: "nearest" }); - }); - } -})(); diff --git a/recipes/band/README.md b/recipes/band/README.md deleted file mode 100644 index d0b7d14..0000000 --- a/recipes/band/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# The Tidal Wolves - -A bold, full-bleed one-page band site for a fictional Portland indie-folk quartet, built as a no-build-step vanilla-JS app (`site/index.html` plus a CSS and JS file). It pairs a moody coastal hero with a working sticky bottom audio player (play/pause, seek, prev/next, click-a-song-to-load), a clickable tracklist, a tour-date list with sold-out and "few left" states, a merch shelf, an "About" lineup, a booking/press block, and a mailing-list signup that validates input and remembers returning visitors via localStorage. `prompt.md` is the copy-paste prompt that recreates the site from a few answers and publishes it live to Spacefast. diff --git a/recipes/band/meta.json b/recipes/band/meta.json deleted file mode 100644 index eabeb96..0000000 --- a/recipes/band/meta.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "The Tidal Wolves", - "title": "Band site", - "slug": "band", - "vertical": "Personal & creative", - "tech": "Vanilla JS", - "summary": "A bold one-page band site with a working sticky audio player, tour dates, a merch shelf, and a mailing-list signup.", - "order": 3, - "live_url": "https://band.view.fast/", - "setup_questions": [ - "What's your band name and genre, and the one-line vibe you want the hero to say?", - "What's your latest single, and where can people stream it?", - "List three to five upcoming shows (city, venue, date) — or say you have none yet.", - "Give two or three merch items with prices, plus three song titles." - ], - "photo_terms": [ - "indie folk band performing on stage moody lighting", - "four piece band group portrait outdoors film grain", - "live concert crowd silhouette warm stage lights", - "vinyl record and band tee flat lay merch" - ], - "sections": [ - "Full-bleed hero with band photo, logo, and latest-single tagline", - "Sticky bottom audio player streaming the new single", - "Music / discography with embedded streaming links (Spotify/Apple/Bandcamp)", - "Tour dates list with city, venue, date, and ticket buttons (with sold-out states)", - "Merch shelf (vinyl, tee, tote) with price and buy links", - "Mailing-list signup", - "Band bio / about", - "Booking & press contact with social links" - ], - "based_on": "A Portland indie-folk quartet in the vein of Sons of the East / DeVotchKa — one-page, image-forward band sites with a sticky audio player, tour dates, and a merch shelf (Bandzoogle/Wix band-site pattern)." -} diff --git a/recipes/band/prompt.md b/recipes/band/prompt.md deleted file mode 100644 index 9b3765f..0000000 --- a/recipes/band/prompt.md +++ /dev/null @@ -1,62 +0,0 @@ -Build me a **bold, full-bleed one-page website for my band** as a vanilla-JS app -(a single `index.html` plus a small CSS and JS file — no framework, no build step). - -**Before you build, ask me these questions in one message and wait for my answers. If I skip anything, choose a sensible default and tell me what you chose:** - -1. What's your band name and genre, and the one-line vibe you want the hero to say? -2. What's your latest single or release, and where can people stream it (Spotify/Apple/Bandcamp links)? -3. List 3–5 upcoming shows (city, venue, date) — or say you have none yet and we'll show a "tour TBA" state. -4. Give me 2–3 merch items with names and prices, plus 3 song titles for the music section. - -**Then build a complete, polished, responsive site with:** - -- A full-bleed hero with the band photo, the band name big, a logo mark, and the latest-single tagline. -- A **sticky bottom audio player** that streams the new single — play/pause, a seek bar with elapsed/total time, and previous/next track buttons. Clicking any song in the music list loads it into the player. -- A "Music" section: the songs as a clickable tracklist, plus links out to Spotify / Apple Music / Bandcamp / YouTube. -- A "Tour" section: each show with city, venue, date, and a ticket button — including "Sold out" and "Few left" states. -- A merch shelf (vinyl, tee, tote) with photo, description, price, and a buy button. -- A mailing-list signup (first name + email) that validates and shows a friendly "You're on the list" confirmation, and remembers the visitor on return via localStorage. -- A short "About" blurb in the band's voice with the member lineup. -- A "Booking & press" contact block with email addresses and social links. - -**Design & content notes:** - -- Moody, coastal, gig-poster mood: a near-black base, a deep teal, and one warm amber accent; a big serif display face (e.g. Fraunces) for headings and a clean sans (e.g. Inter) for body. -- Use realistic content based on my answers — never "lorem ipsum". Invent believable show dates, song descriptions, and bios if I leave gaps. -- Use real images. Pull free photos that fit, e.g. - `https://picsum.photos/seed/indie-band-1/1200/800` (vary the seed) or Pexels, and add - descriptive `alt` text. -- No build step — a single `index.html` with a linked CSS and JS file that opens straight in a browser. Make the player actually work (real `