@thorprovider/storefront 0.4.0-mvp.0 → 0.4.0-mvp.2

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 (166) hide show
  1. package/README.md +173 -153
  2. package/VENDOR.md +69 -0
  3. package/assets/openspec/config.yaml +38 -0
  4. package/assets/templates/AGENTS.md +76 -0
  5. package/assets/templates/app/app/[[...slug]]/page.tsx +42 -1
  6. package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
  7. package/assets/templates/app/app/globals.css +5 -8
  8. package/assets/templates/app/app/layout.tsx +1 -5
  9. package/assets/templates/app/app/robots.ts +9 -3
  10. package/assets/templates/app/app/sitemap.ts +64 -5
  11. package/assets/templates/app/lib/__STOREFRONT__/actions.ts +70 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +15 -5
  13. package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +71 -0
  14. package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
  15. package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +36 -4
  16. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +82 -50
  17. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +3 -0
  18. package/assets/templates/app/lib/__STOREFRONT__/schema.ts +29 -14
  19. package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
  20. package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
  21. package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +31 -7
  22. package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +4 -0
  23. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +56 -26
  24. package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +165 -0
  25. package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +26 -7
  26. package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +2 -1
  27. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
  28. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
  29. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
  30. package/assets/templates/features/checkout/handlers.ts +6 -0
  31. package/assets/templates/features/customer-auth/state.ts +10 -1
  32. package/assets/templates/views/account/index.tsx +1 -1
  33. package/assets/templates/views/account/registry.tsx +80 -19
  34. package/assets/templates/views/account/schema.ts +1 -0
  35. package/assets/templates/views/cart/content.ts +11 -0
  36. package/assets/templates/views/cart/data.ts +23 -0
  37. package/assets/templates/views/cart/index.tsx +53 -0
  38. package/assets/templates/views/cart/props.ts +12 -0
  39. package/assets/templates/views/cart/registry.tsx +81 -0
  40. package/assets/templates/views/cart/schema.ts +52 -0
  41. package/assets/templates/views/category/assembly/assembly.json +1 -0
  42. package/assets/templates/views/category/assembly/index.skeleton.tsx +64 -6
  43. package/assets/templates/views/category/assembly/registry.skeleton.tsx +2 -1
  44. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
  45. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
  46. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
  47. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
  48. package/assets/templates/views/category/index.tsx +76 -7
  49. package/assets/templates/views/category/props.ts +7 -0
  50. package/assets/templates/views/category/registry.tsx +19 -0
  51. package/assets/templates/views/category/schema.ts +13 -0
  52. package/assets/templates/views/home/assembly/assembly.json +9 -0
  53. package/assets/templates/views/home/assembly/index.skeleton.tsx +22 -4
  54. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +1 -1
  55. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +2 -2
  56. package/assets/templates/views/home/data.ts +9 -0
  57. package/assets/templates/views/home/index.tsx +23 -5
  58. package/assets/templates/views/home/registry.tsx +2 -2
  59. package/assets/templates/views/product/data.ts +4 -2
  60. package/assets/templates/views/product/index.tsx +49 -5
  61. package/assets/templates/views/product/props.ts +7 -0
  62. package/assets/templates/views/product/registry.tsx +25 -0
  63. package/assets/templates/views/product/schema.ts +13 -0
  64. package/assets/templates/views/static/content.ts +4 -4
  65. package/assets/templates/views/static/index.tsx +1 -1
  66. package/bin/storefront.js +1231 -154
  67. package/examples/merchandising/category-led.json +37 -0
  68. package/examples/merchandising/collection-led.json +37 -0
  69. package/package.json +11 -7
  70. package/recipes/archetypes.json +9 -22
  71. package/recipes/catalog.json +40 -1
  72. package/recipes/features.json +25 -2
  73. package/recipes/recipe.schema.json +1 -1
  74. package/recipes/recipes.json +32 -30
  75. package/recipes/sections.json +10 -0
  76. package/recipes/validate.mjs +54 -0
  77. package/scripts/candidate.mjs +4 -4
  78. package/scripts/composers.mjs +25 -5
  79. package/scripts/env-file.mjs +86 -0
  80. package/scripts/file-classes.mjs +5 -0
  81. package/scripts/migrate-english-routes.mjs +276 -0
  82. package/scripts/pinning.mjs +74 -5
  83. package/scripts/plan-apply.mjs +149 -5
  84. package/scripts/reconcile.mjs +49 -11
  85. package/scripts/render-templates.mjs +97 -44
  86. package/scripts/section-config.mjs +253 -0
  87. package/scripts/starter.mjs +17 -0
  88. package/scripts/theme/color.mjs +185 -0
  89. package/scripts/theme/css-theme.mjs +233 -0
  90. package/scripts/theme/fonts.mjs +123 -0
  91. package/scripts/theme/import-export.mjs +125 -0
  92. package/scripts/theme/preset.mjs +93 -0
  93. package/scripts/theme/theme-plan.mjs +292 -0
  94. package/scripts/theme/tokens.mjs +137 -0
  95. package/scripts/view-assembly.mjs +47 -1
  96. package/skills/sf-feature/SKILL.md +32 -19
  97. package/skills/sf-setup/SKILL.md +79 -0
  98. package/skills/sf-theme-gen/SKILL.md +99 -36
  99. package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
  100. package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
  101. package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
  102. package/skills/sf-view-gen/SKILL.md +63 -40
  103. package/skills/sf-view-gen/assets/recipes/archetypes.json +9 -22
  104. package/skills/sf-view-gen/assets/recipes/catalog.json +40 -1
  105. package/skills/sf-view-gen/assets/recipes/features.json +25 -2
  106. package/skills/sf-view-gen/assets/recipes/recipes.json +32 -30
  107. package/skills/sf-view-gen/assets/recipes/sections.json +10 -0
  108. package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
  109. package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
  110. package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
  111. package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
  112. package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
  113. package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
  114. package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
  115. package/skills/sf-view-gen/references/views.md +28 -37
  116. package/skills/sf-view-gen/scripts/validate.mjs +54 -0
  117. package/skills/storefront-workflow/SKILL.md +94 -31
  118. package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
  119. package/CHANGELOG.md +0 -84
  120. package/assets/templates/views/user-panel/index.tsx +0 -106
  121. package/assets/templates/views/user-panel/registry.tsx +0 -54
  122. package/assets/templates/views/user-panel/schema.ts +0 -79
  123. package/commands/sf-add-view.md +0 -57
  124. package/commands/sf-feature.md +0 -28
  125. package/commands/sf-theme.md +0 -26
  126. package/commands/sf-view.md +0 -67
  127. package/skills/building-storefronts/SKILL.md +0 -178
  128. package/skills/building-storefronts/references/frontend-integration.md +0 -229
  129. package/skills/json-render-remotion/SKILL.md +0 -111
  130. package/skills/json-render-solid/SKILL.md +0 -204
  131. package/skills/nextjs-shadcn/SKILL.md +0 -367
  132. package/skills/nextjs-shadcn/references/architecture.md +0 -499
  133. package/skills/nextjs-shadcn/references/project-setup.md +0 -162
  134. package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
  135. package/skills/nextjs-shadcn/references/sidebar.md +0 -274
  136. package/skills/nextjs-shadcn/references/styling.md +0 -555
  137. package/skills/shadcn-component-discovery/SKILL.md +0 -273
  138. package/skills/shadcn-component-discovery/references/registries.md +0 -226
  139. package/skills/shadcn-theming/SKILL.md +0 -104
  140. package/skills/storefront-best-practices/SKILL.md +0 -421
  141. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
  142. package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
  143. package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
  144. package/skills/storefront-best-practices/reference/components/footer.md +0 -112
  145. package/skills/storefront-best-practices/reference/components/hero.md +0 -241
  146. package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
  147. package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
  148. package/skills/storefront-best-practices/reference/components/popups.md +0 -221
  149. package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
  150. package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
  151. package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
  152. package/skills/storefront-best-practices/reference/components/search.md +0 -101
  153. package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
  154. package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
  155. package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
  156. package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
  157. package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
  158. package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
  159. package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
  160. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
  161. package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
  162. package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
  163. package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
  164. package/skills/storefront-best-practices/reference/medusa.md +0 -307
  165. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
  166. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sf-view-gen
3
- description: Add or remove a Thor Commerce storefront view, compose a view's faq/cta sections, or customize a view's user-owned files. Use when the user runs /sf-add-view or /sf-view, or asks to add/remove a view, add/remove/reorder a section, or change a view's content, props, or data. Delegates every structural write to the `storefront` CLI.
3
+ description: Add or remove a Thor Commerce storefront view, compose and reorder a view's executable sections, or customize a view's user-owned files. Use when the user asks to add or remove a view (home, category, product, cart, account, static), add/remove/reorder a section (faq, cta, products), change a variant, or change a view's content, props, or data. Delegates every structural write to the `storefront` CLI.
4
4
  ---
5
5
 
6
6
  # sf-view-gen
@@ -10,51 +10,49 @@ Choose a view's composition **by meaning**, delegate the structural write to the
10
10
  manifest (`storefront.config.json`) is the structural authority; the CLI is the
11
11
  only writer of derived files.
12
12
 
13
- ## Read the registry through the CLI (never from memory)
13
+ ## Read the curated view set through the CLI (never from memory)
14
14
 
15
15
  Run from the storefront project root. The CLI resolves the project from the
16
16
  current directory; do not pass a directory.
17
17
 
18
18
  ```bash
19
- npx @thorprovider/storefront views --json # views, routes, supported intents/variants, installed
19
+ npx @thorprovider/storefront views --json # views, routes, intents/variants, installed
20
20
  npx @thorprovider/storefront recipes --json # whole curated registry (views + features)
21
21
  npx @thorprovider/storefront features --json # feature bundles and what they own
22
22
  ```
23
23
 
24
24
  The `--json` output is the same registry the CLI validates against, so it cannot
25
- advertise a selection the reconciler will not honour. `views --json` and
26
- `recipes --json` also report, per view, `registeredSlots` (the non-removable core
27
- slots), `allowedSections` (the section types the view may host), and
28
- `maxSections` (the editorial-instance bound), plus a top-level `sectionTypes`
29
- list marking each type `executable` or `held` with a reason. Do not read the JSON
30
- files directly; they are an implementation detail.
31
-
32
- ## Curated MVP set
33
-
34
- - **Standalone views:** `home`, `category`, `product` — add with `view add`.
35
- - **Feature-owned view:** `checkout` is owned by the `checkout` feature. A direct
36
- `view add checkout` is rejected; run `feature add checkout` instead (see the
37
- `sf-feature` skill).
38
- - **Held post-MVP:** `user-panel` and recommendations are rejected before any
39
- write. Do not try to install them. (Sections are composable — see below.)
25
+ advertise a selection the reconciler will not honour. There is **no fixed view
26
+ list in this skill**: `views --json` reports the authoritative curated set
27
+ (currently `home`, `product`, `cart`, `category`, `checkout`, `account`, and
28
+ `static`) annotated with `installed` and `supported`, so a view added to or
29
+ removed from the registry is reflected without editing this text.
30
+
31
+ `views --json` and `recipes --json` also report, per view, `registeredSlots` (the
32
+ non-removable core slots), `allowedSections` (the section types the view may
33
+ host), and `maxSections` (the editorial-instance bound), plus a top-level
34
+ `sectionTypes` list marking each type `executable` or `held` with a reason. Do not
35
+ read the JSON files directly; they are an implementation detail.
40
36
 
41
37
  ## Add a view
42
38
 
43
- 1. Read `views --json`. If the view is held or feature-owned, stop and explain
44
- the correct path.
39
+ 1. Read `views --json`. If the view is held (`supported: false`) or is
40
+ feature-owned, stop and explain the correct path (a direct `view add` of a
41
+ feature-owned view is rejected — route it through the owning feature's
42
+ `feature add`; see the `sf-feature` skill).
45
43
  2. Choose an **intent by meaning** for each slot from the view's
46
- `supportedIntents` (e.g. a "dense grid" description → the matching intent).
47
- If the description is silent, use the default. Only use
48
- `--variant slot=Name` when the user names an exact supported variant.
44
+ `supportedIntents` (for example a "dense grid" description → the matching
45
+ intent). If the description is silent, use the default. Use
46
+ `--variant slot=Name` only when the user names an exact supported variant.
49
47
  3. Delegate the write:
50
48
 
51
49
  ```bash
52
50
  npx @thorprovider/storefront view add <name> [--intent slot=intent] [--variant slot=Variant]
53
51
  ```
54
52
 
55
- The CLI validates, updates the manifest, and reconciles the derived files.
56
- If it exits non-zero, show its message and the supported options; do not
57
- hand-edit derived files to work around it.
53
+ The CLI validates, updates the manifest, and reconciles the derived files. If
54
+ it exits non-zero, show its message and the supported options; do not hand-edit
55
+ derived files to work around it.
58
56
  4. Report the created view and its route.
59
57
 
60
58
  ## Remove a view
@@ -75,11 +73,15 @@ natural language and never types a command.
75
73
 
76
74
  1. Read `views --json`. For the target view use `registeredSlots` (the core
77
75
  slots), `allowedSections` (the host allowlist), and `maxSections`; read the
78
- top-level `sectionTypes` list for which types are available.
79
- 2. Only `faq` and `cta` are `executable`. Held types (`testimonials`,
80
- `statistics`, `logoCloud`, `pricing`) are refused with a bounded reason. Never
81
- present a held type as available, and never invent a reason — relay the
82
- `reason` the registry reports.
76
+ top-level `sectionTypes` list for which types are `executable`. Never treat the
77
+ `sections --json` installed-instance list as the set of available types.
78
+ 2. Only `executable` types may be composed; a `held` type is refused with the
79
+ `reason` the registry reports — relay that reason, never invent one. A
80
+ `dataBound` `products` instance is configured separately by the developer in
81
+ `views/<view>/data.ts` → `sectionSources[id]` (a canonical query — not a sales
82
+ ranking); its variant only changes presentation, and until the selection is set
83
+ the instance renders a truthful "needs configuration" state that `doctor`
84
+ reports by file and instance id.
83
85
  3. Compose through the CLI's section operations, carried in the operations
84
86
  envelope `{ "formatVersion": 1, "operations": [ ... ] }`. Write the envelope
85
87
  to a file, plan it, then apply the plan:
@@ -89,14 +91,14 @@ natural language and never types a command.
89
91
  npx @thorprovider/storefront apply --plan plan.json
90
92
  ```
91
93
 
92
- - `{ "op": "section.add", "view": "home", "type": "faq" }` — optional `id`,
94
+ - `{ "op": "section.add", "view": "<view>", "type": "<type>" }` — optional `id`,
93
95
  `variant`, and `at` (0-based insertion index, default append). Omit `id` to
94
96
  auto-mint one; a `variant` must be declared by the section type.
95
- - `{ "op": "section.remove", "view": "home", "id": "home-faq-1" }` — removes an
97
+ - `{ "op": "section.remove", "view": "<view>", "id": "<id>" }` — removes an
96
98
  editorial instance. A registered slot (`slot:<slot>`) cannot be removed.
97
- - `{ "op": "section.reorder", "view": "home", "order": ["slot:hero", "home-faq-1", "slot:benefits", "slot:categories"] }` —
98
- `order` must be a permutation of the view's current entry ids: every current
99
- id exactly once, and every registered slot present.
99
+ - `{ "op": "section.reorder", "view": "<view>", "order": [ ... ] }` — `order`
100
+ must be a permutation of the view's current entry ids: every current id
101
+ exactly once, and every registered slot present.
100
102
 
101
103
  4. `plan` is read-only and validates the whole intended state; `apply --plan` is
102
104
  the only writer. On success the CLI updates the manifest and reconciles derived
@@ -107,7 +109,15 @@ natural language and never types a command.
107
109
  `content.ts`; re-adding the same `id` rebinds it, while an auto-minted id never
108
110
  rebinds preserved content.
109
111
 
110
- ## Customize a view (the `/sf-view` workflow)
112
+ ## Variants
113
+
114
+ A slot's variant is a presentation choice carried by the manifest. Change it
115
+ through the CLI (`view add`/`remove` with `--variant`, or a `section.add` /
116
+ `section.reorder` operation for editorial sections); never hand-edit a derived
117
+ `index.tsx` or registry to swap an import identifier. Variant changes go through
118
+ the CLI plan/apply path so the manifest and the derived files stay in sync.
119
+
120
+ ## Customize a view
111
121
 
112
122
  Only **user-owned** files may be edited by hand. They are consumed by thin
113
123
  derived wrappers, so `sync` regenerates the wrappers around your edits.
@@ -124,7 +134,18 @@ Derived (never edit; the CLI regenerates them): `index.tsx`, `registry.tsx`,
124
134
  the event/prop contracts the derived wrapper imports.
125
135
  3. Regenerate the wrappers: `npx @thorprovider/storefront sync`.
126
136
  4. If the change needs a different block variant or a new view, use the CLI
127
- (`view add`/`remove`); never edit the derived `index.tsx` to swap a variant.
137
+ (`view add`/`remove` or the section operations); never edit a derived file to
138
+ swap a variant.
139
+
140
+ ## References
141
+
142
+ Relative to this `SKILL.md`:
143
+
144
+ - `references/views.md` — view module shape, registering, and data contracts.
145
+ - `references/json-render-core.md` — catalog zod props and spec shape.
146
+ - `references/json-render-react.md` — registry mapping, providers, and actions.
147
+ - `references/json-render-next.md` — the Next rendering shape.
148
+ - `references/json-render-shadcn.md` — standard component definitions.
128
149
 
129
150
  ## Stack constraints
130
151
 
@@ -132,11 +153,13 @@ Derived (never edit; the CLI regenerates them): `index.tsx`, `registry.tsx`,
132
153
  - Data only through `@thorprovider/adapters` + `@thorprovider/runtime/commerce`.
133
154
  - Never `@thorprovider/medusa-extended`.
134
155
  - No custom blocks without explicit user authorization.
156
+ - A missing capability is a gap to report, not a reason to install another UI
157
+ framework or hand-edit a derived file.
135
158
 
136
159
  ## Workflow model
137
160
 
138
- Read `../storefront-workflow/SKILL.md` for the four phases (bootstrap → compose
139
- → customize → verify), the ownership classes, and the CLI/agent authority.
161
+ Read `../storefront-workflow/SKILL.md` for the four phases and the CLI/agent
162
+ authority.
140
163
 
141
164
  ## Completion criteria
142
165
 
@@ -88,6 +88,15 @@
88
88
  }
89
89
  }
90
90
  },
91
+ "cart.view": {
92
+ "default": "review",
93
+ "intents": {
94
+ "review": {
95
+ "variant": "ShoppingCart4",
96
+ "when": "A standalone cart review page: line editing plus backend-authoritative totals before checkout."
97
+ }
98
+ }
99
+ },
91
100
  "category.filter": {
92
101
  "default": "sidebar",
93
102
  "intents": {
@@ -148,28 +157,6 @@
148
157
  "when": "A signed-in shopper's order history with per-order totals and detail on demand."
149
158
  }
150
159
  }
151
- },
152
- "user-panel.shell": {
153
- "default": "standard",
154
- "intents": {
155
- "standard": {
156
- "variant": "AppShell1",
157
- "when": "A standard account shell with side navigation and content area."
158
- }
159
- }
160
- },
161
- "user-panel.orderHistory": {
162
- "default": "compact",
163
- "intents": {
164
- "compact": {
165
- "variant": "OrderHistory1",
166
- "when": "A concise list of past orders with status and totals."
167
- },
168
- "detailed": {
169
- "variant": "OrderHistory2",
170
- "when": "Order history with line-item detail, tracking, and reorder actions."
171
- }
172
- }
173
160
  }
174
161
  }
175
162
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./catalog.schema.json",
3
3
  "catalogVersion": 1,
4
- "description": "Initial semantic executable catalog (E1 tasks 1.2-1.3), classified by source-shape inspection of the @thorprovider/blocks prop contracts and the view templates. Scope: every slot the curated MVP views (home, product, category, checkout, account) render by default, covering both the selectable archetype slots declared by recipes/archetypes.json and the nested default slots that have no archetype (category.productCard, checkout.confirmation) so no legacy default support is dropped. The account view (change: storefront-customer-auth) declares the login/register slots: LoginPage1/SignUpPage1 are executable while LoginPage2 is held for its divergent callback contract. A variant is `executable` only when the slot's canonical data shape can feed its block props without a prop bridge and without inventing data; every other non-default variant is `held` with the specific reason. The one executable non-default swap is home.benefits Feature1 -> Feature2 (identical item contract, no bridge). user-panel (non-MVP view) is outside this initial catalog scope. E2 task 6.1 declares the base listing slot category.filter CategoryFilter2 as the single controlled listing (dataShape `CatalogQuery & CatalogResults`, state controlled) with the search/filter controls contributed by the logical mounts category.search / category.filters (no second block). Source-shape compatibility is not end-to-end backend behavior proof: the render is executable, but the category.search.apply / category.filter.apply actions remain held under the capability catalog (owner: storefront-catalog-search-filters, E2) until a real runtime proof. The `capabilities` map declares those operational capabilities with their requirements and verification state; E1 declares the held category.filter.apply action, E2 adds the held category.search.apply action and the held category.price.range capability that gates the category-filters showPrice option; no capability is claimed verified.",
4
+ "description": "Initial semantic executable catalog (E1 tasks 1.2-1.3), classified by source-shape inspection of the @thorprovider/blocks prop contracts and the view templates. Scope: every slot the curated MVP views (home, product, category, checkout, account) render by default, covering both the selectable archetype slots declared by recipes/archetypes.json and the nested default slots that have no archetype (category.productCard, checkout.confirmation) so no legacy default support is dropped. The account view (change: storefront-customer-auth) declares the login/register slots: LoginPage1/SignUpPage1 are executable while LoginPage2 is held for its divergent callback contract. A variant is `executable` only when the slot's canonical data shape can feed its block props without a prop bridge and without inventing data; every other non-default variant is `held` with the specific reason. The one executable non-default swap is home.benefits Feature1 -> Feature2 (identical item contract, no bridge). E2 task 6.1 declares the base listing slot category.filter CategoryFilter2 as the single controlled listing (dataShape `CatalogQuery & CatalogResults`, state controlled) with the search/filter controls contributed by the logical mounts category.search / category.filters (no second block). Source-shape compatibility is not end-to-end backend behavior proof: the render is executable, but the category.search.apply / category.filter.apply actions remain held under the capability catalog (owner: storefront-catalog-search-filters, E2) until a real runtime proof. The `capabilities` map declares those operational capabilities with their requirements and verification state; E1 declares the held category.filter.apply action, E2 adds the held category.search.apply action and the held category.price.range capability that gates the category-filters showPrice option; no capability is claimed verified.",
5
5
  "slots": {
6
6
  "home.hero": {
7
7
  "default": "StorefrontHero1",
@@ -157,6 +157,32 @@
157
157
  }
158
158
  }
159
159
  },
160
+ "product.breadcrumb": {
161
+ "default": "Breadcrumb",
162
+ "variants": {
163
+ "Breadcrumb": {
164
+ "status": "executable",
165
+ "block": "Breadcrumb",
166
+ "dataShape": "BreadcrumbItemData[]",
167
+ "events": [],
168
+ "state": "uncontrolled",
169
+ "requirements": ["productBreadcrumb"]
170
+ }
171
+ }
172
+ },
173
+ "cart.view": {
174
+ "default": "ShoppingCart4",
175
+ "variants": {
176
+ "ShoppingCart4": {
177
+ "status": "executable",
178
+ "block": "ShoppingCart4",
179
+ "dataShape": "CartLineState[] & CartTotalsState",
180
+ "events": ["refreshCart", "updateQuantity", "removeFromCart", "checkout", "continueShopping"],
181
+ "state": "controlled",
182
+ "requirements": ["cart", "cartTotals"]
183
+ }
184
+ }
185
+ },
160
186
  "category.filter": {
161
187
  "default": "CategoryFilter2",
162
188
  "variants": {
@@ -339,6 +365,19 @@
339
365
  "requirements": ["accountCustomer", "accountAuthenticated"]
340
366
  }
341
367
  }
368
+ },
369
+ "category.breadcrumb": {
370
+ "default": "Breadcrumb",
371
+ "variants": {
372
+ "Breadcrumb": {
373
+ "status": "executable",
374
+ "block": "Breadcrumb",
375
+ "dataShape": "BreadcrumbItemData[]",
376
+ "events": [],
377
+ "state": "uncontrolled",
378
+ "requirements": ["categoryBreadcrumb"]
379
+ }
380
+ }
342
381
  }
343
382
  },
344
383
  "capabilities": {
@@ -30,7 +30,7 @@
30
30
  "name": "customer-auth",
31
31
  "mvp": true,
32
32
  "description": "Customer authentication: registration, login, logout, and a minimal signed-in state. Owns the account view.",
33
- "route": "/cuenta",
33
+ "route": "/account",
34
34
  "views": ["account"],
35
35
  "state": [
36
36
  "accountCustomer",
@@ -48,7 +48,7 @@
48
48
  "name": "static-pages",
49
49
  "mvp": true,
50
50
  "description": "Content-driven static pages (contact, legal, about) served with per-page metadata. Owns the static view.",
51
- "route": "/contacto",
51
+ "route": "/contact",
52
52
  "views": ["static"],
53
53
  "state": [],
54
54
  "handlers": [],
@@ -122,6 +122,29 @@
122
122
  "mounts": [{ "id": "category-filters", "host": "category", "slot": "filters" }],
123
123
  "surfaces": []
124
124
  },
125
+ {
126
+ "name": "navigability",
127
+ "mvp": true,
128
+ "description": "Cross-cutting navigability feature: entity-derived breadcrumbs with BreadcrumbList structured data on the product and category surfaces, plus skip-to-content, current-page indication, back-to-top, and a mobile bottom navigation in the shared shell.",
129
+ "views": [],
130
+ "state": [],
131
+ "handlers": [],
132
+ "adapter": { "needs": [] },
133
+ "config": {
134
+ "type": "object",
135
+ "additionalProperties": false,
136
+ "properties": {
137
+ "presentation": { "type": "string", "enum": ["default"], "default": "default" },
138
+ "showBackToTop": { "type": "boolean", "default": true }
139
+ }
140
+ },
141
+ "presentations": { "default": "Breadcrumb" },
142
+ "mounts": [
143
+ { "id": "category-breadcrumb", "host": "category", "slot": "breadcrumb" },
144
+ { "id": "product-breadcrumb", "host": "product", "slot": "breadcrumb" }
145
+ ],
146
+ "surfaces": []
147
+ },
125
148
  {
126
149
  "name": "recommendations",
127
150
  "mvp": false,
@@ -22,47 +22,69 @@
22
22
  },
23
23
  "optionalSections": ["testimonials", "faq", "statistics", "logoCloud", "pricing"],
24
24
  "registeredSlots": ["hero", "benefits", "categories"],
25
- "allowedSections": ["faq", "cta"],
25
+ "allowedSections": ["faq", "cta", "products"],
26
26
  "maxSections": 4
27
27
  },
28
28
  {
29
29
  "name": "product",
30
30
  "mvp": true,
31
31
  "description": "Single product detail page with overview, gallery, and reviews",
32
- "route": "/productos/[id]",
32
+ "route": "/products/[id]",
33
33
  "loader": "product",
34
34
  "requiredDataKeys": ["selectedProduct"],
35
35
  "defaultVariants": {
36
36
  "overview": "ProductOverview1",
37
- "reviews": "ReviewRating1"
37
+ "reviews": "ReviewRating1",
38
+ "breadcrumb": "Breadcrumb"
38
39
  },
39
40
  "allowedVariants": {
40
41
  "overview": ["ProductOverview1", "ProductOverview2", "ProductOverview5"],
41
42
  "reviews": ["ReviewRating1", "ReviewRating2"],
43
+ "breadcrumb": ["Breadcrumb"],
42
44
  "productCard": ["ProductCard1", "ProductCard2", "ProductCard3", "ProductCard5"]
43
45
  },
44
46
  "optionalSections": ["testimonials", "faq", "statistics"],
45
- "registeredSlots": ["overview", "reviews"],
47
+ "registeredSlots": ["overview", "reviews", "breadcrumb"],
46
48
  "allowedSections": [],
47
49
  "maxSections": 4
48
50
  },
51
+ {
52
+ "name": "cart",
53
+ "mvp": true,
54
+ "description": "Standalone cart review page with authoritative totals and quantity editing",
55
+ "route": "/cart",
56
+ "loader": "cart",
57
+ "requiredDataKeys": ["cart"],
58
+ "defaultVariants": {
59
+ "view": "ShoppingCart4"
60
+ },
61
+ "allowedVariants": {
62
+ "view": ["ShoppingCart4"]
63
+ },
64
+ "optionalSections": [],
65
+ "registeredSlots": ["view"],
66
+ "allowedSections": [],
67
+ "maxSections": 0
68
+ },
49
69
  {
50
70
  "name": "category",
51
71
  "mvp": true,
52
72
  "description": "Product listing filtered by category with filters and sorting",
53
- "route": "/categorias/[[...slug]]",
73
+ "route": "/categories/[[...slug]]",
54
74
  "loader": "category",
55
75
  "requiredDataKeys": ["categoryProducts", "storeCategories"],
56
76
  "defaultVariants": {
57
77
  "filter": "CategoryFilter2",
58
- "productCard": "ProductCard1"
78
+ "productCard": "ProductCard1",
79
+ "breadcrumb": "Breadcrumb"
59
80
  },
60
81
  "allowedVariants": {
61
82
  "filter": ["CategoryFilter1", "CategoryFilter2", "CategoryFilter4"],
62
- "productCard": ["ProductCard1", "ProductCard2", "ProductCard3", "ProductCard5"]
83
+ "productCard": ["ProductCard1", "ProductCard2", "ProductCard3", "ProductCard5"],
84
+ "breadcrumb": ["Breadcrumb"]
63
85
  },
64
86
  "optionalSections": ["logoCloud", "testimonials"],
65
- "registeredSlots": ["filter", "productCard"],
87
+ "registeredSlots": ["breadcrumb", "filter", "productCard"],
66
88
  "allowedSections": ["faq", "cta"],
67
89
  "maxSections": 4
68
90
  },
@@ -90,7 +112,7 @@
90
112
  "name": "account",
91
113
  "mvp": true,
92
114
  "description": "Customer account area with sign-in, registration, and a minimal signed-in state",
93
- "route": "/cuenta",
115
+ "route": "/account",
94
116
  "loader": "account",
95
117
  "requiredDataKeys": ["accountCustomer"],
96
118
  "defaultVariants": {
@@ -111,7 +133,7 @@
111
133
  "name": "static",
112
134
  "mvp": true,
113
135
  "description": "Content-driven static pages (contact, legal, about) with per-page metadata",
114
- "route": "/contacto",
136
+ "route": "/contact",
115
137
  "loader": "static",
116
138
  "requiredDataKeys": [],
117
139
  "defaultVariants": {},
@@ -120,26 +142,6 @@
120
142
  "registeredSlots": [],
121
143
  "allowedSections": [],
122
144
  "maxSections": 0
123
- },
124
- {
125
- "name": "user-panel",
126
- "mvp": false,
127
- "description": "Customer account area with order history and account navigation",
128
- "route": "/cuenta",
129
- "loader": "userPanel",
130
- "requiredDataKeys": ["lastOrder", "lastOrderLines", "orderPlaced"],
131
- "defaultVariants": {
132
- "shell": "AppShell1",
133
- "orderHistory": "OrderHistory1"
134
- },
135
- "allowedVariants": {
136
- "shell": ["AppShell1"],
137
- "orderHistory": ["OrderHistory1", "OrderHistory2"]
138
- },
139
- "optionalSections": [],
140
- "registeredSlots": ["shell", "orderHistory"],
141
- "allowedSections": [],
142
- "maxSections": 4
143
145
  }
144
146
  ]
145
147
  }
@@ -31,6 +31,16 @@
31
31
  "propsHint": "badgeText, title, description, primaryCta/secondaryCta (label, href)",
32
32
  "status": "executable"
33
33
  },
34
+ {
35
+ "name": "products",
36
+ "description": "Data-bound product presentation (grid or combined grid-and-carousel) whose selection is configured independently per instance",
37
+ "elementType": "Products",
38
+ "variants": ["ProductCard1", "ProductShowcase1"],
39
+ "defaultVariant": "ProductCard1",
40
+ "dataBound": true,
41
+ "propsHint": "per-instance selection configured in views/<view>/data.ts sectionSources[id] (canonical category/collection/search/ordering query); heading, description, objective, ctaLabel/ctaHref are editorial content",
42
+ "status": "executable"
43
+ },
34
44
  {
35
45
  "name": "statistics",
36
46
  "description": "Key metrics or trust statistics",
@@ -0,0 +1,100 @@
1
+ 'use client'
2
+
3
+ // Optional section fragment: products (mirrors recipes/sections.json) — client registry.
4
+ // The matching server-side catalog lives in products.tsx. A data-bound section
5
+ // renders one of two verified presentations (ProductCard1 grid, or the combined
6
+ // grid-and-carousel ProductShowcase1) chosen by the resolved `variant`. The
7
+ // selection and resolved products arrive through the view state; this boundary
8
+ // only validates them and wires navigation/purchase callbacks.
9
+
10
+ import {
11
+ ProductCard1 as ProductCard1Block,
12
+ ProductShowcase1 as ProductShowcase1Block,
13
+ type ProductCard1Product,
14
+ } from '@thorprovider/blocks'
15
+ import { useStateStore } from '@json-render/react'
16
+ import { useSectionObservation } from '../../section-observation'
17
+ import { productsPropsSchema, type ProductsStatus } from './schema.products'
18
+ import type { ViewRegistry } from '../types'
19
+
20
+ // Truthful, actionable messages for the non-`ready` states. A failed or
21
+ // unconfigured section never fabricates products.
22
+ function sectionStatusMessage(status: ProductsStatus): string {
23
+ switch (status) {
24
+ case 'unconfigured':
25
+ return 'Esta sección de productos aún no tiene una selección configurada.'
26
+ case 'empty':
27
+ return 'Esta selección no tiene productos disponibles por ahora.'
28
+ case 'invalid-query':
29
+ return 'La selección de productos de esta sección no es válida.'
30
+ case 'unsupported-query':
31
+ return 'Esta selección usa criterios que el catálogo no admite.'
32
+ case 'unmounted-capability':
33
+ return 'Esta selección requiere una capacidad que este catálogo no tiene montada.'
34
+ case 'data-error':
35
+ return 'No se pudieron cargar los productos de esta sección.'
36
+ default:
37
+ return ''
38
+ }
39
+ }
40
+
41
+ export const productsRegistry: ViewRegistry = {
42
+ Products: ({ props, emit }) => {
43
+ const { set } = useStateStore()
44
+ const parsed = productsPropsSchema.parse(props)
45
+ const { containerRef, noteProductClick } = useSectionObservation({
46
+ sectionId: parsed.sectionId ?? '',
47
+ sectionType: parsed.sectionType ?? 'products',
48
+ ...(parsed.variant ? { sectionVariant: parsed.variant } : {}),
49
+ ...(parsed.objective ? { objective: parsed.objective } : {}),
50
+ })
51
+
52
+ if (parsed.status !== 'ready') {
53
+ return (
54
+ <section
55
+ aria-label={parsed.heading ?? 'Productos'}
56
+ className="mx-auto w-full max-w-7xl px-4 py-12"
57
+ >
58
+ {parsed.heading ? (
59
+ <h2 className="mb-2 text-2xl font-semibold tracking-tight">{parsed.heading}</h2>
60
+ ) : null}
61
+ <p className="text-sm text-muted-foreground">{sectionStatusMessage(parsed.status)}</p>
62
+ </section>
63
+ )
64
+ }
65
+
66
+ // The detail affordance opens the product so a variant is chosen explicitly.
67
+ // It is also the section's observation origin (disabled by default).
68
+ const openProduct = (product: ProductCard1Product) => {
69
+ noteProductClick(product)
70
+ set('/lastClicked', product)
71
+ emit('openProduct')
72
+ }
73
+
74
+ // Product-card shortcut: the add affordance adds the product's first
75
+ // purchasable variant (resolved server-side from the product id), then opens
76
+ // the mini-cart. It is not the section's observation origin.
77
+ const addToCart = (product: ProductCard1Product) => {
78
+ set('/lastClicked', product)
79
+ emit('addClickedToCart')
80
+ }
81
+
82
+ const shared = {
83
+ products: parsed.products,
84
+ heading: parsed.heading,
85
+ description: parsed.description,
86
+ onAddToCart: addToCart,
87
+ onProductClick: openProduct,
88
+ }
89
+
90
+ return (
91
+ <div ref={containerRef}>
92
+ {parsed.variant === 'ProductShowcase1' ? (
93
+ <ProductShowcase1Block {...shared} />
94
+ ) : (
95
+ <ProductCard1Block {...shared} />
96
+ )}
97
+ </div>
98
+ )
99
+ },
100
+ }
@@ -0,0 +1,19 @@
1
+ // Optional section fragment: products (mirrors recipes/sections.json)
2
+ // Server-side part: element type + zod catalog entry. The component mappings
3
+ // live in products.client.tsx ('use client'). Unlike the stateless editorial
4
+ // fragments (faq/cta), the products section is data-bound: its per-instance
5
+ // selection is resolved by the host loader and delivered through the view state,
6
+ // so this catalog entry describes the *resolved* props shape.
7
+
8
+ import { productsPropsSchema } from './schema.products'
9
+
10
+ export const productsSection = {
11
+ elementType: 'Products',
12
+ catalog: {
13
+ Products: {
14
+ props: productsPropsSchema,
15
+ description: 'Data-bound product presentation (grid or combined grid-and-carousel).',
16
+ },
17
+ },
18
+ registryModule: './products.client',
19
+ }
@@ -0,0 +1,39 @@
1
+ // Optional section fragment: products (mirrors recipes/sections.json) — shared contract.
2
+ // Zod mirror of the resolved Products section props, shared by the server catalog
3
+ // (products.tsx) and the client registry (products.client.tsx). The product list
4
+ // reuses the canonical presentation contract owned by @thorprovider/blocks so the
5
+ // section renders exactly what the host mapped from the catalog result.
6
+
7
+ import { z } from 'zod'
8
+ import { productPresentationSchema } from '@thorprovider/blocks'
9
+
10
+ // The per-instance resolution status the host loader computes for a data-bound
11
+ // section. `ready`/`empty` carry resolved products; the remaining states carry no
12
+ // products and render an actionable, truthful message instead.
13
+ export const productsStatusSchema = z.enum([
14
+ 'unconfigured',
15
+ 'ready',
16
+ 'empty',
17
+ 'invalid-query',
18
+ 'unsupported-query',
19
+ 'unmounted-capability',
20
+ 'data-error',
21
+ ])
22
+
23
+ export const productsPropsSchema = z.object({
24
+ sectionId: z.string().min(1).optional(),
25
+ sectionType: z.string().min(1).optional(),
26
+ variant: z.string().optional(),
27
+ // Defensive defaults: a missing resolved state (e.g. the view loader could not
28
+ // run) degrades to a truthful error instead of crashing the render.
29
+ status: productsStatusSchema.default('data-error'),
30
+ products: z.array(productPresentationSchema).default([]),
31
+ heading: z.string().optional(),
32
+ description: z.string().optional(),
33
+ objective: z.string().optional(),
34
+ ctaLabel: z.string().optional(),
35
+ ctaHref: z.string().optional(),
36
+ })
37
+
38
+ export type ProductsProps = z.infer<typeof productsPropsSchema>
39
+ export type ProductsStatus = z.infer<typeof productsStatusSchema>