@thorprovider/storefront 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +333 -0
  3. package/assets/templates/app/app/[[...slug]]/page.tsx +17 -0
  4. package/assets/templates/app/app/[[...slug]]/renderer.tsx +10 -0
  5. package/assets/templates/app/app/globals.css +101 -0
  6. package/assets/templates/app/app/layout.tsx +35 -0
  7. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +88 -0
  8. package/assets/templates/app/lib/__STOREFRONT__/handlers.ts +42 -0
  9. package/assets/templates/app/lib/__STOREFRONT__/mappers.ts +47 -0
  10. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +98 -0
  11. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +17 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +48 -0
  13. package/assets/templates/app/lib/__STOREFRONT__/spec/page-root.ts +26 -0
  14. package/assets/templates/app/lib/__STOREFRONT__/spec/types.ts +14 -0
  15. package/assets/templates/app/lib/__STOREFRONT__/state.ts +35 -0
  16. package/assets/templates/app/lib/__STOREFRONT__/views/index.ts +7 -0
  17. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.tsx +29 -0
  18. package/assets/templates/app/lib/__STOREFRONT__/views/sections/logoCloud.tsx +29 -0
  19. package/assets/templates/app/lib/__STOREFRONT__/views/sections/pricing.tsx +33 -0
  20. package/assets/templates/app/lib/__STOREFRONT__/views/sections/statistics.tsx +29 -0
  21. package/assets/templates/app/lib/__STOREFRONT__/views/sections/testimonials.tsx +28 -0
  22. package/assets/templates/app/lib/__STOREFRONT__/views/types.ts +46 -0
  23. package/assets/templates/views/home/index.tsx +142 -0
  24. package/assets/templates/views/home/registry.tsx +65 -0
  25. package/bin/storefront.js +531 -0
  26. package/commands/sf-add-view.md +26 -0
  27. package/commands/sf-theme.md +20 -0
  28. package/commands/sf-view.md +25 -0
  29. package/package.json +48 -0
  30. package/recipes/archetype.schema.json +39 -0
  31. package/recipes/archetypes.json +148 -0
  32. package/recipes/recipe.schema.json +59 -0
  33. package/recipes/recipes.json +90 -0
  34. package/recipes/sections.json +46 -0
  35. package/recipes/validate.mjs +190 -0
  36. package/scripts/render-templates.mjs +255 -0
  37. package/scripts/sync-assets.mjs +87 -0
  38. package/skills/building-storefronts/SKILL.md +178 -0
  39. package/skills/building-storefronts/references/frontend-integration.md +229 -0
  40. package/skills/json-render-core/SKILL.md +291 -0
  41. package/skills/json-render-next/SKILL.md +194 -0
  42. package/skills/json-render-react/SKILL.md +298 -0
  43. package/skills/json-render-remotion/SKILL.md +111 -0
  44. package/skills/json-render-shadcn/SKILL.md +159 -0
  45. package/skills/json-render-solid/SKILL.md +204 -0
  46. package/skills/nextjs-shadcn/SKILL.md +367 -0
  47. package/skills/nextjs-shadcn/references/architecture.md +499 -0
  48. package/skills/nextjs-shadcn/references/project-setup.md +162 -0
  49. package/skills/nextjs-shadcn/references/shadcn-platform.md +261 -0
  50. package/skills/nextjs-shadcn/references/sidebar.md +274 -0
  51. package/skills/nextjs-shadcn/references/styling.md +555 -0
  52. package/skills/sf-theme-gen/SKILL.md +61 -0
  53. package/skills/sf-view-gen/SKILL.md +116 -0
  54. package/skills/sf-view-gen/assets/recipes/archetypes.json +148 -0
  55. package/skills/sf-view-gen/assets/recipes/recipes.json +90 -0
  56. package/skills/sf-view-gen/assets/recipes/sections.json +46 -0
  57. package/skills/sf-view-gen/assets/templates/views/category/index.tsx +86 -0
  58. package/skills/sf-view-gen/assets/templates/views/category/registry.tsx +48 -0
  59. package/skills/sf-view-gen/assets/templates/views/checkout/index.tsx +192 -0
  60. package/skills/sf-view-gen/assets/templates/views/checkout/registry.tsx +45 -0
  61. package/skills/sf-view-gen/assets/templates/views/product/index.tsx +122 -0
  62. package/skills/sf-view-gen/assets/templates/views/product/registry.tsx +49 -0
  63. package/skills/sf-view-gen/assets/templates/views/sections/faq.client.tsx +20 -0
  64. package/skills/sf-view-gen/assets/templates/views/sections/faq.tsx +21 -0
  65. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.client.tsx +18 -0
  66. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.tsx +21 -0
  67. package/skills/sf-view-gen/assets/templates/views/sections/pricing.client.tsx +20 -0
  68. package/skills/sf-view-gen/assets/templates/views/sections/pricing.tsx +23 -0
  69. package/skills/sf-view-gen/assets/templates/views/sections/statistics.client.tsx +18 -0
  70. package/skills/sf-view-gen/assets/templates/views/sections/statistics.tsx +21 -0
  71. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.client.tsx +19 -0
  72. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.tsx +19 -0
  73. package/skills/sf-view-gen/assets/templates/views/user-panel/index.tsx +168 -0
  74. package/skills/sf-view-gen/assets/templates/views/user-panel/registry.tsx +49 -0
  75. package/skills/sf-view-gen/references/views.md +69 -0
  76. package/skills/sf-view-gen/scripts/validate.mjs +190 -0
  77. package/skills/shadcn-component-discovery/SKILL.md +273 -0
  78. package/skills/shadcn-component-discovery/references/registries.md +226 -0
  79. package/skills/shadcn-theming/SKILL.md +104 -0
  80. package/skills/shadcn-theming/references/templates/theme-setup.md +109 -0
  81. package/skills/shadcn-theming/references/theming-guide.md +90 -0
  82. package/skills/storefront-best-practices/SKILL.md +421 -0
  83. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +123 -0
  84. package/skills/storefront-best-practices/reference/components/cart-popup.md +189 -0
  85. package/skills/storefront-best-practices/reference/components/country-selector.md +298 -0
  86. package/skills/storefront-best-practices/reference/components/footer.md +112 -0
  87. package/skills/storefront-best-practices/reference/components/hero.md +241 -0
  88. package/skills/storefront-best-practices/reference/components/megamenu.md +239 -0
  89. package/skills/storefront-best-practices/reference/components/navbar.md +397 -0
  90. package/skills/storefront-best-practices/reference/components/popups.md +221 -0
  91. package/skills/storefront-best-practices/reference/components/product-card.md +125 -0
  92. package/skills/storefront-best-practices/reference/components/product-reviews.md +217 -0
  93. package/skills/storefront-best-practices/reference/components/product-slider.md +174 -0
  94. package/skills/storefront-best-practices/reference/components/search.md +101 -0
  95. package/skills/storefront-best-practices/reference/connecting-to-backend.md +391 -0
  96. package/skills/storefront-best-practices/reference/design.md +388 -0
  97. package/skills/storefront-best-practices/reference/features/promotions.md +307 -0
  98. package/skills/storefront-best-practices/reference/features/wishlist.md +230 -0
  99. package/skills/storefront-best-practices/reference/layouts/account.md +380 -0
  100. package/skills/storefront-best-practices/reference/layouts/cart.md +316 -0
  101. package/skills/storefront-best-practices/reference/layouts/checkout.md +486 -0
  102. package/skills/storefront-best-practices/reference/layouts/home-page.md +264 -0
  103. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +231 -0
  104. package/skills/storefront-best-practices/reference/layouts/product-details.md +527 -0
  105. package/skills/storefront-best-practices/reference/layouts/product-listing.md +520 -0
  106. package/skills/storefront-best-practices/reference/layouts/static-pages.md +356 -0
  107. package/skills/storefront-best-practices/reference/medusa.md +307 -0
  108. package/skills/storefront-best-practices/reference/mobile-responsiveness.md +183 -0
  109. package/skills/storefront-best-practices/reference/seo.md +195 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ - **Renamed package**: `@thorprovider/create-storefront` → `@thorprovider/storefront`;
6
+ the `bin` is now `storefront` (`npx @thorprovider/storefront create`).
7
+ - **Single scaffold command**: `storefront create <dir>` scaffolds the storefront and
8
+ installs the OpenCode command suite in the same run. The standalone `install` action
9
+ is gone; `storefront update` refreshes, prunes removed files, and migrates the
10
+ version record.
11
+ - **Interactive wizard**: on a TTY, `create` prompts for name, site name, backend URL,
12
+ and preset (Maia preselected). Brand color is decided via `/sf-theme`, not the wizard.
13
+ Without a TTY, every required input must be passed as a flag or the command fails
14
+ fast before writing anything.
15
+ - **Command surface**: `<dir>` is positional; `--dir` is removed; `--name` is a flag or
16
+ wizard prompt. Commands are `storefront create|update|doctor <dir>`.
17
+ - **Removed `/sf-init`**: the `sf-init` command and `sf-scaffold` skill are gone.
18
+ Scaffolding is fully deterministic inside the binary.
19
+ - **Decoupled templates**: the scaffold templates moved from the `sf-scaffold` skill
20
+ into `assets/templates/` at the package root.
21
+ - **Version record**: `.opencode/storefront.json` (replacing `.create-storefront.json`);
22
+ `update` migrates existing installs.
23
+
24
+ ## 0.3.0
25
+
26
+ - **Embedded assets**: each skill now carries the templates, recipes, and scripts it
27
+ uses inside its own directory (`assets/`, `scripts/`, `references/`) and addresses
28
+ them by relative path. No path marker is needed to locate the package.
29
+ - **Versioned install command**: `bin` exposes `create-storefront` with `install`,
30
+ `update`, and `doctor`. Installation is an explicit command; there is no
31
+ `postinstall`. The installer records `{ version, installedAt }` in
32
+ `.opencode/.create-storefront.json` (replacing the `.create-storefront-root` marker).
33
+ - **Single source, generated copies**: `recipes/` stays the single source at the
34
+ package root; `scripts/sync-assets.mjs` generates the per-skill copies and a
35
+ `--check` mode fails on drift. The check runs at build/pretest and before the
36
+ installer copies anything into a target project.
37
+ - **Positive source-of-truth rule**: the skills state that the workflow uses the assets
38
+ inside the skill and that an undecided choice is resolved by asking the user.
39
+ - **Named interfaces**: each skill declares the target-project inputs it reads and the
40
+ outputs it produces.
41
+ - **General self-containment check**: the test fails on any absolute machine path in a
42
+ shipped artifact, independent of any name.
43
+
44
+ ## 0.2.0
45
+
46
+ - Per-view templates: every recipe view (`home`, `product`, `category`, `checkout`,
47
+ `user-panel`) ships as a self-contained module under `templates/views/<view>/`.
48
+ - Generated storefronts now compose views through a barrel (`lib/<name>/views/index.ts`);
49
+ shared files (`catalog.ts`, `registry.tsx`, `runtime.ts`, `spec/index.ts`) derive from
50
+ the registered views and are never edited per view.
51
+ - `/sf-add-view` copies a view module and registers one barrel entry, substituting only
52
+ the imported block identifier for the resolved variant. No hand-authored wiring.
53
+ - Optional sections (testimonials, FAQ, statistics, logo cloud, pricing) ship as
54
+ fragments under `views/sections/` and are validated against `recipes/sections.json`.
55
+ - `sf-scaffold` copies `templates/app/` plus `templates/views/home/` and registers
56
+ `home` in the barrel.
57
+ - Templates and skills no longer reference any repository-local application.
58
+
59
+ ## 0.1.3
60
+
61
+ - Previous release.
package/README.md ADDED
@@ -0,0 +1,333 @@
1
+ ---
2
+ title: "@thorprovider/storefront"
3
+ purpose: "Deterministic storefront scaffolder and installable command suite for Thor Commerce"
4
+ lastReviewed: "2026-10-01"
5
+ ---
6
+
7
+ # @thorprovider/storefront
8
+
9
+ Deterministic storefront scaffolder and installable command suite for Thor Commerce.
10
+
11
+ `npx @thorprovider/storefront create <dir>` scaffolds a complete, buildable storefront
12
+ and installs the `/sf-*` OpenCode command suite into `<dir>/.opencode/` in the same
13
+ run. It ships static project templates, a recipe registry, and vendored skills, and
14
+ installs them into any project's `.opencode/` directory. Nothing else from the Thor
15
+ Commerce monorepo is required at generation time.
16
+
17
+ This README is organised in two parts:
18
+
19
+ - **Getting started** — for consumers who install the published package and want a
20
+ running storefront. Start here.
21
+ - **Contributing** — for maintainers working on the package itself. See
22
+ [Development](#development).
23
+
24
+ ---
25
+
26
+ ## Getting started
27
+
28
+ You need no checkout of the Thor Commerce monorepo. The package is on npm and the
29
+ storefront it generates resolves its own dependencies from the registry.
30
+
31
+ ### Prerequisites
32
+
33
+ | Requirement | Supported |
34
+ | --- | --- |
35
+ | Node.js | >= 18.17.0 |
36
+ | Package manager | Yarn with the `node-modules` linker (for generated projects) |
37
+ | OpenCode | a version that supports project skills in `.opencode/skills/` |
38
+ | OS | macOS, Linux, Windows (via WSL) |
39
+ | Network | access to the npm registry (the generated project installs its dependencies from it) |
40
+
41
+ Generated projects use Yarn with the `node-modules` linker, not Plug'n'Play (PnP).
42
+ Yarn 4 defaults to PnP, which Next.js 16's Turbopack cannot resolve from, so the
43
+ scaffold writes a `.yarnrc.yml` setting `nodeLinker: node-modules` before installing
44
+ dependencies. PnP is not used. You do not need to configure this yourself.
45
+
46
+ ### Install
47
+
48
+ Run the scaffolder directly from npm — no global install required. `npx` and
49
+ `yarn dlx` are equivalent:
50
+
51
+ ```bash
52
+ npx @thorprovider/storefront create my-shop \
53
+ --site-name "My Shop" \
54
+ --backend-url http://localhost:9000 \
55
+ --preset maia
56
+ ```
57
+
58
+ ```bash
59
+ yarn dlx @thorprovider/storefront create my-shop --preset maia
60
+ ```
61
+
62
+ The first positional argument is the target directory: it is created if missing, and
63
+ it is where `.opencode/`, the Next.js app, and `lib/<name>/` live. Run the command
64
+ from the parent directory you want the project created in.
65
+
66
+ In a terminal, `create` runs an interactive wizard for any input you omit (name,
67
+ site name, backend URL, preset — Maia is preselected). Without a terminal, every
68
+ required input must be passed as a flag; the command fails fast and writes nothing
69
+ rather than guessing. Brand color is never asked by the wizard — apply it later with
70
+ `/sf-theme`, or pass `--brand` on the command line.
71
+
72
+ Once the package is installed as a dependency, the shorter binary form is available:
73
+
74
+ ```bash
75
+ storefront create my-shop --preset maia # scaffold + install the suite
76
+ storefront update # refresh an existing install
77
+ storefront doctor # report the recorded version and status
78
+ ```
79
+
80
+ ### Create a storefront
81
+
82
+ `create` scaffolds a complete, buildable storefront in one reproducible pass — no
83
+ LLM re-derivation, no hand-editing. The target directory (positional `<dir>`) is the
84
+ project root that will hold `.opencode/`:
85
+
86
+ ```bash
87
+ storefront create my-shop \
88
+ --name my-shop \
89
+ --site-name "My Shop" \
90
+ --backend-url http://localhost:9000 \
91
+ --preset maia \
92
+ [--brand orange]
93
+ ```
94
+
95
+ | Flag | Meaning | Default |
96
+ | --- | --- | --- |
97
+ | `<dir>` (positional) | target project directory (created if missing) | required |
98
+ | `--name <name>` | kebab-case storefront name = `lib/<name>/` = `__STOREFRONT__` | prompted in the wizard |
99
+ | `--site-name <name>` | human site name (`__SITE_NAME__`) | `<name>` |
100
+ | `--backend-url <url>` | Medusa backend URL | `http://localhost:9000` |
101
+ | `--preset <preset>` | visual preset: `maia`, `nova`, `vega`, `luma`, `lyra`, `mira` | prompted in the wizard (Maia preselected) |
102
+ | `--brand <color>` | named brand color overriding preset primary/nav/ring tokens (never prompted) | none |
103
+ | `--force`, `-f` | overwrite files that already exist | off |
104
+
105
+ The pipeline runs: preconditions → Next.js base → shadcn base with the preset →
106
+ `.yarnrc.yml` (`nodeLinker: node-modules`) → dependency install → template copy +
107
+ placeholder substitution → default-page removal → `.env.local` + theme → suite
108
+ install into `.opencode/`.
109
+
110
+ Same inputs produce a byte-identical tree (verified by recursive `diff -r`). Running
111
+ `create` twice with different inputs is the only reason the output differs.
112
+
113
+ The suite install copies `commands/` and the shipped skills under `skills/` (own
114
+ skills and vendored skills, flat) into `<dir>/.opencode/`. It verifies its own
115
+ generated copies against their source before copying, records the installed version
116
+ in `.opencode/storefront.json`, and is idempotent: use `--force` to overwrite files
117
+ that already exist.
118
+
119
+ ### Run the storefront
120
+
121
+ When `create` finishes it prints the next steps:
122
+
123
+ ```bash
124
+ cd my-shop
125
+ yarn dev
126
+ ```
127
+
128
+ The storefront renders its home view — hero, benefits, and category sections — using
129
+ only `@thorprovider/blocks` for UI and `@thorprovider/adapters` for data. Then grow
130
+ it incrementally from OpenCode:
131
+
132
+ ```
133
+ /sf-add-view product # add a view from its recipe
134
+ /sf-view home add a testimonials section # modify an existing view
135
+ /sf-theme # colors, typography, identity
136
+ ```
137
+
138
+ ### Backend configuration
139
+
140
+ Generated projects expect a Medusa backend. `create` writes a `.env.local` with the
141
+ backend URL you passed and blank placeholders for the rest:
142
+
143
+ ```bash
144
+ MEDUSA_BACKEND_URL=http://localhost:9000
145
+ MEDUSA_REGION_ID=
146
+ MEDUSA_PUBLISHABLE_KEY=
147
+ ```
148
+
149
+ Set `MEDUSA_REGION_ID` and `MEDUSA_PUBLISHABLE_KEY` as needed, then restart
150
+ `yarn dev`. The storefront shows no catalog data until these are set (see
151
+ [Backend not configured](#backend-not-configured)). During local development you can
152
+ use the `mock` adapter data source instead of a live backend.
153
+
154
+ ### Troubleshooting / Known Issues
155
+
156
+ Each entry below follows the same shape: **cause** → **symptom** → **remedy**.
157
+
158
+ #### Ancestor `.opencode/`
159
+
160
+ - **Cause**: the project is created under a directory tree in which an ancestor
161
+ directory already contains a `.opencode/`.
162
+ - **Symptom**: OpenCode resolves the wrong command set — `/sf-add-view`, `/sf-view`,
163
+ and `/sf-theme` run commands from the ancestor's `.opencode/` instead of the ones
164
+ this package installed, so you see stale or unrelated behaviour.
165
+ - **Remedy**: scaffold in a clean root with no ancestor `.opencode/` (for example a
166
+ fresh directory outside the monorepo), or remove/relocate the ancestor `.opencode/`
167
+ before opening the project.
168
+
169
+ #### Non-empty target directory
170
+
171
+ - **Cause**: `create` runs against a directory whose `lib/<name>/` already exists (a
172
+ previous scaffold with the same name), or whose root is already a Next.js project.
173
+ - **Symptom**: the run stops at the preconditions stage with
174
+ `lib/<name>/ already exists — refusing to overwrite`.
175
+ - **Remedy**: choose a different `--name`, or remove the existing `lib/<name>/`
176
+ directory. To extend an existing storefront instead of creating one, use
177
+ `/sf-add-view`. `create-next-app` also refuses to initialize a directory that is
178
+ already a Next.js project unless it can reuse it; run `create` in an empty directory.
179
+
180
+ #### Missing or invalid `--preset`
181
+
182
+ - **Cause**: `--preset` was omitted (in a non-interactive run) or the value is not one
183
+ of the recognized presets.
184
+ - **Symptom**: the run stops at the input-validation stage with
185
+ `--preset must be one of: maia, nova, vega, luma, lyra, mira`.
186
+ - **Remedy**: pass `--preset` with one of `maia`, `nova`, `vega`, `luma`, `lyra`,
187
+ `mira`. In a terminal the wizard preselects `maia`, so the preset is only required
188
+ on the command line for non-interactive runs.
189
+
190
+ #### Yarn Plug'n'Play / linker
191
+
192
+ - **Cause**: a generated project installed with Yarn's default PnP linker.
193
+ - **Symptom**: Next.js 16's Turbopack fails to resolve the Next.js package
194
+ (`Could not find the Next.js package`).
195
+ - **Remedy**: this is why `create` writes `.yarnrc.yml` with
196
+ `nodeLinker: node-modules` before installing. If you set up the project manually,
197
+ add that file and re-run `yarn install` so a real `node_modules/` is materialized.
198
+ Do not use PnP.
199
+
200
+ #### Backend not configured
201
+
202
+ - **Cause**: `MEDUSA_REGION_ID` / `MEDUSA_PUBLISHABLE_KEY` (or a reachable backend) are
203
+ not set in `.env.local`.
204
+ - **Symptom**: the storefront renders its layout but shows no catalog data.
205
+ - **Remedy**: fill in the variables in [Backend configuration](#backend-configuration)
206
+ and restart `yarn dev`, or use the `mock` adapter data source during local
207
+ development.
208
+
209
+ ---
210
+
211
+ ## What gets installed
212
+
213
+ ```
214
+ <project>/.opencode/
215
+ storefront.json # { version, installedAt } — what generation is installed
216
+ commands/
217
+ sf-add-view.md
218
+ sf-view.md
219
+ sf-theme.md
220
+ skills/
221
+ sf-view-gen/ # assets/templates/views/, assets/recipes/, scripts/validate.mjs
222
+ sf-theme-gen/ # assets/
223
+ nextjs-shadcn/
224
+ shadcn-theming/
225
+ shadcn-component-discovery/
226
+ storefront-best-practices/
227
+ building-storefronts/
228
+ ```
229
+
230
+ Each skill carries the templates, recipes, and scripts it uses inside its own
231
+ directory. Skills address them by relative path, so nothing needs the package to be
232
+ located on disk at generation time.
233
+
234
+ ## Commands
235
+
236
+ | Command | Purpose |
237
+ | --- | --- |
238
+ | `/sf-add-view <view>` | Add one view incrementally (`product`, `category`, `checkout`, `user-panel`) |
239
+ | `/sf-view <view> <change>` | Modify an existing view's spec, preserving structure |
240
+ | `/sf-theme` | Customize OKLCH color tokens (light/dark), typography, and identity metadata |
241
+
242
+ Scaffolding itself is `npx @thorprovider/storefront create <dir>` — there is no
243
+ `/sf-init` command; the wizard lives in the CLI and is deterministic.
244
+
245
+ ## Design guarantees
246
+
247
+ - **Self-contained**: each skill carries the assets it uses and addresses them by
248
+ relative path; nothing depends on the Thor Commerce monorepo, on machine-global
249
+ skills, or on locating the package at generation time.
250
+ - **Consistent by construction**: shared recipes are authored once and generated into
251
+ each skill; a check fails when a copy drifts, both at build time and before the
252
+ installer copies anything.
253
+ - **Stack-constrained**: UI comes only from `@thorprovider/blocks`; data only from
254
+ `@thorprovider/adapters` and `@thorprovider/runtime/commerce`.
255
+ - **Varied output**: a recipe registry lets the skills pick block variants and optional
256
+ sections (testimonials, FAQ, statistics, logo cloud, pricing) from your description,
257
+ so sites are not identical.
258
+
259
+ ## Generated project structure
260
+
261
+ Each view is a self-contained module. Shared files compose whatever the view barrel
262
+ exports and never name a view, so adding one is a copy plus a single registration.
263
+
264
+ ```
265
+ lib/<name>/
266
+ catalog.ts, registry.tsx, runtime.ts, handlers.ts, state.ts # shell; compose the barrel
267
+ mappers.ts # CardProduct -> FeaturedProduct / CategoryCard
268
+ spec/index.ts, spec/page-root.ts, spec/types.ts # routes built from the barrel
269
+ views/
270
+ index.ts # barrel: export const views: ViewModule[] = [home, ...]
271
+ types.ts # ViewModule contract
272
+ sections/*.tsx # optional-section fragments (testimonials, faq, ...)
273
+ home/index.tsx # one folder per view
274
+ product/index.tsx
275
+ category/index.tsx
276
+ checkout/index.tsx
277
+ user-panel/index.tsx
278
+ ```
279
+
280
+ A view folder ships the package's default block variants; `/sf-add-view` picks the
281
+ intent/variant from your description (validated by the skill's `scripts/validate.mjs`)
282
+ and substitutes only the imported block identifier. The scaffold templates live at
283
+ the package root, and the view templates travel inside the skills that use them:
284
+
285
+ ```
286
+ assets/templates/app/ # shell (copied by `create`)
287
+ assets/templates/views/home/ # home (copied by `create`)
288
+ skills/sf-view-gen/assets/templates/views/<view>/ # the other views (copied by /sf-add-view)
289
+ ```
290
+
291
+ ## Development
292
+
293
+ Testing the package during development does not require `npm pack`. The local
294
+ binary reads the working tree directly:
295
+
296
+ ```bash
297
+ yarn sync-assets && yarn check-assets # recipes ↔ generated copies are consistent
298
+ yarn test # contract + packaging test suite
299
+ node bin/storefront.js create <tmp> --name my-shop --preset maia # scaffold into a throwaway project
300
+ node bin/storefront.js doctor <tmp> # report the recorded version and status
301
+ node scripts/render-templates.mjs <tmp> my-shop # re-render templates only (no install; fast loop)
302
+ ```
303
+
304
+ `scripts/render-templates.mjs` is the same renderer the `create` action uses for
305
+ stage 6. It copies the templates and substitutes placeholders without touching
306
+ dependencies, so iterating on a template against a persistent project (e.g.
307
+ `projects/<name>/`) costs seconds and no network.
308
+
309
+ `npm pack` exists to verify a different thing: **what actually ships to npm** —
310
+ the `files` list, and that every asset travels inside its skill. It is only
311
+ meaningful at these moments:
312
+
313
+ | Moment | Action |
314
+ | --- | --- |
315
+ | Day-to-day development | Nothing — use the local binary above |
316
+ | Changing `files`, moving assets, adding or removing directories | `npm pack --dry-run` to list what would ship |
317
+ | CI, before publishing | `yarn check-assets` + `npm pack --dry-run` as a guard |
318
+ | Publishing a release | `npm pack` / `yarn npm publish` — the real tarball |
319
+
320
+ A generated `.tgz` goes stale the moment the tree changes, so it is not a
321
+ development tool. Keep it out of the working tree.
322
+
323
+ ## Maintenance
324
+
325
+ See `VENDOR.md` for how vendored skills are refreshed and how to bump
326
+ templates.
327
+
328
+ The [Troubleshooting / Known Issues](#troubleshooting--known-issues) entries are part
329
+ of the contract with consumers: revisit them whenever the CLI surface changes
330
+ (flags, actions, validation) or the scaffold's preconditions change (target-directory
331
+ rules, linker, backend variables), and update the corresponding entry in
332
+ `packages/storefront/README.md`. The packaging test suite asserts these
333
+ sections and that every failure-mode entry carries a remedy.
@@ -0,0 +1,17 @@
1
+ // Generated by Thor Commerce storefront (template: assets/templates/app/app/[[...slug]]/page.tsx)
2
+
3
+ import { notFound } from 'next/navigation'
4
+ import { __STOREFRONT_CAMEL__App } from '@/lib/__STOREFRONT__/runtime'
5
+ import { StorefrontRenderer } from './renderer'
6
+
7
+ export const { generateMetadata, generateStaticParams } = __STOREFRONT_CAMEL__App
8
+
9
+ export default async function Page({
10
+ params,
11
+ }: {
12
+ params: Promise<{ slug?: string[] }>
13
+ }) {
14
+ const data = await __STOREFRONT_CAMEL__App.getPageData({ params })
15
+ if (!data) return notFound()
16
+ return <StorefrontRenderer {...data} />
17
+ }
@@ -0,0 +1,10 @@
1
+ 'use client'
2
+
3
+ // Generated by Thor Commerce storefront (template: assets/templates/app/app/[[...slug]]/renderer.tsx)
4
+
5
+ import { PageRenderer } from '@json-render/next'
6
+ import type { PageData } from '@json-render/next'
7
+
8
+ export function StorefrontRenderer(data: PageData) {
9
+ return <PageRenderer {...data} />
10
+ }
@@ -0,0 +1,101 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "@thorprovider/blocks/styles.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ @theme inline {
8
+ --color-background: var(--background);
9
+ --color-foreground: var(--foreground);
10
+ --color-card: var(--card);
11
+ --color-card-foreground: var(--card-foreground);
12
+ --color-popover: var(--popover);
13
+ --color-popover-foreground: var(--popover-foreground);
14
+ --color-primary: var(--primary);
15
+ --color-primary-foreground: var(--primary-foreground);
16
+ --color-secondary: var(--secondary);
17
+ --color-secondary-foreground: var(--secondary-foreground);
18
+ --color-muted: var(--muted);
19
+ --color-muted-foreground: var(--muted-foreground);
20
+ --color-accent: var(--accent);
21
+ --color-accent-foreground: var(--accent-foreground);
22
+ --color-destructive: var(--destructive);
23
+ --color-destructive-foreground: var(--destructive-foreground);
24
+ --color-success: var(--success);
25
+ --color-success-foreground: var(--success-foreground);
26
+ --color-warning: var(--warning);
27
+ --color-warning-foreground: var(--warning-foreground);
28
+ --color-border: var(--border);
29
+ --color-input: var(--input);
30
+ --color-ring: var(--ring);
31
+ --radius-sm: var(--radius-sm);
32
+ --radius-md: var(--radius-md);
33
+ --radius-lg: var(--radius-lg);
34
+ --radius-xl: var(--radius-xl);
35
+ --font-sans: var(--font-geist-sans);
36
+ }
37
+
38
+ :root {
39
+ --background: oklch(0.98 0.002 90);
40
+ --foreground: oklch(0.15 0 0);
41
+ --card: oklch(1 0 0);
42
+ --card-foreground: oklch(0.15 0 0);
43
+ --popover: oklch(1 0 0);
44
+ --popover-foreground: oklch(0.15 0 0);
45
+ --primary: oklch(0.15 0 0);
46
+ --primary-foreground: oklch(0.98 0.002 90);
47
+ --secondary: oklch(0.96 0.003 90);
48
+ --secondary-foreground: oklch(0.15 0 0);
49
+ --muted: oklch(0.94 0.003 90);
50
+ --muted-foreground: oklch(0.45 0 0);
51
+ --accent: oklch(0.58 0.16 25);
52
+ --accent-foreground: oklch(1 0 0);
53
+ --destructive: oklch(0.55 0.2 25);
54
+ --destructive-foreground: oklch(0.98 0.002 90);
55
+ --success: oklch(0.47 0.14 155);
56
+ --success-foreground: oklch(0.98 0.01 155);
57
+ --warning: oklch(0.55 0.18 75);
58
+ --warning-foreground: oklch(0.98 0.02 75);
59
+ --border: oklch(0.88 0.003 90);
60
+ --input: oklch(0.92 0.003 90);
61
+ --ring: oklch(0.15 0 0);
62
+ --radius-sm: 0.25rem;
63
+ --radius-md: 0.375rem;
64
+ --radius-lg: 0.5rem;
65
+ --radius-xl: 0.75rem;
66
+ }
67
+
68
+ .dark {
69
+ --background: oklch(0.15 0 0);
70
+ --foreground: oklch(0.98 0.002 90);
71
+ --card: oklch(0.2 0 0);
72
+ --card-foreground: oklch(0.98 0.002 90);
73
+ --popover: oklch(0.2 0 0);
74
+ --popover-foreground: oklch(0.98 0.002 90);
75
+ --primary: oklch(0.98 0.002 90);
76
+ --primary-foreground: oklch(0.15 0 0);
77
+ --secondary: oklch(0.25 0.003 90);
78
+ --secondary-foreground: oklch(0.98 0.002 90);
79
+ --muted: oklch(0.25 0.003 90);
80
+ --muted-foreground: oklch(0.65 0 0);
81
+ --accent: oklch(0.65 0.15 25);
82
+ --accent-foreground: oklch(0.15 0 0);
83
+ --destructive: oklch(0.65 0.2 25);
84
+ --destructive-foreground: oklch(0.15 0 0);
85
+ --success: oklch(0.6 0.13 155);
86
+ --success-foreground: oklch(0.1 0.02 155);
87
+ --warning: oklch(0.7 0.15 75);
88
+ --warning-foreground: oklch(0.1 0.02 75);
89
+ --border: oklch(0.3 0.003 90);
90
+ --input: oklch(0.3 0.003 90);
91
+ --ring: oklch(0.85 0 0);
92
+ }
93
+
94
+ @layer base {
95
+ * {
96
+ @apply border-border;
97
+ }
98
+ body {
99
+ @apply bg-background text-foreground;
100
+ }
101
+ }
@@ -0,0 +1,35 @@
1
+ // Generated by Thor Commerce storefront (template: assets/templates/app/app/layout.tsx)
2
+
3
+ import type { Metadata } from 'next'
4
+ import { Geist } from 'next/font/google'
5
+ import { StorefrontProvider } from '@thorprovider/runtime/host-glue/providers'
6
+ import { themeVarsToCss, themeTokensToVars } from '@thorprovider/runtime/render-core/theme'
7
+ import { DEFAULT_THEME_TOKENS } from '@thorprovider/runtime/contracts'
8
+ import { registry } from '@/lib/__STOREFRONT__/registry'
9
+ import './globals.css'
10
+
11
+ const geist = Geist({
12
+ variable: '--font-geist-sans',
13
+ subsets: ['latin'],
14
+ })
15
+
16
+ const themeCss = themeVarsToCss(themeTokensToVars(DEFAULT_THEME_TOKENS))
17
+
18
+ export const metadata: Metadata = {
19
+ title: '__SITE_NAME__',
20
+ description: 'Tienda online de __SITE_NAME__.',
21
+ }
22
+
23
+ export default function RootLayout({
24
+ children,
25
+ }: Readonly<{ children: React.ReactNode }>) {
26
+ return (
27
+ <html lang="es" className={geist.variable}>
28
+ <body className="font-sans antialiased">
29
+ <StorefrontProvider themeCss={themeCss} registry={registry}>
30
+ {children}
31
+ </StorefrontProvider>
32
+ </body>
33
+ </html>
34
+ )
35
+ }
@@ -0,0 +1,88 @@
1
+ // Generated by Thor Commerce storefront (template: assets/templates/app/lib/__STOREFRONT__/catalog.ts)
2
+ // Base catalog for the app shell plus every registered view's catalog fragment.
3
+ // View element types live with their view module; the shell owns only chrome.
4
+
5
+ import { defineCatalog } from '@json-render/core'
6
+ import { schema } from '@json-render/react/schema'
7
+ import { z } from 'zod'
8
+ import { views } from './views'
9
+
10
+ const ctaSchema = z.object({
11
+ label: z.string(),
12
+ href: z.string().optional(),
13
+ })
14
+
15
+ const navLinkSchema = z.object({
16
+ label: z.string(),
17
+ href: z.string().optional(),
18
+ })
19
+
20
+ const footerLinkSchema = z.object({
21
+ label: z.string(),
22
+ href: z.string().optional(),
23
+ })
24
+
25
+ const footerLinkColumnSchema = z.object({
26
+ title: z.string(),
27
+ links: z.array(footerLinkSchema),
28
+ })
29
+
30
+ // Declared locally so the catalog builder's zod version is the only one in play.
31
+ // Cross-major schema assignment (@thorprovider/blocks ships zod v3) breaks the
32
+ // defineCatalog contract, so the block contract is mirrored here instead.
33
+ const productSchema = z.object({
34
+ id: z.union([z.string(), z.number()]),
35
+ title: z.string(),
36
+ price: z.number(),
37
+ originalPrice: z.number().optional(),
38
+ image: z.string().optional(),
39
+ badge: z.string().optional(),
40
+ })
41
+
42
+ const baseComponents = {
43
+ Layout: {
44
+ props: z.object({}),
45
+ slots: ['default', 'header', 'footer'],
46
+ description: 'Page layout shell. Pages render inside its default slot.',
47
+ },
48
+ Navbar: {
49
+ props: z.object({
50
+ navLinks: z.array(navLinkSchema),
51
+ logoText: z.string(),
52
+ logoHref: z.string().optional(),
53
+ currentPath: z.string().optional(),
54
+ signIn: ctaSchema.optional(),
55
+ cta: ctaSchema.optional(),
56
+ cartCount: z.number().optional(),
57
+ }),
58
+ description: 'Top navigation bar.',
59
+ },
60
+ ProductCard: {
61
+ props: z.object({
62
+ products: z.array(productSchema),
63
+ heading: z.string().optional(),
64
+ maxItems: z.number().optional(),
65
+ }),
66
+ description: 'Grid of product cards.',
67
+ },
68
+ Footer: {
69
+ props: z.object({
70
+ brandName: z.string(),
71
+ brandDescription: z.string().optional(),
72
+ linkColumns: z.array(footerLinkColumnSchema),
73
+ newsletter: z
74
+ .object({ placeholder: z.string().optional(), buttonLabel: z.string().optional() })
75
+ .optional(),
76
+ }),
77
+ description: 'Site footer.',
78
+ },
79
+ }
80
+
81
+ const viewComponents = Object.assign({}, ...views.map((view) => view.catalog))
82
+
83
+ export const catalog = defineCatalog(schema, {
84
+ components: { ...baseComponents, ...viewComponents },
85
+ actions: {},
86
+ })
87
+
88
+ export type StorefrontCatalog = typeof catalog