create-tanqory-theme 0.0.0-stage → 0.2.3

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 (879) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -2
  3. package/bin/create-tanqory-theme.mjs +13 -0
  4. package/lib/agents.mjs +118 -0
  5. package/lib/create.mjs +245 -0
  6. package/package.json +39 -4
  7. package/templates/blank/README.md +13 -0
  8. package/templates/blank/config/settings.json +3 -0
  9. package/templates/blank/locales/en.json +22 -0
  10. package/templates/blank/locales/th.json +22 -0
  11. package/templates/blank/package.json +34 -0
  12. package/templates/blank/regions/footer.json +35 -0
  13. package/templates/blank/regions/header.json +13 -0
  14. package/templates/blank/scripts/build.mjs +193 -0
  15. package/templates/blank/scripts/probe.ts +4 -0
  16. package/templates/blank/src/bundle.ts +15 -0
  17. package/templates/blank/src/components/parts.tsx +32 -0
  18. package/templates/blank/src/entry.client.tsx +6 -0
  19. package/templates/blank/src/entry.server.tsx +13 -0
  20. package/templates/blank/src/sections/chrome.tsx +132 -0
  21. package/templates/blank/src/sections/commerce.tsx +242 -0
  22. package/templates/blank/src/sections/content.tsx +111 -0
  23. package/templates/blank/src/sections/editorial.tsx +107 -0
  24. package/templates/blank/src/settings.ts +15 -0
  25. package/templates/blank/src/styles/theme.css +195 -0
  26. package/templates/blank/src/templates.ts +14 -0
  27. package/templates/blank/src/theme.tsx +76 -0
  28. package/templates/blank/template.json +24 -0
  29. package/templates/blank/templates/404.json +14 -0
  30. package/templates/blank/templates/account.json +14 -0
  31. package/templates/blank/templates/article.json +14 -0
  32. package/templates/blank/templates/blog.json +14 -0
  33. package/templates/blank/templates/cart.json +14 -0
  34. package/templates/blank/templates/collection.json +14 -0
  35. package/templates/blank/templates/index.json +36 -0
  36. package/templates/blank/templates/page.contact.json +18 -0
  37. package/templates/blank/templates/page.json +14 -0
  38. package/templates/blank/templates/product.json +18 -0
  39. package/templates/blank/templates/search.json +14 -0
  40. package/templates/blank/tests/theme.test.ts +49 -0
  41. package/templates/blank/tsconfig.json +17 -0
  42. package/templates/blank/vite.client.config.ts +17 -0
  43. package/templates/blank/vite.probe.config.ts +16 -0
  44. package/templates/blank/vite.server.config.ts +32 -0
  45. package/templates/blank/vitest.config.ts +9 -0
  46. package/templates/common/.claude/settings.json +8 -0
  47. package/templates/common/.codex/config.toml +4 -0
  48. package/templates/common/.gemini/settings.json +12 -0
  49. package/templates/common/.github/workflows/theme-check.yml +44 -0
  50. package/templates/common/.mcp.json +12 -0
  51. package/templates/common/.vscode/mcp.json +9 -0
  52. package/templates/common/_gitignore +9 -0
  53. package/templates/common/scripts/check.mjs +44 -0
  54. package/templates/common/scripts/dev.mjs +30 -0
  55. package/templates/common/tanqory.config.json +6 -0
  56. package/templates/nova/.gitattributes +3 -0
  57. package/templates/nova/.tanqory/contracts/LOCK.json +66 -0
  58. package/templates/nova/.tanqory/contracts/nova-01.json +1092 -0
  59. package/templates/nova/.tanqory/contracts/nova-12A.json +2674 -0
  60. package/templates/nova/.tanqory/contracts/nova-12B.json +2285 -0
  61. package/templates/nova/.tanqory/contracts/nova-12C.json +2848 -0
  62. package/templates/nova/.tanqory/contracts/nova-12D.json +3039 -0
  63. package/templates/nova/.tanqory/contracts/nova-12E.json +1473 -0
  64. package/templates/nova/.tanqory/contracts/nova-12F.json +1670 -0
  65. package/templates/nova/.tanqory/contracts/nova-12G.json +1661 -0
  66. package/templates/nova/.tanqory/contracts/nova-13.json +301 -0
  67. package/templates/nova/.tanqory/contracts/nova-14.json +172 -0
  68. package/templates/nova/.tanqory/legacy-settings.json +1121 -0
  69. package/templates/nova/README.md +150 -0
  70. package/templates/nova/_gitignore +28 -0
  71. package/templates/nova/assets/ai-tokens.css +6 -0
  72. package/templates/nova/assets/starter/starter-art-1200x800-0b773ac4.svg +1 -0
  73. package/templates/nova/assets/starter/starter-art-1200x900-59600290.svg +1 -0
  74. package/templates/nova/assets/starter/starter-art-1200x900-c549d496.svg +1 -0
  75. package/templates/nova/assets/starter/starter-art-1600x900-8f979fcb.svg +1 -0
  76. package/templates/nova/assets/starter/starter-art-2100x900-cbb71bfd.svg +1 -0
  77. package/templates/nova/config/legacy-settings.json +438 -0
  78. package/templates/nova/config/promotion-popup.schema.ts +29 -0
  79. package/templates/nova/config/settings.json +23 -0
  80. package/templates/nova/config/settings.schema.ts +231 -0
  81. package/templates/nova/entry.abi.tsx +27 -0
  82. package/templates/nova/groups/footer.json +39 -0
  83. package/templates/nova/groups/header.json +11 -0
  84. package/templates/nova/lib/abi/abi.css +41 -0
  85. package/templates/nova/lib/bundle-core.ts +35 -0
  86. package/templates/nova/lib/bundle.client.ts +15 -0
  87. package/templates/nova/lib/bundle.ts +15 -0
  88. package/templates/nova/lib/legacy-settings.ts +104 -0
  89. package/templates/nova/locales/ar.json +590 -0
  90. package/templates/nova/locales/en.json +590 -0
  91. package/templates/nova/locales/th.json +590 -0
  92. package/templates/nova/main.abi.tsx +12 -0
  93. package/templates/nova/package.json +44 -0
  94. package/templates/nova/scripts/abi/build.mjs +145 -0
  95. package/templates/nova/scripts/abi/check-content.mjs +99 -0
  96. package/templates/nova/scripts/abi/jcs.mjs +27 -0
  97. package/templates/nova/scripts/abi/local-host.mjs +294 -0
  98. package/templates/nova/scripts/abi/manifest.mjs +392 -0
  99. package/templates/nova/scripts/abi/starter-assets.mjs +32 -0
  100. package/templates/nova/scripts/compiler/checks/check-a11y.mjs +88 -0
  101. package/templates/nova/scripts/compiler/checks/check-layout-sanity.mjs +169 -0
  102. package/templates/nova/scripts/compiler/checks/check-page-headings.mjs +67 -0
  103. package/templates/nova/scripts/compiler/checks/check-product-jsonld.mjs +80 -0
  104. package/templates/nova/scripts/compiler/checks/theme-spec/check.mjs +88 -0
  105. package/templates/nova/scripts/compiler/contracts/extract.mjs +188 -0
  106. package/templates/nova/scripts/compiler/contracts/index.ts +125 -0
  107. package/templates/nova/scripts/compiler/contracts/validate.ts +347 -0
  108. package/templates/nova/scripts/compiler/manifest/gen-manifest.mjs +156 -0
  109. package/templates/nova/scripts/compiler/manifest/manifest-entry.ts +386 -0
  110. package/templates/nova/scripts/compiler/manifest/manifest-sections.ts +247 -0
  111. package/templates/nova/scripts/compiler/migrate/migrate-content.mjs +171 -0
  112. package/templates/nova/scripts/compiler/migrate/migrate-groups.mjs +159 -0
  113. package/templates/nova/scripts/parity/case-shim.ts +14 -0
  114. package/templates/nova/scripts/parity/emit.ts +206 -0
  115. package/templates/nova/scripts/parity/index.html +12 -0
  116. package/templates/nova/scripts/parity/main.tsx +23 -0
  117. package/templates/nova/scripts/parity/pages.tsx +123 -0
  118. package/templates/nova/scripts/parity/vite.config.ts +29 -0
  119. package/templates/nova/src/components/base/Accordion.css +20 -0
  120. package/templates/nova/src/components/base/Accordion.tsx +50 -0
  121. package/templates/nova/src/components/base/Badge.css +17 -0
  122. package/templates/nova/src/components/base/Badge.tsx +26 -0
  123. package/templates/nova/src/components/base/Button.css +73 -0
  124. package/templates/nova/src/components/base/Button.tsx +64 -0
  125. package/templates/nova/src/components/base/Checkbox.tsx +18 -0
  126. package/templates/nova/src/components/base/Chip.css +20 -0
  127. package/templates/nova/src/components/base/Chip.tsx +33 -0
  128. package/templates/nova/src/components/base/Divider.css +6 -0
  129. package/templates/nova/src/components/base/Divider.tsx +28 -0
  130. package/templates/nova/src/components/base/IconButton.css +34 -0
  131. package/templates/nova/src/components/base/IconButton.tsx +41 -0
  132. package/templates/nova/src/components/base/Input.css +21 -0
  133. package/templates/nova/src/components/base/Input.tsx +24 -0
  134. package/templates/nova/src/components/base/Link.css +15 -0
  135. package/templates/nova/src/components/base/Link.tsx +23 -0
  136. package/templates/nova/src/components/base/Radio.tsx +13 -0
  137. package/templates/nova/src/components/base/Select.css +18 -0
  138. package/templates/nova/src/components/base/Select.tsx +34 -0
  139. package/templates/nova/src/components/base/Skeleton.css +17 -0
  140. package/templates/nova/src/components/base/Skeleton.tsx +23 -0
  141. package/templates/nova/src/components/base/Spinner.css +13 -0
  142. package/templates/nova/src/components/base/Spinner.tsx +8 -0
  143. package/templates/nova/src/components/base/Switch.tsx +16 -0
  144. package/templates/nova/src/components/base/Tabs.css +24 -0
  145. package/templates/nova/src/components/base/Tabs.tsx +61 -0
  146. package/templates/nova/src/components/base/Textarea.css +1 -0
  147. package/templates/nova/src/components/base/Textarea.tsx +8 -0
  148. package/templates/nova/src/components/base/choice-props.ts +9 -0
  149. package/templates/nova/src/components/base/choice.css +69 -0
  150. package/templates/nova/src/components/commerce/AddToCart.css +4 -0
  151. package/templates/nova/src/components/commerce/AddToCart.tsx +77 -0
  152. package/templates/nova/src/components/commerce/CartLine.css +26 -0
  153. package/templates/nova/src/components/commerce/CartLine.tsx +82 -0
  154. package/templates/nova/src/components/commerce/CartSummary.css +23 -0
  155. package/templates/nova/src/components/commerce/CartSummary.tsx +120 -0
  156. package/templates/nova/src/components/commerce/CollectionCard.css +66 -0
  157. package/templates/nova/src/components/commerce/CollectionCard.tsx +57 -0
  158. package/templates/nova/src/components/commerce/ComparisonTable.css +33 -0
  159. package/templates/nova/src/components/commerce/ComparisonTable.tsx +113 -0
  160. package/templates/nova/src/components/commerce/EarnHint.css +3 -0
  161. package/templates/nova/src/components/commerce/EarnHint.tsx +31 -0
  162. package/templates/nova/src/components/commerce/Hotspot.css +16 -0
  163. package/templates/nova/src/components/commerce/Hotspot.tsx +64 -0
  164. package/templates/nova/src/components/commerce/ListingParts.css +79 -0
  165. package/templates/nova/src/components/commerce/ListingParts.tsx +211 -0
  166. package/templates/nova/src/components/commerce/LookItem.css +19 -0
  167. package/templates/nova/src/components/commerce/LookItem.tsx +41 -0
  168. package/templates/nova/src/components/commerce/MiniCartLine.css +20 -0
  169. package/templates/nova/src/components/commerce/MiniCartLine.tsx +93 -0
  170. package/templates/nova/src/components/commerce/PaymentMark.css +2 -0
  171. package/templates/nova/src/components/commerce/PaymentMark.tsx +55 -0
  172. package/templates/nova/src/components/commerce/Price.css +27 -0
  173. package/templates/nova/src/components/commerce/Price.tsx +55 -0
  174. package/templates/nova/src/components/commerce/ProductCard.css +66 -0
  175. package/templates/nova/src/components/commerce/ProductCard.tsx +117 -0
  176. package/templates/nova/src/components/commerce/ProductGallery.css +72 -0
  177. package/templates/nova/src/components/commerce/ProductGallery.tsx +98 -0
  178. package/templates/nova/src/components/commerce/QuantitySelector.css +23 -0
  179. package/templates/nova/src/components/commerce/QuantitySelector.tsx +45 -0
  180. package/templates/nova/src/components/commerce/Reviews.css +34 -0
  181. package/templates/nova/src/components/commerce/Reviews.tsx +112 -0
  182. package/templates/nova/src/components/commerce/Stars.css +7 -0
  183. package/templates/nova/src/components/commerce/Stars.tsx +38 -0
  184. package/templates/nova/src/components/commerce/VariantPicker.css +31 -0
  185. package/templates/nova/src/components/commerce/VariantPicker.tsx +122 -0
  186. package/templates/nova/src/components/commerce/blocks/Badge.css +6 -0
  187. package/templates/nova/src/components/commerce/blocks/Badge.tsx +33 -0
  188. package/templates/nova/src/components/commerce/blocks/PaymentIcons.tsx +43 -0
  189. package/templates/nova/src/components/commerce/blocks/Swatches.tsx +17 -0
  190. package/templates/nova/src/components/commerce/blocks/payment.css +2 -0
  191. package/templates/nova/src/components/commerce/cart-limit.ts +11 -0
  192. package/templates/nova/src/components/commerce/money.ts +34 -0
  193. package/templates/nova/src/components/commerce/swatch.ts +54 -0
  194. package/templates/nova/src/components/content/ArticleCard.css +22 -0
  195. package/templates/nova/src/components/content/ArticleCard.tsx +53 -0
  196. package/templates/nova/src/components/content/BeforeAfterSlider.css +38 -0
  197. package/templates/nova/src/components/content/BeforeAfterSlider.tsx +87 -0
  198. package/templates/nova/src/components/content/CardRow.css +80 -0
  199. package/templates/nova/src/components/content/CardRow.tsx +140 -0
  200. package/templates/nova/src/components/content/CarouselControls.css +61 -0
  201. package/templates/nova/src/components/content/CarouselControls.tsx +90 -0
  202. package/templates/nova/src/components/content/ContentsNav.css +16 -0
  203. package/templates/nova/src/components/content/ContentsNav.tsx +55 -0
  204. package/templates/nova/src/components/content/Countdown.css +24 -0
  205. package/templates/nova/src/components/content/Countdown.tsx +115 -0
  206. package/templates/nova/src/components/content/HeroCopy.css +92 -0
  207. package/templates/nova/src/components/content/HeroCopy.tsx +148 -0
  208. package/templates/nova/src/components/content/HeroFrame.css +75 -0
  209. package/templates/nova/src/components/content/HeroFrame.tsx +49 -0
  210. package/templates/nova/src/components/content/HeroMedia.css +40 -0
  211. package/templates/nova/src/components/content/HeroMedia.tsx +170 -0
  212. package/templates/nova/src/components/content/LongForm.css +40 -0
  213. package/templates/nova/src/components/content/LongForm.tsx +56 -0
  214. package/templates/nova/src/components/content/MediaFrame.css +5 -0
  215. package/templates/nova/src/components/content/MediaFrame.tsx +52 -0
  216. package/templates/nova/src/components/content/RichText.tsx +30 -0
  217. package/templates/nova/src/components/content/SectionHead.css +8 -0
  218. package/templates/nova/src/components/content/SectionHead.tsx +34 -0
  219. package/templates/nova/src/components/content/ShareButtons.css +2 -0
  220. package/templates/nova/src/components/content/ShareButtons.tsx +40 -0
  221. package/templates/nova/src/components/content/StoreCard.css +14 -0
  222. package/templates/nova/src/components/content/StoreCard.tsx +60 -0
  223. package/templates/nova/src/components/content/TestimonialCard.css +36 -0
  224. package/templates/nova/src/components/content/TestimonialCard.tsx +102 -0
  225. package/templates/nova/src/components/content/UgcTile.css +8 -0
  226. package/templates/nova/src/components/content/UgcTile.tsx +33 -0
  227. package/templates/nova/src/components/content/blocks/Button.tsx +36 -0
  228. package/templates/nova/src/components/content/blocks/Column.tsx +30 -0
  229. package/templates/nova/src/components/content/blocks/Heading.tsx +31 -0
  230. package/templates/nova/src/components/content/blocks/Icon.tsx +25 -0
  231. package/templates/nova/src/components/content/blocks/Image.tsx +42 -0
  232. package/templates/nova/src/components/content/blocks/Text.tsx +26 -0
  233. package/templates/nova/src/components/content/blocks/Video.tsx +44 -0
  234. package/templates/nova/src/components/content/blocks/content.css +25 -0
  235. package/templates/nova/src/components/content/blocks/shared.ts +5 -0
  236. package/templates/nova/src/components/forms/ContactForm.css +19 -0
  237. package/templates/nova/src/components/forms/ContactForm.tsx +139 -0
  238. package/templates/nova/src/components/forms/FormField.css +15 -0
  239. package/templates/nova/src/components/forms/FormField.tsx +77 -0
  240. package/templates/nova/src/components/forms/GatewayFields.tsx +79 -0
  241. package/templates/nova/src/components/forms/NewsletterForm.css +9 -0
  242. package/templates/nova/src/components/forms/NewsletterForm.tsx +141 -0
  243. package/templates/nova/src/components/forms/SearchField.css +7 -0
  244. package/templates/nova/src/components/forms/SearchField.tsx +68 -0
  245. package/templates/nova/src/components/forms/blocks/Newsletter.css +5 -0
  246. package/templates/nova/src/components/forms/blocks/Newsletter.tsx +71 -0
  247. package/templates/nova/src/components/forms/validate.ts +4 -0
  248. package/templates/nova/src/components/listing/ProductListing.tsx +200 -0
  249. package/templates/nova/src/components/navigation/BrandLogo.tsx +35 -0
  250. package/templates/nova/src/components/navigation/Breadcrumb.css +16 -0
  251. package/templates/nova/src/components/navigation/Breadcrumb.tsx +59 -0
  252. package/templates/nova/src/components/navigation/LineAddFriend.css +5 -0
  253. package/templates/nova/src/components/navigation/LineAddFriend.tsx +42 -0
  254. package/templates/nova/src/components/navigation/LocaleSelector.css +33 -0
  255. package/templates/nova/src/components/navigation/LocaleSelector.tsx +64 -0
  256. package/templates/nova/src/components/navigation/Menu.tsx +26 -0
  257. package/templates/nova/src/components/navigation/Pagination.css +24 -0
  258. package/templates/nova/src/components/navigation/Pagination.tsx +74 -0
  259. package/templates/nova/src/components/navigation/RecoveryLinks.css +8 -0
  260. package/templates/nova/src/components/navigation/RecoveryLinks.tsx +23 -0
  261. package/templates/nova/src/components/navigation/SocialLinks.css +5 -0
  262. package/templates/nova/src/components/navigation/SocialLinks.tsx +51 -0
  263. package/templates/nova/src/components/navigation/blocks/SocialLinks.tsx +23 -0
  264. package/templates/nova/src/components/overlays/AccountMenu.css +11 -0
  265. package/templates/nova/src/components/overlays/AccountMenu.tsx +91 -0
  266. package/templates/nova/src/components/overlays/CartDrawer.css +17 -0
  267. package/templates/nova/src/components/overlays/CartDrawer.tsx +118 -0
  268. package/templates/nova/src/components/overlays/ConfirmationDialog.tsx +49 -0
  269. package/templates/nova/src/components/overlays/CookieConsent.css +56 -0
  270. package/templates/nova/src/components/overlays/CookieConsent.tsx +170 -0
  271. package/templates/nova/src/components/overlays/Drawer.css +38 -0
  272. package/templates/nova/src/components/overlays/Drawer.tsx +66 -0
  273. package/templates/nova/src/components/overlays/FriendOfferPanel.tsx +79 -0
  274. package/templates/nova/src/components/overlays/MarketSelector.css +111 -0
  275. package/templates/nova/src/components/overlays/MarketSelector.tsx +450 -0
  276. package/templates/nova/src/components/overlays/Menu.css +9 -0
  277. package/templates/nova/src/components/overlays/Menu.tsx +124 -0
  278. package/templates/nova/src/components/overlays/MobileNavDrawer.css +28 -0
  279. package/templates/nova/src/components/overlays/MobileNavDrawer.tsx +166 -0
  280. package/templates/nova/src/components/overlays/Modal.css +52 -0
  281. package/templates/nova/src/components/overlays/Modal.tsx +88 -0
  282. package/templates/nova/src/components/overlays/Popover.css +4 -0
  283. package/templates/nova/src/components/overlays/Popover.tsx +115 -0
  284. package/templates/nova/src/components/overlays/ProductLightbox.css +20 -0
  285. package/templates/nova/src/components/overlays/ProductLightbox.tsx +70 -0
  286. package/templates/nova/src/components/overlays/PromoPopup.css +32 -0
  287. package/templates/nova/src/components/overlays/PromoPopup.tsx +198 -0
  288. package/templates/nova/src/components/overlays/QuickView.css +31 -0
  289. package/templates/nova/src/components/overlays/QuickView.tsx +127 -0
  290. package/templates/nova/src/components/overlays/ReferralPanel.css +16 -0
  291. package/templates/nova/src/components/overlays/ReferralPanel.tsx +138 -0
  292. package/templates/nova/src/components/overlays/RewardsLauncher.css +22 -0
  293. package/templates/nova/src/components/overlays/RewardsLauncher.tsx +270 -0
  294. package/templates/nova/src/components/overlays/SearchModal.css +48 -0
  295. package/templates/nova/src/components/overlays/SearchModal.tsx +302 -0
  296. package/templates/nova/src/components/overlays/Sheet.css +16 -0
  297. package/templates/nova/src/components/overlays/Sheet.tsx +48 -0
  298. package/templates/nova/src/components/overlays/SizeGuide.css +8 -0
  299. package/templates/nova/src/components/overlays/SizeGuide.tsx +74 -0
  300. package/templates/nova/src/components/overlays/Toast.css +20 -0
  301. package/templates/nova/src/components/overlays/Toast.tsx +164 -0
  302. package/templates/nova/src/components/overlays/Tooltip.css +6 -0
  303. package/templates/nova/src/components/overlays/Tooltip.tsx +59 -0
  304. package/templates/nova/src/components/overlays/use-dialog.ts +74 -0
  305. package/templates/nova/src/components/section/shared.css +42 -0
  306. package/templates/nova/src/components/section/shared.ts +120 -0
  307. package/templates/nova/src/components/section/shell.tsx +29 -0
  308. package/templates/nova/src/components/states/Alert.css +10 -0
  309. package/templates/nova/src/components/states/Alert.tsx +50 -0
  310. package/templates/nova/src/components/states/EditorFrame.css +31 -0
  311. package/templates/nova/src/components/states/EditorFrame.tsx +43 -0
  312. package/templates/nova/src/components/states/MediaState.css +1 -0
  313. package/templates/nova/src/components/states/MediaState.tsx +49 -0
  314. package/templates/nova/src/components/states/OfflineNotice.css +4 -0
  315. package/templates/nova/src/components/states/OfflineNotice.tsx +24 -0
  316. package/templates/nova/src/components/states/SkeletonLayout.css +22 -0
  317. package/templates/nova/src/components/states/SkeletonLayout.tsx +104 -0
  318. package/templates/nova/src/components/states/StateBlock.css +22 -0
  319. package/templates/nova/src/components/states/StateBlock.tsx +39 -0
  320. package/templates/nova/src/components/states/media/MediaError.css +6 -0
  321. package/templates/nova/src/components/states/media/MediaError.tsx +49 -0
  322. package/templates/nova/src/components/states/media/MediaPlaceholder.css +24 -0
  323. package/templates/nova/src/components/states/media/MediaPlaceholder.tsx +101 -0
  324. package/templates/nova/src/components/states/media/placeholder-art.ts +142 -0
  325. package/templates/nova/src/layouts/Main.tsx +80 -0
  326. package/templates/nova/src/sections/Account/Account.tsx +109 -0
  327. package/templates/nova/src/sections/Account/index.ts +22 -0
  328. package/templates/nova/src/sections/Account/presets.ts +6 -0
  329. package/templates/nova/src/sections/Account/schema.ts +29 -0
  330. package/templates/nova/src/sections/AnnouncementBar/AnnouncementBar.css +37 -0
  331. package/templates/nova/src/sections/AnnouncementBar/AnnouncementBar.tsx +151 -0
  332. package/templates/nova/src/sections/AnnouncementBar/index.ts +20 -0
  333. package/templates/nova/src/sections/AnnouncementBar/presets.ts +10 -0
  334. package/templates/nova/src/sections/AnnouncementBar/schema.ts +26 -0
  335. package/templates/nova/src/sections/ArticleBody/ArticleBody.tsx +129 -0
  336. package/templates/nova/src/sections/ArticleBody/index.ts +21 -0
  337. package/templates/nova/src/sections/ArticleBody/presets.ts +10 -0
  338. package/templates/nova/src/sections/ArticleBody/schema.ts +40 -0
  339. package/templates/nova/src/sections/BeforeAfter/BeforeAfter.tsx +47 -0
  340. package/templates/nova/src/sections/BeforeAfter/blocks/Comparison.tsx +20 -0
  341. package/templates/nova/src/sections/BeforeAfter/index.ts +28 -0
  342. package/templates/nova/src/sections/BeforeAfter/presets.ts +10 -0
  343. package/templates/nova/src/sections/BeforeAfter/schema.ts +34 -0
  344. package/templates/nova/src/sections/BlogPosts/BlogPosts.tsx +106 -0
  345. package/templates/nova/src/sections/BlogPosts/index.ts +30 -0
  346. package/templates/nova/src/sections/BlogPosts/presets.ts +10 -0
  347. package/templates/nova/src/sections/BlogPosts/schema.ts +42 -0
  348. package/templates/nova/src/sections/Breadcrumbs/Breadcrumbs.css +10 -0
  349. package/templates/nova/src/sections/Breadcrumbs/Breadcrumbs.tsx +41 -0
  350. package/templates/nova/src/sections/Breadcrumbs/index.ts +20 -0
  351. package/templates/nova/src/sections/Breadcrumbs/presets.ts +6 -0
  352. package/templates/nova/src/sections/Breadcrumbs/schema.ts +24 -0
  353. package/templates/nova/src/sections/CartItems/CartItems.tsx +170 -0
  354. package/templates/nova/src/sections/CartItems/blocks/CartBadge.tsx +14 -0
  355. package/templates/nova/src/sections/CartItems/index.ts +37 -0
  356. package/templates/nova/src/sections/CartItems/presets.ts +8 -0
  357. package/templates/nova/src/sections/CartItems/schema.ts +34 -0
  358. package/templates/nova/src/sections/CollectionBanner/CollectionBanner.tsx +120 -0
  359. package/templates/nova/src/sections/CollectionBanner/index.ts +25 -0
  360. package/templates/nova/src/sections/CollectionBanner/presets.ts +10 -0
  361. package/templates/nova/src/sections/CollectionBanner/schema.ts +37 -0
  362. package/templates/nova/src/sections/CollectionLinks/CollectionLinks.tsx +47 -0
  363. package/templates/nova/src/sections/CollectionLinks/index.ts +20 -0
  364. package/templates/nova/src/sections/CollectionLinks/presets.ts +8 -0
  365. package/templates/nova/src/sections/CollectionLinks/schema.ts +33 -0
  366. package/templates/nova/src/sections/CollectionList/CollectionList.tsx +89 -0
  367. package/templates/nova/src/sections/CollectionList/blocks/CollectionItem.tsx +25 -0
  368. package/templates/nova/src/sections/CollectionList/index.ts +33 -0
  369. package/templates/nova/src/sections/CollectionList/presets.ts +10 -0
  370. package/templates/nova/src/sections/CollectionList/schema.ts +41 -0
  371. package/templates/nova/src/sections/ComparisonTable/ComparisonTable.tsx +52 -0
  372. package/templates/nova/src/sections/ComparisonTable/blocks/CompareItem.tsx +26 -0
  373. package/templates/nova/src/sections/ComparisonTable/index.ts +31 -0
  374. package/templates/nova/src/sections/ComparisonTable/presets.ts +8 -0
  375. package/templates/nova/src/sections/ComparisonTable/schema.ts +31 -0
  376. package/templates/nova/src/sections/ContactForm/ContactForm.tsx +83 -0
  377. package/templates/nova/src/sections/ContactForm/blocks/ContactDetail.tsx +13 -0
  378. package/templates/nova/src/sections/ContactForm/index.ts +29 -0
  379. package/templates/nova/src/sections/ContactForm/presets.ts +8 -0
  380. package/templates/nova/src/sections/ContactForm/schema.ts +34 -0
  381. package/templates/nova/src/sections/Divider/Divider.css +17 -0
  382. package/templates/nova/src/sections/Divider/Divider.tsx +21 -0
  383. package/templates/nova/src/sections/Divider/index.ts +20 -0
  384. package/templates/nova/src/sections/Divider/presets.ts +10 -0
  385. package/templates/nova/src/sections/Divider/schema.ts +24 -0
  386. package/templates/nova/src/sections/Faq/Faq.tsx +51 -0
  387. package/templates/nova/src/sections/Faq/blocks/FaqItem.tsx +13 -0
  388. package/templates/nova/src/sections/Faq/index.ts +28 -0
  389. package/templates/nova/src/sections/Faq/presets.ts +8 -0
  390. package/templates/nova/src/sections/Faq/schema.ts +32 -0
  391. package/templates/nova/src/sections/FeatureGridBlocks/FeatureGridBlocks.tsx +90 -0
  392. package/templates/nova/src/sections/FeatureGridBlocks/index.ts +35 -0
  393. package/templates/nova/src/sections/FeatureGridBlocks/presets.ts +8 -0
  394. package/templates/nova/src/sections/FeatureGridBlocks/schema.ts +35 -0
  395. package/templates/nova/src/sections/FeatureHighlights/FeatureHighlights.tsx +56 -0
  396. package/templates/nova/src/sections/FeatureHighlights/index.ts +20 -0
  397. package/templates/nova/src/sections/FeatureHighlights/presets.ts +6 -0
  398. package/templates/nova/src/sections/FeatureHighlights/schema.ts +27 -0
  399. package/templates/nova/src/sections/FeaturedCollection/FeaturedCollection.tsx +67 -0
  400. package/templates/nova/src/sections/FeaturedCollection/index.ts +21 -0
  401. package/templates/nova/src/sections/FeaturedCollection/presets.ts +10 -0
  402. package/templates/nova/src/sections/FeaturedCollection/schema.ts +39 -0
  403. package/templates/nova/src/sections/FeaturedProduct/FeaturedProduct.tsx +107 -0
  404. package/templates/nova/src/sections/FeaturedProduct/index.ts +23 -0
  405. package/templates/nova/src/sections/FeaturedProduct/presets.ts +8 -0
  406. package/templates/nova/src/sections/FeaturedProduct/schema.ts +35 -0
  407. package/templates/nova/src/sections/Footer/Footer.css +81 -0
  408. package/templates/nova/src/sections/Footer/Footer.tsx +256 -0
  409. package/templates/nova/src/sections/Footer/blocks/FooterBrand.tsx +40 -0
  410. package/templates/nova/src/sections/Footer/blocks/FooterLine.tsx +11 -0
  411. package/templates/nova/src/sections/Footer/blocks/FooterMenu.tsx +14 -0
  412. package/templates/nova/src/sections/Footer/blocks/FooterText.tsx +8 -0
  413. package/templates/nova/src/sections/Footer/blocks/footer.css +7 -0
  414. package/templates/nova/src/sections/Footer/index.ts +38 -0
  415. package/templates/nova/src/sections/Footer/presets.ts +10 -0
  416. package/templates/nova/src/sections/Footer/schema.ts +27 -0
  417. package/templates/nova/src/sections/Group/Group.css +59 -0
  418. package/templates/nova/src/sections/Group/Group.tsx +167 -0
  419. package/templates/nova/src/sections/Group/blocks/Accordion.tsx +26 -0
  420. package/templates/nova/src/sections/Group/blocks/JumboText.tsx +20 -0
  421. package/templates/nova/src/sections/Group/blocks/Spacer.tsx +16 -0
  422. package/templates/nova/src/sections/Group/index.ts +36 -0
  423. package/templates/nova/src/sections/Group/presets.ts +12 -0
  424. package/templates/nova/src/sections/Group/schema.ts +35 -0
  425. package/templates/nova/src/sections/Header/Header.css +185 -0
  426. package/templates/nova/src/sections/Header/Header.tsx +478 -0
  427. package/templates/nova/src/sections/Header/index.ts +32 -0
  428. package/templates/nova/src/sections/Header/presets.ts +10 -0
  429. package/templates/nova/src/sections/Header/schema.ts +32 -0
  430. package/templates/nova/src/sections/Hero/Hero.tsx +128 -0
  431. package/templates/nova/src/sections/Hero/index.ts +24 -0
  432. package/templates/nova/src/sections/Hero/presets.ts +16 -0
  433. package/templates/nova/src/sections/Hero/schema.ts +56 -0
  434. package/templates/nova/src/sections/ImageGallery/ImageGallery.tsx +86 -0
  435. package/templates/nova/src/sections/ImageGallery/index.ts +28 -0
  436. package/templates/nova/src/sections/ImageGallery/presets.ts +10 -0
  437. package/templates/nova/src/sections/ImageGallery/schema.ts +34 -0
  438. package/templates/nova/src/sections/ImageWithText/ImageWithText.tsx +62 -0
  439. package/templates/nova/src/sections/ImageWithText/index.ts +23 -0
  440. package/templates/nova/src/sections/ImageWithText/presets.ts +12 -0
  441. package/templates/nova/src/sections/ImageWithText/schema.ts +45 -0
  442. package/templates/nova/src/sections/LineAddFriend/LineAddFriend.tsx +25 -0
  443. package/templates/nova/src/sections/LineAddFriend/index.ts +20 -0
  444. package/templates/nova/src/sections/LineAddFriend/presets.ts +3 -0
  445. package/templates/nova/src/sections/LineAddFriend/schema.ts +25 -0
  446. package/templates/nova/src/sections/LogoList/LogoList.tsx +40 -0
  447. package/templates/nova/src/sections/LogoList/blocks/Logo.tsx +18 -0
  448. package/templates/nova/src/sections/LogoList/index.ts +29 -0
  449. package/templates/nova/src/sections/LogoList/presets.ts +8 -0
  450. package/templates/nova/src/sections/LogoList/schema.ts +29 -0
  451. package/templates/nova/src/sections/MainCollection/MainCollection.tsx +51 -0
  452. package/templates/nova/src/sections/MainCollection/index.ts +26 -0
  453. package/templates/nova/src/sections/MainCollection/presets.ts +10 -0
  454. package/templates/nova/src/sections/MainCollection/schema.ts +29 -0
  455. package/templates/nova/src/sections/Marquee/Marquee.tsx +114 -0
  456. package/templates/nova/src/sections/Marquee/index.ts +20 -0
  457. package/templates/nova/src/sections/Marquee/presets.ts +10 -0
  458. package/templates/nova/src/sections/Marquee/schema.ts +23 -0
  459. package/templates/nova/src/sections/Multicolumn/Multicolumn.tsx +56 -0
  460. package/templates/nova/src/sections/Multicolumn/index.ts +25 -0
  461. package/templates/nova/src/sections/Multicolumn/presets.ts +8 -0
  462. package/templates/nova/src/sections/Multicolumn/schema.ts +34 -0
  463. package/templates/nova/src/sections/Newsletter/Newsletter.tsx +64 -0
  464. package/templates/nova/src/sections/Newsletter/index.ts +20 -0
  465. package/templates/nova/src/sections/Newsletter/presets.ts +10 -0
  466. package/templates/nova/src/sections/Newsletter/schema.ts +36 -0
  467. package/templates/nova/src/sections/NotFound/NotFound.tsx +41 -0
  468. package/templates/nova/src/sections/NotFound/index.ts +20 -0
  469. package/templates/nova/src/sections/NotFound/presets.ts +6 -0
  470. package/templates/nova/src/sections/NotFound/schema.ts +30 -0
  471. package/templates/nova/src/sections/PageBody/PageBody.tsx +35 -0
  472. package/templates/nova/src/sections/PageBody/index.ts +21 -0
  473. package/templates/nova/src/sections/PageBody/presets.ts +6 -0
  474. package/templates/nova/src/sections/PageBody/schema.ts +29 -0
  475. package/templates/nova/src/sections/PolicyPage/PolicyPage.tsx +62 -0
  476. package/templates/nova/src/sections/PolicyPage/index.ts +23 -0
  477. package/templates/nova/src/sections/PolicyPage/presets.ts +6 -0
  478. package/templates/nova/src/sections/PolicyPage/schema.ts +27 -0
  479. package/templates/nova/src/sections/ProductDetails/ProductDetails.tsx +269 -0
  480. package/templates/nova/src/sections/ProductDetails/blocks/AddToCartBlock.tsx +15 -0
  481. package/templates/nova/src/sections/ProductDetails/blocks/ProductDescription.tsx +15 -0
  482. package/templates/nova/src/sections/ProductDetails/blocks/ProductInventory.tsx +38 -0
  483. package/templates/nova/src/sections/ProductDetails/blocks/ProductPrice.tsx +19 -0
  484. package/templates/nova/src/sections/ProductDetails/blocks/ProductSku.tsx +15 -0
  485. package/templates/nova/src/sections/ProductDetails/blocks/ProductTitle.tsx +17 -0
  486. package/templates/nova/src/sections/ProductDetails/blocks/Quantity.tsx +14 -0
  487. package/templates/nova/src/sections/ProductDetails/blocks/Tab.tsx +23 -0
  488. package/templates/nova/src/sections/ProductDetails/blocks/VariantPickerBlock.tsx +44 -0
  489. package/templates/nova/src/sections/ProductDetails/blocks/commerce-blocks.css +13 -0
  490. package/templates/nova/src/sections/ProductDetails/index.ts +44 -0
  491. package/templates/nova/src/sections/ProductDetails/presets.ts +10 -0
  492. package/templates/nova/src/sections/ProductDetails/schema.ts +44 -0
  493. package/templates/nova/src/sections/ProductGrid/ProductGrid.tsx +38 -0
  494. package/templates/nova/src/sections/ProductGrid/index.ts +25 -0
  495. package/templates/nova/src/sections/ProductGrid/presets.ts +6 -0
  496. package/templates/nova/src/sections/ProductGrid/schema.ts +37 -0
  497. package/templates/nova/src/sections/ProductRecommendations/ProductRecommendations.tsx +45 -0
  498. package/templates/nova/src/sections/ProductRecommendations/index.ts +20 -0
  499. package/templates/nova/src/sections/ProductRecommendations/presets.ts +8 -0
  500. package/templates/nova/src/sections/ProductRecommendations/schema.ts +28 -0
  501. package/templates/nova/src/sections/ProductReviews/ProductReviews.tsx +136 -0
  502. package/templates/nova/src/sections/ProductReviews/index.ts +22 -0
  503. package/templates/nova/src/sections/ProductReviews/presets.ts +6 -0
  504. package/templates/nova/src/sections/ProductReviews/schema.ts +31 -0
  505. package/templates/nova/src/sections/PromoBanner/PromoBanner.tsx +78 -0
  506. package/templates/nova/src/sections/PromoBanner/index.ts +20 -0
  507. package/templates/nova/src/sections/PromoBanner/presets.ts +10 -0
  508. package/templates/nova/src/sections/PromoBanner/schema.ts +38 -0
  509. package/templates/nova/src/sections/RecentlyViewed/RecentlyViewed.tsx +53 -0
  510. package/templates/nova/src/sections/RecentlyViewed/index.ts +20 -0
  511. package/templates/nova/src/sections/RecentlyViewed/presets.ts +6 -0
  512. package/templates/nova/src/sections/RecentlyViewed/schema.ts +26 -0
  513. package/templates/nova/src/sections/RichText/RichText.tsx +38 -0
  514. package/templates/nova/src/sections/RichText/index.ts +20 -0
  515. package/templates/nova/src/sections/RichText/presets.ts +10 -0
  516. package/templates/nova/src/sections/RichText/schema.ts +37 -0
  517. package/templates/nova/src/sections/SearchResults/SearchResults.tsx +112 -0
  518. package/templates/nova/src/sections/SearchResults/index.ts +26 -0
  519. package/templates/nova/src/sections/SearchResults/presets.ts +8 -0
  520. package/templates/nova/src/sections/SearchResults/schema.ts +30 -0
  521. package/templates/nova/src/sections/ShopTheLook/ShopTheLook.tsx +112 -0
  522. package/templates/nova/src/sections/ShopTheLook/blocks/Hotspot.tsx +22 -0
  523. package/templates/nova/src/sections/ShopTheLook/index.ts +33 -0
  524. package/templates/nova/src/sections/ShopTheLook/presets.ts +8 -0
  525. package/templates/nova/src/sections/ShopTheLook/schema.ts +31 -0
  526. package/templates/nova/src/sections/Slideshow/Slideshow.tsx +199 -0
  527. package/templates/nova/src/sections/Slideshow/blocks/Slide.tsx +93 -0
  528. package/templates/nova/src/sections/Slideshow/index.ts +30 -0
  529. package/templates/nova/src/sections/Slideshow/presets.ts +10 -0
  530. package/templates/nova/src/sections/Slideshow/schema.ts +36 -0
  531. package/templates/nova/src/sections/StoreLocator/StoreLocator.tsx +66 -0
  532. package/templates/nova/src/sections/StoreLocator/index.ts +20 -0
  533. package/templates/nova/src/sections/StoreLocator/presets.ts +6 -0
  534. package/templates/nova/src/sections/StoreLocator/schema.ts +34 -0
  535. package/templates/nova/src/sections/TeamFounders/TeamFounders.tsx +74 -0
  536. package/templates/nova/src/sections/TeamFounders/blocks/Person.tsx +28 -0
  537. package/templates/nova/src/sections/TeamFounders/index.ts +28 -0
  538. package/templates/nova/src/sections/TeamFounders/presets.ts +10 -0
  539. package/templates/nova/src/sections/TeamFounders/schema.ts +36 -0
  540. package/templates/nova/src/sections/Testimonials/Testimonials.tsx +83 -0
  541. package/templates/nova/src/sections/Testimonials/blocks/Testimonial.tsx +30 -0
  542. package/templates/nova/src/sections/Testimonials/index.ts +29 -0
  543. package/templates/nova/src/sections/Testimonials/presets.ts +10 -0
  544. package/templates/nova/src/sections/Testimonials/schema.ts +35 -0
  545. package/templates/nova/src/sections/Timeline/Timeline.tsx +43 -0
  546. package/templates/nova/src/sections/Timeline/blocks/Step.tsx +19 -0
  547. package/templates/nova/src/sections/Timeline/index.ts +28 -0
  548. package/templates/nova/src/sections/Timeline/presets.ts +8 -0
  549. package/templates/nova/src/sections/Timeline/schema.ts +31 -0
  550. package/templates/nova/src/sections/TrustBadges/TrustBadges.tsx +42 -0
  551. package/templates/nova/src/sections/TrustBadges/index.ts +29 -0
  552. package/templates/nova/src/sections/TrustBadges/presets.ts +8 -0
  553. package/templates/nova/src/sections/TrustBadges/schema.ts +29 -0
  554. package/templates/nova/src/sections/UgcGallery/UgcGallery.tsx +58 -0
  555. package/templates/nova/src/sections/UgcGallery/blocks/UgcImage.tsx +27 -0
  556. package/templates/nova/src/sections/UgcGallery/index.ts +31 -0
  557. package/templates/nova/src/sections/UgcGallery/presets.ts +8 -0
  558. package/templates/nova/src/sections/UgcGallery/schema.ts +35 -0
  559. package/templates/nova/src/sections/VideoHero/VideoHero.tsx +75 -0
  560. package/templates/nova/src/sections/VideoHero/index.ts +22 -0
  561. package/templates/nova/src/sections/VideoHero/presets.ts +8 -0
  562. package/templates/nova/src/sections/VideoHero/schema.ts +43 -0
  563. package/templates/nova/src/sections/data.ts +115 -0
  564. package/templates/nova/src/sections/shipped-copy.ts +25 -0
  565. package/templates/nova/src/sections/starter.ts +49 -0
  566. package/templates/nova/src/styles/Container.css +6 -0
  567. package/templates/nova/src/styles/Container.tsx +13 -0
  568. package/templates/nova/src/styles/Icon.css +2 -0
  569. package/templates/nova/src/styles/Icon.tsx +101 -0
  570. package/templates/nova/src/styles/NovaProvider.tsx +91 -0
  571. package/templates/nova/src/styles/base.css +142 -0
  572. package/templates/nova/src/styles/blocks.css +6 -0
  573. package/templates/nova/src/styles/fonts/OFL.txt +93 -0
  574. package/templates/nova/src/styles/fonts/geist-cyrillic-ext.woff2 +0 -0
  575. package/templates/nova/src/styles/fonts/geist-cyrillic.woff2 +0 -0
  576. package/templates/nova/src/styles/fonts/geist-latin-ext.woff2 +0 -0
  577. package/templates/nova/src/styles/fonts/geist-latin.woff2 +0 -0
  578. package/templates/nova/src/styles/fonts/geist-mono-cyrillic-ext.woff2 +0 -0
  579. package/templates/nova/src/styles/fonts/geist-mono-cyrillic.woff2 +0 -0
  580. package/templates/nova/src/styles/fonts/geist-mono-latin-ext.woff2 +0 -0
  581. package/templates/nova/src/styles/fonts/geist-mono-latin.woff2 +0 -0
  582. package/templates/nova/src/styles/fonts/geist-mono-vietnamese.woff2 +0 -0
  583. package/templates/nova/src/styles/fonts/geist-vietnamese.woff2 +0 -0
  584. package/templates/nova/src/styles/fonts.css +95 -0
  585. package/templates/nova/src/styles/global-settings.css +88 -0
  586. package/templates/nova/src/styles/index.css +6 -0
  587. package/templates/nova/src/styles/motion.ts +53 -0
  588. package/templates/nova/src/styles/scroll.ts +24 -0
  589. package/templates/nova/src/styles/sections/commerce.css +147 -0
  590. package/templates/nova/src/styles/sections/content.css +304 -0
  591. package/templates/nova/src/styles/sections/editorial.css +131 -0
  592. package/templates/nova/src/styles/sections/promotion.css +336 -0
  593. package/templates/nova/src/styles/sections/trust.css +117 -0
  594. package/templates/nova/src/styles/sections/utility.css +190 -0
  595. package/templates/nova/src/styles/strings.ts +492 -0
  596. package/templates/nova/src/styles/text.ts +4 -0
  597. package/templates/nova/src/styles/theme.ts +171 -0
  598. package/templates/nova/src/styles/tokens.css +600 -0
  599. package/templates/nova/src/styles/viewport.ts +23 -0
  600. package/templates/nova/template.json +94 -0
  601. package/templates/nova/templates/404.json +30 -0
  602. package/templates/nova/templates/account.json +15 -0
  603. package/templates/nova/templates/article.json +41 -0
  604. package/templates/nova/templates/article.longform.json +17 -0
  605. package/templates/nova/templates/blog.featured.json +22 -0
  606. package/templates/nova/templates/blog.json +29 -0
  607. package/templates/nova/templates/cart.json +67 -0
  608. package/templates/nova/templates/collection.featured.json +43 -0
  609. package/templates/nova/templates/collection.json +30 -0
  610. package/templates/nova/templates/contact.json +65 -0
  611. package/templates/nova/templates/index.json +167 -0
  612. package/templates/nova/templates/list-collections.json +19 -0
  613. package/templates/nova/templates/page.about.json +134 -0
  614. package/templates/nova/templates/page.contact.json +27 -0
  615. package/templates/nova/templates/page.json +27 -0
  616. package/templates/nova/templates/policy.json +23 -0
  617. package/templates/nova/templates/product.bundle.json +145 -0
  618. package/templates/nova/templates/product.json +119 -0
  619. package/templates/nova/templates/search.json +17 -0
  620. package/templates/nova/tests/abi-ai-contract.test.ts +99 -0
  621. package/templates/nova/tests/abi-forms.test.tsx +269 -0
  622. package/templates/nova/tests/abi-inventory.test.tsx +197 -0
  623. package/templates/nova/tests/abi-legacy-settings.test.tsx +105 -0
  624. package/templates/nova/tests/abi-opt-in.test.tsx +100 -0
  625. package/templates/nova/tests/abi-parity.test.tsx +95 -0
  626. package/templates/nova/tests/abi-r5-store-settings.test.tsx +337 -0
  627. package/templates/nova/tests/abi-render.test.tsx +178 -0
  628. package/templates/nova/tests/abi-shop-links.test.tsx +141 -0
  629. package/templates/nova/tests/abi-storefront-basics.test.tsx +233 -0
  630. package/templates/nova/tests/abi-variant-url.test.tsx +73 -0
  631. package/templates/nova/tests/add-refused-no-success.test.tsx +193 -0
  632. package/templates/nova/tests/architecture.test.ts +63 -0
  633. package/templates/nova/tests/architecture.ts +160 -0
  634. package/templates/nova/tests/audit-guards.test.ts +31 -0
  635. package/templates/nova/tests/bundle-split.test.ts +29 -0
  636. package/templates/nova/tests/cart-limit-message.test.tsx +212 -0
  637. package/templates/nova/tests/cart-link-message.test.tsx +98 -0
  638. package/templates/nova/tests/cases/components/Overview.cases.tsx +8 -0
  639. package/templates/nova/tests/cases/components/base/Accordion.cases.tsx +40 -0
  640. package/templates/nova/tests/cases/components/base/Badge.cases.tsx +30 -0
  641. package/templates/nova/tests/cases/components/base/Button.cases.tsx +73 -0
  642. package/templates/nova/tests/cases/components/base/Checkbox.cases.tsx +27 -0
  643. package/templates/nova/tests/cases/components/base/Chip.cases.tsx +37 -0
  644. package/templates/nova/tests/cases/components/base/Divider.cases.tsx +26 -0
  645. package/templates/nova/tests/cases/components/base/IconButton.cases.tsx +28 -0
  646. package/templates/nova/tests/cases/components/base/Input.cases.tsx +28 -0
  647. package/templates/nova/tests/cases/components/base/Link.cases.tsx +27 -0
  648. package/templates/nova/tests/cases/components/base/Radio.cases.tsx +27 -0
  649. package/templates/nova/tests/cases/components/base/Select.cases.tsx +26 -0
  650. package/templates/nova/tests/cases/components/base/Skeleton.cases.tsx +29 -0
  651. package/templates/nova/tests/cases/components/base/Spinner.cases.tsx +16 -0
  652. package/templates/nova/tests/cases/components/base/Switch.cases.tsx +26 -0
  653. package/templates/nova/tests/cases/components/base/Tabs.cases.tsx +39 -0
  654. package/templates/nova/tests/cases/components/base/Textarea.cases.tsx +17 -0
  655. package/templates/nova/tests/cases/components/commerce/AddToCart.cases.tsx +36 -0
  656. package/templates/nova/tests/cases/components/commerce/CartLine.cases.tsx +20 -0
  657. package/templates/nova/tests/cases/components/commerce/CartSummary.cases.tsx +16 -0
  658. package/templates/nova/tests/cases/components/commerce/CollectionCard.cases.tsx +19 -0
  659. package/templates/nova/tests/cases/components/commerce/ComparisonTable.cases.tsx +19 -0
  660. package/templates/nova/tests/cases/components/commerce/Hotspot.cases.tsx +25 -0
  661. package/templates/nova/tests/cases/components/commerce/LookItem.cases.tsx +19 -0
  662. package/templates/nova/tests/cases/components/commerce/PaymentMark.cases.tsx +16 -0
  663. package/templates/nova/tests/cases/components/commerce/Price.cases.tsx +27 -0
  664. package/templates/nova/tests/cases/components/commerce/ProductCard.cases.tsx +67 -0
  665. package/templates/nova/tests/cases/components/commerce/ProductGallery.cases.tsx +39 -0
  666. package/templates/nova/tests/cases/components/commerce/QuantitySelector.cases.tsx +31 -0
  667. package/templates/nova/tests/cases/components/commerce/Reviews.cases.tsx +35 -0
  668. package/templates/nova/tests/cases/components/commerce/Stars.cases.tsx +16 -0
  669. package/templates/nova/tests/cases/components/commerce/VariantPicker.cases.tsx +38 -0
  670. package/templates/nova/tests/cases/components/commerce/blocks/Badge.cases.tsx +17 -0
  671. package/templates/nova/tests/cases/components/commerce/blocks/PaymentIcons.cases.tsx +17 -0
  672. package/templates/nova/tests/cases/components/commerce/blocks/Swatches.cases.tsx +22 -0
  673. package/templates/nova/tests/cases/components/content/ArticleCard.cases.tsx +17 -0
  674. package/templates/nova/tests/cases/components/content/BeforeAfterSlider.cases.tsx +18 -0
  675. package/templates/nova/tests/cases/components/content/CardRow.cases.tsx +23 -0
  676. package/templates/nova/tests/cases/components/content/CarouselControls.cases.tsx +48 -0
  677. package/templates/nova/tests/cases/components/content/ContentsNav.cases.tsx +17 -0
  678. package/templates/nova/tests/cases/components/content/Countdown.cases.tsx +28 -0
  679. package/templates/nova/tests/cases/components/content/HeroCopy.cases.tsx +42 -0
  680. package/templates/nova/tests/cases/components/content/HeroFrame.cases.tsx +22 -0
  681. package/templates/nova/tests/cases/components/content/HeroMedia.cases.tsx +18 -0
  682. package/templates/nova/tests/cases/components/content/LongForm.cases.tsx +17 -0
  683. package/templates/nova/tests/cases/components/content/MediaFrame.cases.tsx +32 -0
  684. package/templates/nova/tests/cases/components/content/RichText.cases.tsx +19 -0
  685. package/templates/nova/tests/cases/components/content/SectionHead.cases.tsx +19 -0
  686. package/templates/nova/tests/cases/components/content/ShareButtons.cases.tsx +24 -0
  687. package/templates/nova/tests/cases/components/content/StoreCard.cases.tsx +17 -0
  688. package/templates/nova/tests/cases/components/content/TestimonialCard.cases.tsx +19 -0
  689. package/templates/nova/tests/cases/components/content/UgcTile.cases.tsx +18 -0
  690. package/templates/nova/tests/cases/components/content/blocks/Button.cases.tsx +20 -0
  691. package/templates/nova/tests/cases/components/content/blocks/Column.cases.tsx +17 -0
  692. package/templates/nova/tests/cases/components/content/blocks/Heading.cases.tsx +18 -0
  693. package/templates/nova/tests/cases/components/content/blocks/Icon.cases.tsx +17 -0
  694. package/templates/nova/tests/cases/components/content/blocks/Image.cases.tsx +22 -0
  695. package/templates/nova/tests/cases/components/content/blocks/Text.cases.tsx +18 -0
  696. package/templates/nova/tests/cases/components/content/blocks/Video.cases.tsx +18 -0
  697. package/templates/nova/tests/cases/components/forms/ContactForm.cases.tsx +29 -0
  698. package/templates/nova/tests/cases/components/forms/FormField.cases.tsx +61 -0
  699. package/templates/nova/tests/cases/components/forms/NewsletterForm.cases.tsx +17 -0
  700. package/templates/nova/tests/cases/components/forms/SearchField.cases.tsx +25 -0
  701. package/templates/nova/tests/cases/components/forms/blocks/Newsletter.cases.tsx +19 -0
  702. package/templates/nova/tests/cases/components/navigation/BrandLogo.cases.tsx +29 -0
  703. package/templates/nova/tests/cases/components/navigation/Breadcrumb.cases.tsx +19 -0
  704. package/templates/nova/tests/cases/components/navigation/LocaleSelector.cases.tsx +32 -0
  705. package/templates/nova/tests/cases/components/navigation/Menu.cases.tsx +27 -0
  706. package/templates/nova/tests/cases/components/navigation/Pagination.cases.tsx +25 -0
  707. package/templates/nova/tests/cases/components/navigation/RecoveryLinks.cases.tsx +16 -0
  708. package/templates/nova/tests/cases/components/navigation/SocialLinks.cases.tsx +18 -0
  709. package/templates/nova/tests/cases/components/navigation/blocks/SocialLinks.cases.tsx +17 -0
  710. package/templates/nova/tests/cases/components/overlays/AccountMenu.cases.tsx +28 -0
  711. package/templates/nova/tests/cases/components/overlays/CartDrawer.cases.tsx +30 -0
  712. package/templates/nova/tests/cases/components/overlays/ConfirmationDialog.cases.tsx +16 -0
  713. package/templates/nova/tests/cases/components/overlays/CookieConsent.cases.tsx +23 -0
  714. package/templates/nova/tests/cases/components/overlays/CookiePreferences.cases.tsx +16 -0
  715. package/templates/nova/tests/cases/components/overlays/Drawer.cases.tsx +52 -0
  716. package/templates/nova/tests/cases/components/overlays/MarketSelector.cases.tsx +82 -0
  717. package/templates/nova/tests/cases/components/overlays/Menu.cases.tsx +27 -0
  718. package/templates/nova/tests/cases/components/overlays/MobileNavDrawer.cases.tsx +32 -0
  719. package/templates/nova/tests/cases/components/overlays/Modal.cases.tsx +71 -0
  720. package/templates/nova/tests/cases/components/overlays/Popover.cases.tsx +50 -0
  721. package/templates/nova/tests/cases/components/overlays/PromoPopup.cases.tsx +19 -0
  722. package/templates/nova/tests/cases/components/overlays/QuickView.cases.tsx +18 -0
  723. package/templates/nova/tests/cases/components/overlays/SearchModal.cases.tsx +60 -0
  724. package/templates/nova/tests/cases/components/overlays/Sheet.cases.tsx +32 -0
  725. package/templates/nova/tests/cases/components/overlays/SizeGuide.cases.tsx +27 -0
  726. package/templates/nova/tests/cases/components/overlays/Toast.cases.tsx +48 -0
  727. package/templates/nova/tests/cases/components/overlays/Tooltip.cases.tsx +33 -0
  728. package/templates/nova/tests/cases/components/states/Alert.cases.tsx +27 -0
  729. package/templates/nova/tests/cases/components/states/EditorFrame.cases.tsx +36 -0
  730. package/templates/nova/tests/cases/components/states/Empty.cases.tsx +30 -0
  731. package/templates/nova/tests/cases/components/states/Error.cases.tsx +38 -0
  732. package/templates/nova/tests/cases/components/states/Loading.cases.tsx +15 -0
  733. package/templates/nova/tests/cases/components/states/MediaState.cases.tsx +29 -0
  734. package/templates/nova/tests/cases/components/states/SkeletonLayout.cases.tsx +20 -0
  735. package/templates/nova/tests/cases/components/states/Unavailable.cases.tsx +16 -0
  736. package/templates/nova/tests/cases/components/states/media/MediaError.cases.tsx +18 -0
  737. package/templates/nova/tests/cases/components/states/media/MediaPlaceholder.cases.tsx +87 -0
  738. package/templates/nova/tests/cases/sections/Overview.cases.tsx +8 -0
  739. package/templates/nova/tests/cases/sections/commerce/collection-list/CollectionList.cases.tsx +46 -0
  740. package/templates/nova/tests/cases/sections/commerce/collection-list/blocks/CollectionItem.cases.tsx +18 -0
  741. package/templates/nova/tests/cases/sections/commerce/featured-collection/FeaturedCollection.cases.tsx +46 -0
  742. package/templates/nova/tests/cases/sections/commerce/featured-product/FeaturedProduct.cases.tsx +40 -0
  743. package/templates/nova/tests/cases/sections/commerce/main-collection/MainCollection.cases.tsx +81 -0
  744. package/templates/nova/tests/cases/sections/commerce/product-details/ProductDetails.cases.tsx +110 -0
  745. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/AddToCartBlock.cases.tsx +21 -0
  746. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductDescription.cases.tsx +17 -0
  747. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductInventory.cases.tsx +17 -0
  748. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductPrice.cases.tsx +17 -0
  749. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductSku.cases.tsx +17 -0
  750. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/ProductTitle.cases.tsx +17 -0
  751. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/Quantity.cases.tsx +20 -0
  752. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/Tab.cases.tsx +29 -0
  753. package/templates/nova/tests/cases/sections/commerce/product-details/blocks/VariantPickerBlock.cases.tsx +32 -0
  754. package/templates/nova/tests/cases/sections/commerce/product-grid/ProductGrid.cases.tsx +48 -0
  755. package/templates/nova/tests/cases/sections/commerce/product-recommendations/ProductRecommendations.cases.tsx +27 -0
  756. package/templates/nova/tests/cases/sections/commerce/recently-viewed/RecentlyViewed.cases.tsx +36 -0
  757. package/templates/nova/tests/cases/sections/content/article-body/ArticleBody.cases.tsx +32 -0
  758. package/templates/nova/tests/cases/sections/content/before-after/BeforeAfter.cases.tsx +40 -0
  759. package/templates/nova/tests/cases/sections/content/before-after/blocks/Comparison.cases.tsx +17 -0
  760. package/templates/nova/tests/cases/sections/content/blog-posts/BlogPosts.cases.tsx +47 -0
  761. package/templates/nova/tests/cases/sections/content/collection-links/CollectionLinks.cases.tsx +36 -0
  762. package/templates/nova/tests/cases/sections/content/faq/Faq.cases.tsx +38 -0
  763. package/templates/nova/tests/cases/sections/content/faq/blocks/FaqItem.cases.tsx +17 -0
  764. package/templates/nova/tests/cases/sections/content/feature-grid-blocks/FeatureGridBlocks.cases.tsx +31 -0
  765. package/templates/nova/tests/cases/sections/content/feature-highlights/FeatureHighlights.cases.tsx +26 -0
  766. package/templates/nova/tests/cases/sections/content/image-gallery/ImageGallery.cases.tsx +31 -0
  767. package/templates/nova/tests/cases/sections/content/image-with-text/ImageWithText.cases.tsx +35 -0
  768. package/templates/nova/tests/cases/sections/content/multicolumn/Multicolumn.cases.tsx +32 -0
  769. package/templates/nova/tests/cases/sections/content/page-body/PageBody.cases.tsx +31 -0
  770. package/templates/nova/tests/cases/sections/content/rich-text/RichTextSection.cases.tsx +33 -0
  771. package/templates/nova/tests/cases/sections/content/team-founders/TeamFounders.cases.tsx +31 -0
  772. package/templates/nova/tests/cases/sections/content/team-founders/blocks/Person.cases.tsx +18 -0
  773. package/templates/nova/tests/cases/sections/content/timeline/Timeline.cases.tsx +27 -0
  774. package/templates/nova/tests/cases/sections/content/timeline/blocks/Step.cases.tsx +17 -0
  775. package/templates/nova/tests/cases/sections/layout/announcement-bar/AnnouncementBar.cases.tsx +119 -0
  776. package/templates/nova/tests/cases/sections/layout/breadcrumbs/Breadcrumbs.cases.tsx +73 -0
  777. package/templates/nova/tests/cases/sections/layout/divider/Divider.cases.tsx +81 -0
  778. package/templates/nova/tests/cases/sections/layout/footer/Footer.cases.tsx +116 -0
  779. package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterBrand.cases.tsx +27 -0
  780. package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterMenu.cases.tsx +22 -0
  781. package/templates/nova/tests/cases/sections/layout/footer/blocks/FooterText.cases.tsx +20 -0
  782. package/templates/nova/tests/cases/sections/layout/group/Group.cases.tsx +185 -0
  783. package/templates/nova/tests/cases/sections/layout/group/blocks/Accordion.cases.tsx +17 -0
  784. package/templates/nova/tests/cases/sections/layout/group/blocks/JumboText.cases.tsx +17 -0
  785. package/templates/nova/tests/cases/sections/layout/group/blocks/Spacer.cases.tsx +33 -0
  786. package/templates/nova/tests/cases/sections/layout/header/Header.cases.tsx +277 -0
  787. package/templates/nova/tests/cases/sections/marketing/collection-banner/CollectionBanner.cases.tsx +62 -0
  788. package/templates/nova/tests/cases/sections/marketing/hero/Hero.cases.tsx +138 -0
  789. package/templates/nova/tests/cases/sections/marketing/marquee/Marquee.cases.tsx +63 -0
  790. package/templates/nova/tests/cases/sections/marketing/promo-banner/PromoBanner.cases.tsx +87 -0
  791. package/templates/nova/tests/cases/sections/marketing/slideshow/Slideshow.cases.tsx +113 -0
  792. package/templates/nova/tests/cases/sections/marketing/slideshow/blocks/Slide.cases.tsx +26 -0
  793. package/templates/nova/tests/cases/sections/marketing/video-hero/VideoHero.cases.tsx +72 -0
  794. package/templates/nova/tests/cases/sections/trust/comparison-table/ComparisonTableSection.cases.tsx +42 -0
  795. package/templates/nova/tests/cases/sections/trust/comparison-table/blocks/CompareItem.cases.tsx +17 -0
  796. package/templates/nova/tests/cases/sections/trust/logo-list/LogoList.cases.tsx +29 -0
  797. package/templates/nova/tests/cases/sections/trust/logo-list/blocks/Logo.cases.tsx +17 -0
  798. package/templates/nova/tests/cases/sections/trust/product-reviews/ProductReviews.cases.tsx +51 -0
  799. package/templates/nova/tests/cases/sections/trust/shop-the-look/ShopTheLook.cases.tsx +51 -0
  800. package/templates/nova/tests/cases/sections/trust/shop-the-look/blocks/Hotspot.cases.tsx +17 -0
  801. package/templates/nova/tests/cases/sections/trust/testimonials/Testimonials.cases.tsx +61 -0
  802. package/templates/nova/tests/cases/sections/trust/testimonials/blocks/Testimonial.cases.tsx +17 -0
  803. package/templates/nova/tests/cases/sections/trust/trust-badges/TrustBadges.cases.tsx +30 -0
  804. package/templates/nova/tests/cases/sections/trust/ugc-gallery/UgcGallery.cases.tsx +32 -0
  805. package/templates/nova/tests/cases/sections/trust/ugc-gallery/blocks/UgcImage.cases.tsx +17 -0
  806. package/templates/nova/tests/cases/sections/utility/account/Account.cases.tsx +44 -0
  807. package/templates/nova/tests/cases/sections/utility/cart-items/CartItems.cases.tsx +43 -0
  808. package/templates/nova/tests/cases/sections/utility/cart-items/blocks/CartBadge.cases.tsx +16 -0
  809. package/templates/nova/tests/cases/sections/utility/contact-form/ContactFormSection.cases.tsx +52 -0
  810. package/templates/nova/tests/cases/sections/utility/contact-form/blocks/ContactDetail.cases.tsx +17 -0
  811. package/templates/nova/tests/cases/sections/utility/newsletter/NewsletterSection.cases.tsx +109 -0
  812. package/templates/nova/tests/cases/sections/utility/not-found/NotFound.cases.tsx +28 -0
  813. package/templates/nova/tests/cases/sections/utility/policy-page/PolicyPage.cases.tsx +29 -0
  814. package/templates/nova/tests/cases/sections/utility/search-results/SearchResultsSection.cases.tsx +39 -0
  815. package/templates/nova/tests/cases/sections/utility/store-locator/StoreLocator.cases.tsx +40 -0
  816. package/templates/nova/tests/cases/styles/Borders.cases.tsx +10 -0
  817. package/templates/nova/tests/cases/styles/Breakpoints.cases.tsx +10 -0
  818. package/templates/nova/tests/cases/styles/Colors.cases.tsx +10 -0
  819. package/templates/nova/tests/cases/styles/Elevation.cases.tsx +10 -0
  820. package/templates/nova/tests/cases/styles/Icon.cases.tsx +23 -0
  821. package/templates/nova/tests/cases/styles/Layout.cases.tsx +8 -0
  822. package/templates/nova/tests/cases/styles/Motion.cases.tsx +10 -0
  823. package/templates/nova/tests/cases/styles/Overview.cases.tsx +8 -0
  824. package/templates/nova/tests/cases/styles/Radius.cases.tsx +10 -0
  825. package/templates/nova/tests/cases/styles/SharedThemeSettings.cases.tsx +19 -0
  826. package/templates/nova/tests/cases/styles/Spacing.cases.tsx +10 -0
  827. package/templates/nova/tests/cases/styles/Typography.cases.tsx +10 -0
  828. package/templates/nova/tests/cases.test.tsx +38 -0
  829. package/templates/nova/tests/components/PromoPopup.test.ts +21 -0
  830. package/templates/nova/tests/components/RichText.test.tsx +65 -0
  831. package/templates/nova/tests/components/formatDate.test.ts +10 -0
  832. package/templates/nova/tests/contract-locks.test.ts +23 -0
  833. package/templates/nova/tests/contracts.test.ts +153 -0
  834. package/templates/nova/tests/cookie-consent-privacy.test.tsx +128 -0
  835. package/templates/nova/tests/empty-store.test.tsx +81 -0
  836. package/templates/nova/tests/fixtures/commerce.ts +128 -0
  837. package/templates/nova/tests/fixtures/content.ts +58 -0
  838. package/templates/nova/tests/fixtures/index.ts +8 -0
  839. package/templates/nova/tests/fixtures/merch.ts +161 -0
  840. package/templates/nova/tests/fixtures/navigation.ts +125 -0
  841. package/templates/nova/tests/fixtures/promotion.ts +43 -0
  842. package/templates/nova/tests/fixtures/sample-store.json +1674 -0
  843. package/templates/nova/tests/fixtures/shop.ts +88 -0
  844. package/templates/nova/tests/fixtures/stress.ts +21 -0
  845. package/templates/nova/tests/inert.test.tsx +285 -0
  846. package/templates/nova/tests/line-add-friend.test.tsx +108 -0
  847. package/templates/nova/tests/localized-copy.test.ts +330 -0
  848. package/templates/nova/tests/product-template-blocks.test.ts +28 -0
  849. package/templates/nova/tests/rewards-launcher.test.tsx +401 -0
  850. package/templates/nova/tests/schema.test.ts +65 -0
  851. package/templates/nova/tests/schema.typetest.ts +7 -0
  852. package/templates/nova/tests/section-groups.test.ts +189 -0
  853. package/templates/nova/tests/sections/Group.test.tsx +37 -0
  854. package/templates/nova/tests/setup.ts +30 -0
  855. package/templates/nova/tests/starter.test.ts +56 -0
  856. package/templates/nova/tests/storefront-click-path.test.tsx +157 -0
  857. package/templates/nova/tests/styles/button-brand.test.ts +24 -0
  858. package/templates/nova/tests/styles/theme.test.ts +43 -0
  859. package/templates/nova/tests/styles/tokens.test.ts +35 -0
  860. package/templates/nova/tests/support/abi-ctx.ts +136 -0
  861. package/templates/nova/tests/support/block-stories.tsx +13 -0
  862. package/templates/nova/tests/support/case.tsx +102 -0
  863. package/templates/nova/tests/support/contracts.ts +30 -0
  864. package/templates/nova/tests/support/foundation-pages.tsx +260 -0
  865. package/templates/nova/tests/support/ia-pages.tsx +154 -0
  866. package/templates/nova/tests/support/listing-demo.tsx +29 -0
  867. package/templates/nova/tests/support/presets.ts +14 -0
  868. package/templates/nova/tests/support/sections.ts +36 -0
  869. package/templates/nova/tests/support/story-content.ts +321 -0
  870. package/templates/nova/tests/support/story-helpers.tsx +37 -0
  871. package/templates/nova/tests/support/with-nova.tsx +26 -0
  872. package/templates/nova/tests/theme-config.test.ts +82 -0
  873. package/templates/nova/tests/theme-integrity.test.ts +315 -0
  874. package/templates/nova/theme.config.ts +134 -0
  875. package/templates/nova/theme.manifest.json +17495 -0
  876. package/templates/nova/tsconfig.json +19 -0
  877. package/templates/nova/vite.abi-ssr.config.ts +37 -0
  878. package/templates/nova/vite.abi.config.ts +26 -0
  879. package/templates/nova/vitest.config.ts +20 -0
@@ -0,0 +1,29 @@
1
+ import { defineSchema, field, type SettingsOf } from '@tanqory/theme-kit'
2
+
3
+ /**
4
+ * Nova 13 · 9 promotion popup — NEW global settings (theme settings, not a
5
+ * section). Off for every store by default. Visual settings (preset,
6
+ * placement, scheme, image, copy) are kept apart from behaviour (delay,
7
+ * frequency): never before consent is answered, max once per session, never
8
+ * on cart / checkout. popupFrequency is stored locally only with consent
9
+ * (Preferences = DATA REQUIREMENT). Every other setting depends on
10
+ * popupEnabled.
11
+ */
12
+ export const promotionPopupSchema = defineSchema(
13
+ { kind: 'global', name: 'popup', family: '13' },
14
+ {
15
+ popupEnabled: field.boolean(false),
16
+ popupPreset: field.select(['popup-newsletter', 'popup-offer', 'popup-announce'], 'popup-offer'),
17
+ popupPlacement: field.select(['center', 'side'], 'center'),
18
+ popupScheme: field.select(['surface', 'primary'], 'surface'),
19
+ popupImage: field.resource('image'),
20
+ popupHeading: field.text(''),
21
+ popupBody: field.richtext(''),
22
+ popupCtaLabel: field.text(''),
23
+ popupCtaLink: field.url(''),
24
+ popupDismissLabel: field.text('No thanks'),
25
+ popupDelay: field.select(['10s', '30s', 'exit-intent'], '30s'),
26
+ popupFrequency: field.select(['session', '7d', '30d'], '7d'),
27
+ },
28
+ )
29
+ export type PromotionPopupSettings = SettingsOf<typeof promotionPopupSchema>
@@ -0,0 +1,23 @@
1
+ {
2
+ "badgeStyle": "filled",
3
+ "buttonBorder": "default",
4
+ "buttonRadius": "medium",
5
+ "buttonTextStyle": "default",
6
+ "cardRadius": "medium",
7
+ "colorPrimary": "",
8
+ "enableCartDrawer": true,
9
+ "enableSearchModal": true,
10
+ "enableSpaNavigation": true,
11
+ "headingWeight": "medium",
12
+ "iconStyle": "outline",
13
+ "inputRadius": "medium",
14
+ "locale": "",
15
+ "logo": "",
16
+ "motion": "standard",
17
+ "pageWidth": "standard",
18
+ "sectionSpacing": "large",
19
+ "shopName": "",
20
+ "typeScale": "default",
21
+ "colorScheme": "light",
22
+ "badgeShape": "rounded"
23
+ }
@@ -0,0 +1,231 @@
1
+ import { defineSettings, type SettingSpec } from '@tanqory/theme-kit'
2
+ import { THEME_DEFAULTS, THEME_LEGACY, THEME_OPTIONS, type ThemeEnum } from '../src/styles/theme'
3
+ import legacySettings from './legacy-settings.json'
4
+ import { LEGACY_ENUM_VALUES } from '../lib/legacy-settings'
5
+
6
+ /** A theme setting: a control plus the editor group it sits in. */
7
+ type ThemeSettingSpec = SettingSpec & { group: string; visible_if?: string }
8
+
9
+ /**
10
+ * Theme settings schema — everything a merchant configures theme-wide (as
11
+ * opposed to per-section `attributes`). The source for the editor's Theme
12
+ * panel and `theme.manifest.json`; `config/settings.json` holds the values.
13
+ *
14
+ * The design-system settings are the new UI's global settings
15
+ * (`ui/styles/theme.ts`): the 14 enums of THEME_OPTIONS plus `colorPrimary`.
16
+ * Their option ids and defaults are read from there, never restated, so this
17
+ * schema cannot offer a value `resolveTheme()` does not understand. Labels are
18
+ * the editor's words for each id.
19
+ *
20
+ * A saved value that is no longer an option (THEME_LEGACY, e.g. the old
21
+ * `sectionSpacing: medium`) is left out of the picker; `resolveTheme()` still
22
+ * renders it as the rung it maps to.
23
+ *
24
+ * The rest are runtime keys the storefront shell still reads: logo, shop name,
25
+ * interface language and three feature switches.
26
+ */
27
+
28
+ /** Editor labels per option id. An id without one falls back to the id itself. */
29
+ const LABELS: { [K in keyof ThemeEnum]: { label: string; group: string; info?: string; options: Record<ThemeEnum[K], string> } } = {
30
+ colorScheme: {
31
+ group: 'Colour',
32
+ label: 'Colour scheme',
33
+ options: { light: 'Light', dark: 'Dark' },
34
+ info: 'Dark switches every surface, text and border to the dark value set. The brand colour derives its dark variants.',
35
+ },
36
+ typeScale: {
37
+ group: 'Typography',
38
+ label: 'Type scale',
39
+ options: { compact: 'Compact', default: 'Default', large: 'Large' },
40
+ info: 'Scales headings; body text is unchanged.',
41
+ },
42
+ headingWeight: {
43
+ group: 'Typography',
44
+ label: 'Heading weight',
45
+ options: { regular: 'Regular', medium: 'Medium', semibold: 'Semibold' },
46
+ },
47
+ buttonTextStyle: {
48
+ group: 'Typography',
49
+ label: 'Button text',
50
+ options: { default: 'Default', uppercase: 'Uppercase' },
51
+ },
52
+ pageWidth: {
53
+ group: 'Layout',
54
+ label: 'Page width',
55
+ options: { standard: 'Standard (1280)', wide: 'Wide (1536)' },
56
+ },
57
+ sectionSpacing: {
58
+ group: 'Layout',
59
+ label: 'Section spacing',
60
+ options: { small: 'Small', large: 'Large', xlarge: 'Extra large' },
61
+ info: 'Scales the space above and below every section.',
62
+ },
63
+ buttonRadius: {
64
+ group: 'Components',
65
+ label: 'Button corners',
66
+ options: { none: 'Square', small: 'Slightly rounded', medium: 'Rounded', pill: 'Pill' },
67
+ },
68
+ buttonBorder: {
69
+ group: 'Components',
70
+ label: 'Secondary button border',
71
+ options: { default: 'Default', strong: 'Strong' },
72
+ },
73
+ inputRadius: {
74
+ group: 'Components',
75
+ label: 'Input corners',
76
+ options: { none: 'Square', small: 'Slightly rounded', medium: 'Rounded', pill: 'Pill' },
77
+ },
78
+ cardRadius: {
79
+ group: 'Components',
80
+ label: 'Card corners',
81
+ options: { none: 'Square', small: 'Slightly rounded', medium: 'Rounded', soft: 'Soft' },
82
+ info: 'Also rounds product and collection images.',
83
+ },
84
+ badgeStyle: {
85
+ group: 'Components',
86
+ label: 'Badge style',
87
+ options: { filled: 'Filled', outline: 'Outline' },
88
+ },
89
+ badgeShape: {
90
+ group: 'Components',
91
+ label: 'Badge shape',
92
+ options: { square: 'Square', rounded: 'Rounded', pill: 'Pill' },
93
+ },
94
+ iconStyle: {
95
+ group: 'Components',
96
+ label: 'Icon style',
97
+ options: { outline: 'Outline', filled: 'Filled' },
98
+ },
99
+ motion: {
100
+ group: 'Components',
101
+ label: 'Motion',
102
+ options: { standard: 'Standard', reduced: 'Reduced' },
103
+ info: 'Reduced-motion preferences always win regardless of this setting.',
104
+ },
105
+ }
106
+
107
+ /** One THEME_OPTIONS enum as a select, ids + default from ui/styles/theme.ts. */
108
+ function themeEnum<K extends keyof ThemeEnum>(key: K): ThemeSettingSpec {
109
+ const meta = LABELS[key]
110
+ const labels = meta.options as Record<string, string>
111
+ const legacy = THEME_LEGACY[key] ?? {}
112
+ return {
113
+ type: 'select',
114
+ group: meta.group,
115
+ label: meta.label,
116
+ default: THEME_DEFAULTS[key],
117
+ options: [
118
+ ...(THEME_OPTIONS[key] as readonly string[]).filter((value) => !(value in legacy)).map((value) => ({ value, label: labels[value] ?? value })),
119
+ // Values stores saved under Nova v1/v2 that the redesign retired: still accepted (so saved content
120
+ // validates and publishes), rendered as their nearest rung (lib/legacy-settings.ts), labelled legacy.
121
+ ...[...new Set([...Object.keys(legacy), ...Object.keys(LEGACY_ENUM_VALUES[key] ?? {})])]
122
+ .filter((value) => !(THEME_OPTIONS[key] as readonly string[]).filter((v) => !(v in legacy)).includes(value))
123
+ .map((value) => ({ value, label: `${labels[value] ?? value} (legacy)` })),
124
+ ],
125
+ ...(meta.info ? { info: meta.info } : {}),
126
+ }
127
+ }
128
+
129
+ const schema: Record<string, ThemeSettingSpec> = {
130
+ // ── Brand ────────────────────────────────────────────────────────────────
131
+ logo: {
132
+ type: 'image',
133
+ group: 'Brand',
134
+ label: 'Logo',
135
+ default: '',
136
+ info: 'Shown in the header. Leave empty to use the logo from Settings → Brand.',
137
+ },
138
+ shopName: {
139
+ type: 'text',
140
+ group: 'Brand',
141
+ label: 'Shop name',
142
+ default: '',
143
+ placeholder: 'Store name',
144
+ info: 'Shown in the header when there is no logo, and in the browser tab. Leave empty to use the store name.',
145
+ },
146
+ // Applied by browser-entry.tsx / prerender.tsx through app/platform/locale.ts: the
147
+ // string map (locales/<code>.json) the theme's fixed interface words use
148
+ // before a visitor picks a language. An AI build sets it to its brief's language.
149
+ locale: {
150
+ type: 'select',
151
+ group: 'Brand',
152
+ label: 'Theme language',
153
+ default: '',
154
+ options: [
155
+ { value: '', label: 'English' },
156
+ { value: 'th', label: 'ไทย (Thai)' },
157
+ ],
158
+ info: 'The language of the theme’s own words — account menu, contact form, cart and policy headings. Your own text is never changed.',
159
+ },
160
+
161
+ // ── Colour ───────────────────────────────────────────────────────────────
162
+ // Only Primary is the merchant's; '' = not set → Settings → Brand, else the
163
+ // token default. A non-empty default would be written on every store.
164
+ colorPrimary: {
165
+ type: 'color',
166
+ group: 'Colour',
167
+ label: 'Primary (brand)',
168
+ default: THEME_DEFAULTS.colorPrimary,
169
+ info: 'Buttons, focus ring, selected states and link hover. Empty = the primary colour from Settings → Brand. The label colour on it is chosen for AA contrast automatically.',
170
+ },
171
+ colorScheme: themeEnum('colorScheme'),
172
+
173
+ // ── Typography ───────────────────────────────────────────────────────────
174
+ typeScale: themeEnum('typeScale'),
175
+ headingWeight: themeEnum('headingWeight'),
176
+ buttonTextStyle: themeEnum('buttonTextStyle'),
177
+
178
+ // ── Layout ───────────────────────────────────────────────────────────────
179
+ pageWidth: themeEnum('pageWidth'),
180
+ sectionSpacing: themeEnum('sectionSpacing'),
181
+
182
+ // ── Components ───────────────────────────────────────────────────────────
183
+ buttonRadius: themeEnum('buttonRadius'),
184
+ buttonBorder: themeEnum('buttonBorder'),
185
+ inputRadius: themeEnum('inputRadius'),
186
+ cardRadius: themeEnum('cardRadius'),
187
+ badgeStyle: themeEnum('badgeStyle'),
188
+ badgeShape: themeEnum('badgeShape'),
189
+ iconStyle: themeEnum('iconStyle'),
190
+ motion: themeEnum('motion'),
191
+
192
+ // ── Runtime switches ─────────────────────────────────────────────────────
193
+ enableSpaNavigation: {
194
+ type: 'boolean',
195
+ group: 'Header',
196
+ label: 'Instant page transitions (SPA)',
197
+ default: true,
198
+ },
199
+ enableSearchModal: {
200
+ type: 'boolean',
201
+ group: 'Search',
202
+ label: 'Enable search modal',
203
+ default: true,
204
+ },
205
+ enableCartDrawer: {
206
+ type: 'boolean',
207
+ group: 'Cart',
208
+ label: 'Enable cart drawer',
209
+ default: true,
210
+ },
211
+ }
212
+
213
+ /**
214
+ * Global settings Nova v1/v2 offered and the redesign does not show (config/legacy-settings.json,
215
+ * append-only). They stay DECLARED — hidden, no default — so a store's saved settings and what the
216
+ * AI store builder writes (`headerMenuHandle`, `footerHelpMenuHandle`, `accent`, `colorBrand`,
217
+ * `fontHeading`, `fontBody`, `footerTagline` …) still validate and publish. The menu and colour keys
218
+ * are honoured at render (lib/legacy-settings.ts); the rest are accepted and have no effect.
219
+ */
220
+ for (const [key, spec] of Object.entries(legacySettings as Record<string, Omit<ThemeSettingSpec, 'group'> & { label?: string }>)) {
221
+ if (key in schema) continue
222
+ schema[key] = {
223
+ ...spec,
224
+ group: 'Legacy',
225
+ label: `${spec.label ?? key} (legacy)`,
226
+ info: 'Legacy setting: saved values keep working; not offered for new content.',
227
+ visible_if: '{{ false }}',
228
+ }
229
+ }
230
+
231
+ export default defineSettings(schema)
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Nova v3's Theme ABI v2 serve entry (theme-abi-v2.md §1): `render(request, ctx)` + `manifest`.
3
+ * Built into ONE file, `server/entry.mjs`, with no imports (scripts/abi/build.mjs, and the
4
+ * platform's own bundler from the same sources).
5
+ *
6
+ * The render itself is the kit's (`createRender` from @tanqory/theme-kit/server): routing from
7
+ * `ctx.route`, data from `ctx.data`, headers, cache hints, SEO head and the hydration state. Nova
8
+ * adds only what is Nova's: the settings and menus older Nova stores still carry
9
+ * (lib/legacy-settings.ts) and Settings › Brand colour as the default brand colour (D-3).
10
+ */
11
+ import type { ThemeManifest } from '@tanqory/theme-abi'
12
+ import { createRender } from '@tanqory/theme-kit/server'
13
+ import { bundle } from './lib/bundle'
14
+ import { applyLegacyMenus, effectiveSettings, withBrandColour } from './lib/legacy-settings'
15
+ import manifestJson from './lib/abi/generated/manifest.json'
16
+
17
+ /** Equal to manifest.json bytes minus the trailing LF after JCS (S3); the build writes both from one object. */
18
+ export const manifest = manifestJson as unknown as ThemeManifest
19
+
20
+ export const render = createRender({
21
+ manifest,
22
+ bundle,
23
+ effectiveSettings,
24
+ transformNodes: applyLegacyMenus,
25
+ // Settings › Brand colour is the theme's brand colour until the theme sets its own (D-3).
26
+ shopSettings: (settings, shop) => withBrandColour(settings, shop.brand?.colors?.primary?.[0]?.background),
27
+ })
@@ -0,0 +1,39 @@
1
+ {
2
+ "type": "footer",
3
+ "name": "Footer",
4
+ "sections": [
5
+ {
6
+ "type": "footer",
7
+ "id": "footer",
8
+ "settings": {},
9
+ "blocks": [
10
+ {
11
+ "type": "footer-brand",
12
+ "id": "footer-brand",
13
+ "settings": {}
14
+ },
15
+ {
16
+ "type": "footer-menu",
17
+ "id": "footer-menu-shop",
18
+ "settings": {
19
+ "menu": "footer-shop"
20
+ }
21
+ },
22
+ {
23
+ "type": "footer-menu",
24
+ "id": "footer-menu-help",
25
+ "settings": {
26
+ "menu": "footer-help"
27
+ }
28
+ },
29
+ {
30
+ "type": "footer-menu",
31
+ "id": "footer-menu-company",
32
+ "settings": {
33
+ "menu": "footer-company"
34
+ }
35
+ }
36
+ ]
37
+ }
38
+ ]
39
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "type": "header",
3
+ "name": "Header",
4
+ "sections": [
5
+ {
6
+ "type": "header",
7
+ "id": "header",
8
+ "settings": {}
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,41 @@
1
+ /* Theme ABI v2 platform hooks (lib/abi). The section wrapper is layout-neutral; the preview-mode
2
+ * affordances paint only inside the editor canvas, which the platform bridge marks with
3
+ * body.tq-preview / .tq-preview-selected. */
4
+ .tq-node { display: contents; }
5
+
6
+ body.tq-preview [data-tq-node] { cursor: pointer; }
7
+
8
+ /* Soft hover outline — guides the merchant toward what they can click. */
9
+ body.tq-preview [data-tq-node]:hover > * {
10
+ outline: 2px dashed rgba(10, 10, 10, 0.35);
11
+ outline-offset: -2px;
12
+ transition: outline-color 120ms ease;
13
+ }
14
+
15
+ /* Solid selection outline (overrides hover when both apply). */
16
+ body.tq-preview [data-tq-node].tq-preview-selected > * {
17
+ outline: 2px solid var(--color-fg);
18
+ outline-offset: -2px;
19
+ }
20
+
21
+ /* Tiny label sticking out the top-left of the selected section so the
22
+ * merchant can confirm WHICH section type they have selected. */
23
+ body.tq-preview [data-tq-node].tq-preview-selected {
24
+ position: relative;
25
+ }
26
+ body.tq-preview [data-tq-node].tq-preview-selected::before {
27
+ content: attr(data-tq-section-type);
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ z-index: var(--z-dropdown);
32
+ background: var(--color-fg);
33
+ color: var(--color-bg);
34
+ font-family: var(--font-mono);
35
+ font-size: 11px;
36
+ letter-spacing: 0.04em;
37
+ text-transform: uppercase;
38
+ padding: 4px 8px;
39
+ pointer-events: none;
40
+ }
41
+ .nv-consent-slot { display: contents; }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What the server and the browser bundles share (lib/bundle.ts, lib/bundle.client.ts): Nova's definition, its
3
+ * settings and schema, its region groups and its locale files. Store-independent — the same for every store an
4
+ * artifact serves — so it may live at module scope (theme-abi-v2.md §1, multi-tenant isolates).
5
+ *
6
+ * The locales are default imports, not an `import.meta.glob`: an eager glob is a namespace import, so the bundler
7
+ * must keep every top-level key of each JSON file as a named export next to its `default`, and each locale file
8
+ * was emitted twice in the browser bundle. `tests/bundle-split.test.ts` keeps this list equal to `locales/*.json`.
9
+ */
10
+ import type { ThemeBundle } from '@tanqory/theme-kit'
11
+ import theme from '../theme.config'
12
+ import settings from '../config/settings.json'
13
+ import settingsSchema from '../config/settings.schema'
14
+ import ar from '../locales/ar.json'
15
+ import en from '../locales/en.json'
16
+ import th from '../locales/th.json'
17
+ import '../assets/ai-tokens.css'
18
+ import './abi/abi.css'
19
+
20
+ export const LOCALE_FILES: ThemeBundle['locales'] = {
21
+ '../locales/ar.json': { default: ar },
22
+ '../locales/en.json': { default: en },
23
+ '../locales/th.json': { default: th },
24
+ }
25
+
26
+ export const bundleCore: Omit<ThemeBundle, 'templates'> = {
27
+ theme,
28
+ groups: import.meta.glob('../groups/*.json', { eager: true }),
29
+ locales: LOCALE_FILES,
30
+ settings: settings as Record<string, unknown>,
31
+ settingsSchema: settingsSchema as unknown as ThemeBundle['settingsSchema'],
32
+ // AI-generated `sections/*.tsx` are not part of a new-system artifact (storefront-system.ts drops them).
33
+ customSections: {},
34
+ manifest: null,
35
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The theme bundle the browser entry hands `hydrate` (@tanqory/theme-kit/client). The same as lib/bundle.ts
3
+ * except for the templates: the page's content document arrives in the server's `tq-state` block, so the only
4
+ * template the browser reads is `404.json` (a product, collection or page the store does not have renders the
5
+ * not-found section with that template's settings, kit abi/page.ts `notFoundTree`). Shipping the other starter
6
+ * templates cost every shopper about 25 KB of script for nothing.
7
+ */
8
+ import type { ThemeBundle } from '@tanqory/theme-kit'
9
+ import notFound from '../templates/404.json'
10
+ import { bundleCore } from './bundle-core'
11
+
12
+ export const clientBundle: ThemeBundle = {
13
+ ...bundleCore,
14
+ templates: { '../templates/404.json': { default: notFound } },
15
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The theme bundle the kit runtime is handed on the server (@tanqory/theme-kit: createRender) and in the tests:
3
+ * Nova's definition, its starter content and its settings schema (lib/bundle-core.ts), with every template.
4
+ * The browser entry takes lib/bundle.client.ts instead.
5
+ *
6
+ * The v1 catalogue (theme.manifest.json) is deliberately NOT imported: the ABI entries read the
7
+ * generated ABI manifest instead, and the browser bundle needs neither.
8
+ */
9
+ import type { ThemeBundle } from '@tanqory/theme-kit'
10
+ import { bundleCore } from './bundle-core'
11
+
12
+ export const bundle: ThemeBundle = {
13
+ ...bundleCore,
14
+ templates: import.meta.glob('../templates/*.json', { eager: true }),
15
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Global theme settings Nova v3 no longer shows, but still HONOURS: keys that stores saved under
3
+ * Nova v1/v2 and that the AI store builder still writes into `config/settings.json`
4
+ * (the platform AI service docs/ONLINESTORE_INTEGRATION.md §3.5–§3.6).
5
+ *
6
+ * headerMenuHandle → the header's menu, when the header section names none of its own
7
+ * footer{Shop,Help,Company}MenuHandle
8
+ * → the footer-menu column of that slot (`footer-shop` / `footer-help` /
9
+ * `footer-company`, or the block ids `footer-menu-<slot>`)
10
+ * accent / colorBrand → the primary (brand) colour, when `colorPrimary` is not set
11
+ * sectionSpacing / pageWidth values the redesign dropped → the nearest rung (src/styles/theme.ts)
12
+ *
13
+ * Pure functions over the content the page renders. They run on the server AND in the browser
14
+ * (before the data plan, before render), so the menus the page needs are loaded and the hydrated
15
+ * tree is the one the server rendered. A section's OWN setting always wins over the global one.
16
+ * '' and whitespace mean "not set" everywhere here.
17
+ */
18
+ import type { ContentNode } from '@tanqory/theme-kit'
19
+
20
+ type Settings = Readonly<Record<string, unknown>>
21
+
22
+ const set = (v: unknown): v is string => typeof v === 'string' && v.trim() !== ''
23
+ const HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/
24
+
25
+ /** Footer-menu slot → the legacy global setting that names its menu. */
26
+ export const FOOTER_MENU_SLOTS: Readonly<Record<string, string>> = {
27
+ 'footer-shop': 'footerShopMenuHandle',
28
+ 'footer-help': 'footerHelpMenuHandle',
29
+ 'footer-company': 'footerCompanyMenuHandle',
30
+ }
31
+
32
+ /** Which footer slot a footer-menu block fills: its menu handle, else its id (`footer-menu-help`). */
33
+ function footerSlotOf(node: ContentNode): string | null {
34
+ const menu = (node.settings as Record<string, unknown> | undefined)?.menu
35
+ if (set(menu)) return FOOTER_MENU_SLOTS[menu.trim()] ? menu.trim() : null
36
+ const m = /^footer-menu-(shop|help|company)$/.exec(String(node.id ?? ''))
37
+ return m ? `footer-${m[1]}` : null
38
+ }
39
+
40
+ function withSetting(node: ContentNode, key: string, value: string): ContentNode {
41
+ return { ...node, settings: { ...(node.settings ?? {}), [key]: value } }
42
+ }
43
+
44
+ function mapNode(node: ContentNode, settings: Settings): ContentNode {
45
+ let out = node
46
+ const own = (node.settings ?? {}) as Record<string, unknown>
47
+ if (node.type === 'header' && !set(own.menu) && set(settings.headerMenuHandle)) {
48
+ out = withSetting(out, 'menu', settings.headerMenuHandle.trim())
49
+ }
50
+ if (node.type === 'footer-menu') {
51
+ const slot = footerSlotOf(node)
52
+ const key = slot ? FOOTER_MENU_SLOTS[slot] : undefined
53
+ const handle = key ? settings[key] : undefined
54
+ if (set(handle) && handle.trim() !== own.menu) out = withSetting(out, 'menu', handle.trim())
55
+ }
56
+ if (Array.isArray(node.blocks) && node.blocks.length) {
57
+ const blocks = node.blocks.map((b) => mapNode(b, settings))
58
+ if (blocks.some((b, i) => b !== node.blocks![i])) out = { ...out, blocks }
59
+ }
60
+ return out
61
+ }
62
+
63
+ /** The nodes as they render under the store's global settings (legacy menu keys applied). */
64
+ export function applyLegacyMenus(nodes: readonly ContentNode[], settings: Settings): ContentNode[] {
65
+ const out = nodes.map((n) => mapNode(n, settings))
66
+ return out.some((n, i) => n !== nodes[i]) ? out : (nodes as ContentNode[])
67
+ }
68
+
69
+ /** Saved values of the redesign's enums that are no longer options → the rung they render as. */
70
+ export const LEGACY_ENUM_VALUES: Readonly<Record<string, Readonly<Record<string, string>>>> = {
71
+ sectionSpacing: { none: 'small', medium: 'large' },
72
+ pageWidth: { full: 'wide' },
73
+ }
74
+
75
+ /**
76
+ * The global settings the theme renders with: '' = not set, legacy colour keys as the brand
77
+ * colour's fallback, retired enum values mapped to their rung. Never adds a key the store did
78
+ * not set except `colorPrimary` from its legacy sources.
79
+ */
80
+ export function effectiveSettings(settings: Settings): Record<string, unknown> {
81
+ const out: Record<string, unknown> = { ...settings }
82
+ if (!set(out.colorPrimary)) {
83
+ const legacy = [settings.accent, settings.colorBrand].find((v) => set(v) && HEX.test(v.trim()))
84
+ if (typeof legacy === 'string') out.colorPrimary = legacy.trim()
85
+ }
86
+ for (const [key, map] of Object.entries(LEGACY_ENUM_VALUES)) {
87
+ const v = out[key]
88
+ if (typeof v === 'string' && map[v]) out[key] = map[v]
89
+ }
90
+ return out
91
+ }
92
+
93
+ /**
94
+ * Settings › Brand › primary colour as the theme's default brand colour (D-3): used only while the
95
+ * theme's own colour is not set — neither `colorPrimary` nor a legacy `accent` / `colorBrand`
96
+ * (both already folded into `colorPrimary` by `effectiveSettings`). Once the merchant picks a theme
97
+ * colour, that colour wins. A brand colour that is not a hex value is ignored. Returns the same
98
+ * object when nothing changes.
99
+ */
100
+ export function withBrandColour<T extends Record<string, unknown>>(settings: T, brandPrimary: unknown): T {
101
+ if (set(settings.colorPrimary)) return settings
102
+ if (!set(brandPrimary) || !HEX.test(brandPrimary.trim())) return settings
103
+ return { ...settings, colorPrimary: brandPrimary.trim() }
104
+ }