@open-mercato/core 0.7.1-develop.7203.1.9396450b78 → 0.8.0

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 (416) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build.mjs +11 -0
  3. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +23 -0
  4. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  5. package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
  6. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  7. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +2 -1
  8. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  9. package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -2
  10. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  11. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +2 -1
  12. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  13. package/dist/modules/customers/lib/detailHelpers.js +4 -0
  14. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  15. package/dist/modules/customers/lib/todoCompatibility.js +5 -0
  16. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  17. package/dist/modules/design_system/backend/design-system/page.meta.js +3 -2
  18. package/dist/modules/design_system/backend/design-system/page.meta.js.map +2 -2
  19. package/dist/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  20. package/dist/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  21. package/dist/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  22. package/dist/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  23. package/dist/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  24. package/dist/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  25. package/dist/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  26. package/dist/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  27. package/dist/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  28. package/dist/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  29. package/dist/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  30. package/dist/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  31. package/dist/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  32. package/dist/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  33. package/dist/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  34. package/dist/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  35. package/dist/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  36. package/dist/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  37. package/dist/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  38. package/dist/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  39. package/dist/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  40. package/dist/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  41. package/dist/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  42. package/dist/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  43. package/dist/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  44. package/dist/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  45. package/dist/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  46. package/dist/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  47. package/dist/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  48. package/dist/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  49. package/dist/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  50. package/dist/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  51. package/dist/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  52. package/dist/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  53. package/dist/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  54. package/dist/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  55. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js +67 -0
  56. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js.map +7 -0
  57. package/dist/modules/design_system/gallery/assets/github-logo.png +0 -0
  58. package/dist/modules/design_system/gallery/assets/images.d.js +1 -0
  59. package/dist/modules/design_system/gallery/assets/images.d.js.map +7 -0
  60. package/dist/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  61. package/dist/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  62. package/dist/modules/design_system/gallery/assets/marketing-artwork.js +83 -0
  63. package/dist/modules/design_system/gallery/assets/marketing-artwork.js.map +7 -0
  64. package/dist/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  65. package/dist/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  66. package/dist/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  67. package/dist/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  68. package/dist/modules/design_system/gallery/assets/select-google.png +0 -0
  69. package/dist/modules/design_system/gallery/assets/select-poland.png +0 -0
  70. package/dist/modules/design_system/gallery/assets/selection-artwork.js +5 -0
  71. package/dist/modules/design_system/gallery/assets/selection-artwork.js.map +7 -0
  72. package/dist/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  73. package/dist/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  74. package/dist/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  75. package/dist/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  76. package/dist/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  77. package/dist/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  78. package/dist/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  79. package/dist/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  80. package/dist/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  81. package/dist/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  82. package/dist/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  83. package/dist/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  84. package/dist/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  85. package/dist/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  86. package/dist/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  87. package/dist/modules/design_system/gallery/assets/upload-images.js +8 -0
  88. package/dist/modules/design_system/gallery/assets/upload-images.js.map +7 -0
  89. package/dist/modules/design_system/gallery/components/BrandStylePreview.js +74 -0
  90. package/dist/modules/design_system/gallery/components/BrandStylePreview.js.map +7 -0
  91. package/dist/modules/design_system/gallery/components/CodeSnippet.js +31 -31
  92. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +2 -2
  93. package/dist/modules/design_system/gallery/components/ColorPlayground.js +408 -0
  94. package/dist/modules/design_system/gallery/components/ColorPlayground.js.map +7 -0
  95. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js +166 -0
  96. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js.map +7 -0
  97. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js +182 -0
  98. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js.map +7 -0
  99. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js +80 -0
  100. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js.map +7 -0
  101. package/dist/modules/design_system/gallery/components/ComponentIndex.js +42 -0
  102. package/dist/modules/design_system/gallery/components/ComponentIndex.js.map +7 -0
  103. package/dist/modules/design_system/gallery/components/DesignSystemHome.js +69 -0
  104. package/dist/modules/design_system/gallery/components/DesignSystemHome.js.map +7 -0
  105. package/dist/modules/design_system/gallery/components/EntryCard.js +47 -41
  106. package/dist/modules/design_system/gallery/components/EntryCard.js.map +2 -2
  107. package/dist/modules/design_system/gallery/components/EntryHeader.js +68 -0
  108. package/dist/modules/design_system/gallery/components/EntryHeader.js.map +7 -0
  109. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js +19 -0
  110. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js.map +7 -0
  111. package/dist/modules/design_system/gallery/components/FoundationGuide.js +46 -0
  112. package/dist/modules/design_system/gallery/components/FoundationGuide.js.map +7 -0
  113. package/dist/modules/design_system/gallery/components/GalleryLink.js +21 -0
  114. package/dist/modules/design_system/gallery/components/GalleryLink.js.map +7 -0
  115. package/dist/modules/design_system/gallery/components/GalleryNavigation.js +82 -0
  116. package/dist/modules/design_system/gallery/components/GalleryNavigation.js.map +7 -0
  117. package/dist/modules/design_system/gallery/components/GalleryShell.js +147 -67
  118. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +3 -3
  119. package/dist/modules/design_system/gallery/components/IconsBrowser.js +33 -0
  120. package/dist/modules/design_system/gallery/components/IconsBrowser.js.map +7 -0
  121. package/dist/modules/design_system/gallery/components/SourceLibrary.js +93 -0
  122. package/dist/modules/design_system/gallery/components/SourceLibrary.js.map +7 -0
  123. package/dist/modules/design_system/gallery/components/StyleAgents.js +139 -0
  124. package/dist/modules/design_system/gallery/components/StyleAgents.js.map +7 -0
  125. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js +101 -0
  126. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js.map +7 -0
  127. package/dist/modules/design_system/gallery/components/VariantPreview.js +2 -2
  128. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +2 -2
  129. package/dist/modules/design_system/gallery/components/colorPreview.js +58 -0
  130. package/dist/modules/design_system/gallery/components/colorPreview.js.map +7 -0
  131. package/dist/modules/design_system/gallery/components/colorStudio.js +218 -0
  132. package/dist/modules/design_system/gallery/components/colorStudio.js.map +7 -0
  133. package/dist/modules/design_system/gallery/components/styleAgentBranding.js +46 -0
  134. package/dist/modules/design_system/gallery/components/styleAgentBranding.js.map +7 -0
  135. package/dist/modules/design_system/gallery/components/themeTokens.js +32 -0
  136. package/dist/modules/design_system/gallery/components/themeTokens.js.map +7 -0
  137. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js +194 -0
  138. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js.map +7 -0
  139. package/dist/modules/design_system/gallery/demos/ai-product.js +423 -0
  140. package/dist/modules/design_system/gallery/demos/ai-product.js.map +7 -0
  141. package/dist/modules/design_system/gallery/demos/buttons.js +289 -0
  142. package/dist/modules/design_system/gallery/demos/buttons.js.map +7 -0
  143. package/dist/modules/design_system/gallery/demos/cryptocurrency.js +1708 -0
  144. package/dist/modules/design_system/gallery/demos/cryptocurrency.js.map +7 -0
  145. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js +92 -0
  146. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js.map +7 -0
  147. package/dist/modules/design_system/gallery/demos/feeds.js +148 -0
  148. package/dist/modules/design_system/gallery/demos/feeds.js.map +7 -0
  149. package/dist/modules/design_system/gallery/demos/file-upload.js +72 -0
  150. package/dist/modules/design_system/gallery/demos/file-upload.js.map +7 -0
  151. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js +4875 -0
  152. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js.map +7 -0
  153. package/dist/modules/design_system/gallery/demos/foundation-references.js +188 -0
  154. package/dist/modules/design_system/gallery/demos/foundation-references.js.map +7 -0
  155. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js +6 -0
  156. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js.map +7 -0
  157. package/dist/modules/design_system/gallery/demos/key-components.js +127 -0
  158. package/dist/modules/design_system/gallery/demos/key-components.js.map +7 -0
  159. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js +98 -0
  160. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js.map +7 -0
  161. package/dist/modules/design_system/gallery/demos/marketing-controls.js +134 -0
  162. package/dist/modules/design_system/gallery/demos/marketing-controls.js.map +7 -0
  163. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js +195 -0
  164. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js.map +7 -0
  165. package/dist/modules/design_system/gallery/demos/marketing-widgets.js +586 -0
  166. package/dist/modules/design_system/gallery/demos/marketing-widgets.js.map +7 -0
  167. package/dist/modules/design_system/gallery/demos/menu-fixtures.js +40 -0
  168. package/dist/modules/design_system/gallery/demos/menu-fixtures.js.map +7 -0
  169. package/dist/modules/design_system/gallery/demos/menu-sources.js +295 -0
  170. package/dist/modules/design_system/gallery/demos/menu-sources.js.map +7 -0
  171. package/dist/modules/design_system/gallery/demos/menus.js +150 -0
  172. package/dist/modules/design_system/gallery/demos/menus.js.map +7 -0
  173. package/dist/modules/design_system/gallery/demos/rating-reviews.js +53 -0
  174. package/dist/modules/design_system/gallery/demos/rating-reviews.js.map +7 -0
  175. package/dist/modules/design_system/gallery/demos/registered-icons.js +104 -0
  176. package/dist/modules/design_system/gallery/demos/registered-icons.js.map +7 -0
  177. package/dist/modules/design_system/gallery/demos/selection-cards.js +99 -0
  178. package/dist/modules/design_system/gallery/demos/selection-cards.js.map +7 -0
  179. package/dist/modules/design_system/gallery/demos/shell-code.generated.js +189 -0
  180. package/dist/modules/design_system/gallery/demos/shell-code.generated.js.map +7 -0
  181. package/dist/modules/design_system/gallery/demos/shell.js +330 -0
  182. package/dist/modules/design_system/gallery/demos/shell.js.map +7 -0
  183. package/dist/modules/design_system/gallery/demos/source-artwork.js +120 -0
  184. package/dist/modules/design_system/gallery/demos/source-artwork.js.map +7 -0
  185. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js +125 -0
  186. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js.map +7 -0
  187. package/dist/modules/design_system/gallery/demos/source-filters.js +185 -0
  188. package/dist/modules/design_system/gallery/demos/source-filters.js.map +7 -0
  189. package/dist/modules/design_system/gallery/demos/source-icons.js +69 -0
  190. package/dist/modules/design_system/gallery/demos/source-icons.js.map +7 -0
  191. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js +150 -0
  192. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js.map +7 -0
  193. package/dist/modules/design_system/gallery/demos/table-cells.js +181 -0
  194. package/dist/modules/design_system/gallery/demos/table-cells.js.map +7 -0
  195. package/dist/modules/design_system/gallery/demos/text-inputs.js +113 -0
  196. package/dist/modules/design_system/gallery/demos/text-inputs.js.map +7 -0
  197. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js +41 -0
  198. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js.map +7 -0
  199. package/dist/modules/design_system/gallery/demos/time-picker.js +41 -0
  200. package/dist/modules/design_system/gallery/demos/time-picker.js.map +7 -0
  201. package/dist/modules/design_system/gallery/entries/banners.js +123 -60
  202. package/dist/modules/design_system/gallery/entries/banners.js.map +2 -2
  203. package/dist/modules/design_system/gallery/entries/buttons-snippets.js +433 -0
  204. package/dist/modules/design_system/gallery/entries/buttons-snippets.js.map +7 -0
  205. package/dist/modules/design_system/gallery/entries/buttons.js +127 -148
  206. package/dist/modules/design_system/gallery/entries/buttons.js.map +2 -2
  207. package/dist/modules/design_system/gallery/entries/charts.js +532 -186
  208. package/dist/modules/design_system/gallery/entries/charts.js.map +2 -2
  209. package/dist/modules/design_system/gallery/entries/dates.js +191 -3
  210. package/dist/modules/design_system/gallery/entries/dates.js.map +2 -2
  211. package/dist/modules/design_system/gallery/entries/detail.js +118 -77
  212. package/dist/modules/design_system/gallery/entries/detail.js.map +2 -2
  213. package/dist/modules/design_system/gallery/entries/display.js +1449 -240
  214. package/dist/modules/design_system/gallery/entries/display.js.map +2 -2
  215. package/dist/modules/design_system/gallery/entries/feedback.js +1058 -132
  216. package/dist/modules/design_system/gallery/entries/feedback.js.map +2 -2
  217. package/dist/modules/design_system/gallery/entries/filters.js +231 -103
  218. package/dist/modules/design_system/gallery/entries/filters.js.map +2 -2
  219. package/dist/modules/design_system/gallery/entries/foundations.js +368 -57
  220. package/dist/modules/design_system/gallery/entries/foundations.js.map +2 -2
  221. package/dist/modules/design_system/gallery/entries/icons.js +29 -91
  222. package/dist/modules/design_system/gallery/entries/icons.js.map +2 -2
  223. package/dist/modules/design_system/gallery/entries/inputs-snippets.js +154 -0
  224. package/dist/modules/design_system/gallery/entries/inputs-snippets.js.map +7 -0
  225. package/dist/modules/design_system/gallery/entries/inputs.js +2043 -306
  226. package/dist/modules/design_system/gallery/entries/inputs.js.map +2 -2
  227. package/dist/modules/design_system/gallery/entries/library.js +20 -0
  228. package/dist/modules/design_system/gallery/entries/library.js.map +7 -0
  229. package/dist/modules/design_system/gallery/entries/messages.js +121 -103
  230. package/dist/modules/design_system/gallery/entries/messages.js.map +2 -2
  231. package/dist/modules/design_system/gallery/entries/navigation.js +676 -103
  232. package/dist/modules/design_system/gallery/entries/navigation.js.map +2 -2
  233. package/dist/modules/design_system/gallery/entries/notifications.js +131 -78
  234. package/dist/modules/design_system/gallery/entries/notifications.js.map +2 -2
  235. package/dist/modules/design_system/gallery/entries/overlays.js +611 -203
  236. package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
  237. package/dist/modules/design_system/gallery/entries/scaffolding.js +1175 -120
  238. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +2 -2
  239. package/dist/modules/design_system/gallery/entries/schedule.js +79 -54
  240. package/dist/modules/design_system/gallery/entries/schedule.js.map +2 -2
  241. package/dist/modules/design_system/gallery/presentation.js +108 -0
  242. package/dist/modules/design_system/gallery/presentation.js.map +7 -0
  243. package/dist/modules/design_system/gallery/registry.js +8 -5
  244. package/dist/modules/design_system/gallery/registry.js.map +2 -2
  245. package/dist/modules/design_system/gallery/source-library.generated.js +5 -0
  246. package/dist/modules/design_system/gallery/source-library.generated.js.map +7 -0
  247. package/dist/modules/design_system/i18n/de.json +7167 -0
  248. package/dist/modules/design_system/i18n/en.json +7167 -0
  249. package/dist/modules/design_system/i18n/es.json +7167 -0
  250. package/dist/modules/design_system/i18n/ko.json +7167 -0
  251. package/dist/modules/design_system/i18n/pl.json +7167 -0
  252. package/dist/modules/push_notifications/lib/queue.js +1 -31
  253. package/dist/modules/push_notifications/lib/queue.js.map +2 -2
  254. package/jest.config.cjs +1 -0
  255. package/jest.mocks/image.js +1 -0
  256. package/package.json +8 -9
  257. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +50 -0
  258. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
  259. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +2 -1
  260. package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -2
  261. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +2 -1
  262. package/src/modules/customers/lib/detailHelpers.ts +4 -0
  263. package/src/modules/customers/lib/todoCompatibility.ts +10 -0
  264. package/src/modules/design_system/backend/design-system/page.meta.ts +3 -2
  265. package/src/modules/design_system/gallery/assets/README.md +30 -0
  266. package/src/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  267. package/src/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  268. package/src/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  269. package/src/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  270. package/src/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  271. package/src/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  272. package/src/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  273. package/src/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  274. package/src/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  275. package/src/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  276. package/src/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  277. package/src/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  278. package/src/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  279. package/src/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  280. package/src/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  281. package/src/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  282. package/src/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  283. package/src/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  284. package/src/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  285. package/src/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  286. package/src/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  287. package/src/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  288. package/src/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  289. package/src/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  290. package/src/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  291. package/src/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  292. package/src/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  293. package/src/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  294. package/src/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  295. package/src/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  296. package/src/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  297. package/src/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  298. package/src/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  299. package/src/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  300. package/src/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  301. package/src/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  302. package/src/modules/design_system/gallery/assets/cryptocurrency-artwork.ts +63 -0
  303. package/src/modules/design_system/gallery/assets/github-logo.png +0 -0
  304. package/src/modules/design_system/gallery/assets/images.d.ts +9 -0
  305. package/src/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  306. package/src/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  307. package/src/modules/design_system/gallery/assets/marketing-artwork.ts +79 -0
  308. package/src/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  309. package/src/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  310. package/src/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  311. package/src/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  312. package/src/modules/design_system/gallery/assets/select-google.png +0 -0
  313. package/src/modules/design_system/gallery/assets/select-poland.png +0 -0
  314. package/src/modules/design_system/gallery/assets/selection-artwork.ts +1 -0
  315. package/src/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  316. package/src/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  317. package/src/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  318. package/src/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  319. package/src/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  320. package/src/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  321. package/src/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  322. package/src/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  323. package/src/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  324. package/src/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  325. package/src/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  326. package/src/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  327. package/src/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  328. package/src/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  329. package/src/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  330. package/src/modules/design_system/gallery/assets/upload-images.ts +5 -0
  331. package/src/modules/design_system/gallery/components/BrandStylePreview.tsx +94 -0
  332. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +34 -34
  333. package/src/modules/design_system/gallery/components/ColorPlayground.tsx +324 -0
  334. package/src/modules/design_system/gallery/components/ColorStudioCompositions.tsx +96 -0
  335. package/src/modules/design_system/gallery/components/ColorStudioSpecimen.tsx +164 -0
  336. package/src/modules/design_system/gallery/components/CompactEntrySpecimen.tsx +65 -0
  337. package/src/modules/design_system/gallery/components/ComponentIndex.tsx +47 -0
  338. package/src/modules/design_system/gallery/components/DesignSystemHome.tsx +66 -0
  339. package/src/modules/design_system/gallery/components/EntryCard.tsx +52 -37
  340. package/src/modules/design_system/gallery/components/EntryHeader.tsx +80 -0
  341. package/src/modules/design_system/gallery/components/FamilyEntryPreview.tsx +22 -0
  342. package/src/modules/design_system/gallery/components/FoundationGuide.tsx +43 -0
  343. package/src/modules/design_system/gallery/components/GalleryLink.tsx +19 -0
  344. package/src/modules/design_system/gallery/components/GalleryNavigation.tsx +86 -0
  345. package/src/modules/design_system/gallery/components/GalleryShell.tsx +149 -63
  346. package/src/modules/design_system/gallery/components/IconsBrowser.tsx +40 -0
  347. package/src/modules/design_system/gallery/components/SourceLibrary.tsx +90 -0
  348. package/src/modules/design_system/gallery/components/StyleAgents.tsx +115 -0
  349. package/src/modules/design_system/gallery/components/ThemeTokenReference.tsx +76 -0
  350. package/src/modules/design_system/gallery/components/VariantPreview.tsx +2 -2
  351. package/src/modules/design_system/gallery/components/colorPreview.ts +63 -0
  352. package/src/modules/design_system/gallery/components/colorStudio.ts +260 -0
  353. package/src/modules/design_system/gallery/components/styleAgentBranding.ts +42 -0
  354. package/src/modules/design_system/gallery/components/themeTokens.ts +26 -0
  355. package/src/modules/design_system/gallery/demos/ai-product-code.generated.ts +3 -0
  356. package/src/modules/design_system/gallery/demos/ai-product.tsx +188 -0
  357. package/src/modules/design_system/gallery/demos/buttons.tsx +331 -0
  358. package/src/modules/design_system/gallery/demos/cryptocurrency.tsx +1943 -0
  359. package/src/modules/design_system/gallery/demos/feeds-code.generated.ts +4 -0
  360. package/src/modules/design_system/gallery/demos/feeds.tsx +82 -0
  361. package/src/modules/design_system/gallery/demos/file-upload.tsx +61 -0
  362. package/src/modules/design_system/gallery/demos/foundation-reference-data.ts +4872 -0
  363. package/src/modules/design_system/gallery/demos/foundation-references.tsx +140 -0
  364. package/src/modules/design_system/gallery/demos/key-components-code.generated.ts +3 -0
  365. package/src/modules/design_system/gallery/demos/key-components.tsx +106 -0
  366. package/src/modules/design_system/gallery/demos/marketing-controls-code.generated.ts +3 -0
  367. package/src/modules/design_system/gallery/demos/marketing-controls.tsx +92 -0
  368. package/src/modules/design_system/gallery/demos/marketing-widgets-code.generated.ts +3 -0
  369. package/src/modules/design_system/gallery/demos/marketing-widgets.tsx +189 -0
  370. package/src/modules/design_system/gallery/demos/menu-fixtures.tsx +48 -0
  371. package/src/modules/design_system/gallery/demos/menu-sources.ts +5 -0
  372. package/src/modules/design_system/gallery/demos/menus.tsx +122 -0
  373. package/src/modules/design_system/gallery/demos/rating-reviews.tsx +44 -0
  374. package/src/modules/design_system/gallery/demos/registered-icons.tsx +117 -0
  375. package/src/modules/design_system/gallery/demos/selection-cards.tsx +97 -0
  376. package/src/modules/design_system/gallery/demos/shell-code.generated.ts +3 -0
  377. package/src/modules/design_system/gallery/demos/shell.tsx +183 -0
  378. package/src/modules/design_system/gallery/demos/source-artwork.tsx +88 -0
  379. package/src/modules/design_system/gallery/demos/source-filters-code.generated.ts +3 -0
  380. package/src/modules/design_system/gallery/demos/source-filters.tsx +119 -0
  381. package/src/modules/design_system/gallery/demos/source-icons.tsx +62 -0
  382. package/src/modules/design_system/gallery/demos/table-cells-code.generated.ts +3 -0
  383. package/src/modules/design_system/gallery/demos/table-cells.tsx +144 -0
  384. package/src/modules/design_system/gallery/demos/text-inputs.tsx +90 -0
  385. package/src/modules/design_system/gallery/demos/time-picker-code.generated.ts +5 -0
  386. package/src/modules/design_system/gallery/demos/time-picker.tsx +31 -0
  387. package/src/modules/design_system/gallery/entries/banners.tsx +143 -94
  388. package/src/modules/design_system/gallery/entries/buttons-snippets.ts +432 -0
  389. package/src/modules/design_system/gallery/entries/buttons.tsx +125 -196
  390. package/src/modules/design_system/gallery/entries/charts.tsx +545 -233
  391. package/src/modules/design_system/gallery/entries/dates.tsx +171 -3
  392. package/src/modules/design_system/gallery/entries/detail.tsx +142 -115
  393. package/src/modules/design_system/gallery/entries/display.tsx +1691 -319
  394. package/src/modules/design_system/gallery/entries/feedback.tsx +1082 -169
  395. package/src/modules/design_system/gallery/entries/filters.tsx +238 -143
  396. package/src/modules/design_system/gallery/entries/foundations.tsx +404 -61
  397. package/src/modules/design_system/gallery/entries/icons.tsx +31 -117
  398. package/src/modules/design_system/gallery/entries/inputs-snippets.ts +156 -0
  399. package/src/modules/design_system/gallery/entries/inputs.tsx +2146 -503
  400. package/src/modules/design_system/gallery/entries/library.tsx +16 -0
  401. package/src/modules/design_system/gallery/entries/messages.tsx +114 -106
  402. package/src/modules/design_system/gallery/entries/navigation.tsx +782 -192
  403. package/src/modules/design_system/gallery/entries/notifications.tsx +145 -120
  404. package/src/modules/design_system/gallery/entries/overlays.tsx +696 -280
  405. package/src/modules/design_system/gallery/entries/scaffolding.tsx +1192 -158
  406. package/src/modules/design_system/gallery/entries/schedule.tsx +72 -58
  407. package/src/modules/design_system/gallery/presentation.ts +106 -0
  408. package/src/modules/design_system/gallery/registry.ts +8 -6
  409. package/src/modules/design_system/gallery/source-library.generated.ts +2 -0
  410. package/src/modules/design_system/i18n/de.json +7112 -3
  411. package/src/modules/design_system/i18n/en.json +7112 -3
  412. package/src/modules/design_system/i18n/es.json +7112 -3
  413. package/src/modules/design_system/i18n/ko.json +7112 -3
  414. package/src/modules/design_system/i18n/pl.json +7112 -3
  415. package/src/modules/push_notifications/AGENTS.md +9 -5
  416. package/src/modules/push_notifications/lib/queue.ts +15 -44
@@ -1,9 +1,11 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
- import { useSearchParams } from 'next/navigation'
4
+ import { GalleryLink as Link, navigateGallery } from './GalleryLink'
5
+ import { ArrowLeft, Home, BookOpen, Grid2X2, Palette } from 'lucide-react'
6
+ import { useRouter, useSearchParams } from 'next/navigation'
5
7
  import { useT } from '@open-mercato/shared/lib/i18n/context'
6
- import { SectionPage } from '@open-mercato/ui/backend/section-page'
8
+ import { GalleryNavigation } from './GalleryNavigation'
7
9
  import type { SectionNavGroup } from '@open-mercato/ui/backend/section-page'
8
10
  import { SearchInput } from '@open-mercato/ui/primitives/search-input'
9
11
  import { Skeleton } from '@open-mercato/ui/primitives/skeleton'
@@ -13,6 +15,12 @@ import { ErrorMessage } from '@open-mercato/ui/backend/detail'
13
15
  import type { GalleryEntry, GalleryFamily } from '../types'
14
16
  import { GALLERY_BASE_PATH, galleryFamilies } from '../registry'
15
17
  import { EntryCard } from './EntryCard'
18
+ import { DesignSystemHome, DesignSystemPrinciples } from './DesignSystemHome'
19
+ import { StyleAgents } from './StyleAgents'
20
+ import { ComponentIndex } from './ComponentIndex'
21
+ import { FoundationGuide } from './FoundationGuide'
22
+ import { IconsBrowser } from './IconsBrowser'
23
+ import { fullWidthEntryIds } from '../presentation'
16
24
 
17
25
  /** 'buttons' → 'Buttons' — untranslated fallback when a family labelKey has no message. */
18
26
  function familyLabelFallback(id: string): string {
@@ -28,27 +36,77 @@ function matchesQuery(entry: GalleryEntry, query: string): boolean {
28
36
  return needle.includes(entry.id.toLowerCase()) || needle.includes(entry.title.toLowerCase())
29
37
  }
30
38
 
39
+ /** A half-width card with no neighbour before the next full-width card would leave a hole, so it spans the row too. */
40
+ export function resolveFullWidthIds(entryIds: string[], fullWidthIds: ReadonlySet<string>): Set<string> {
41
+ const resolved = new Set(entryIds.filter(id => fullWidthIds.has(id)))
42
+ let unpaired: string | null = null
43
+ for (const id of entryIds) {
44
+ if (resolved.has(id)) {
45
+ if (unpaired) resolved.add(unpaired)
46
+ unpaired = null
47
+ } else {
48
+ unpaired = unpaired ? null : id
49
+ }
50
+ }
51
+ if (unpaired) resolved.add(unpaired)
52
+ return resolved
53
+ }
54
+
55
+ /**
56
+ * The backend shell centres `main` once the viewport outgrows its max width, which would leave the
57
+ * library navigation floating mid-screen. Pull the gallery back by that centring margin so the
58
+ * navigation stays docked next to the application sidebar at every width.
59
+ */
60
+ function useShellDockOffset(rootRef: React.RefObject<HTMLElement | null>): number {
61
+ const [offset, setOffset] = React.useState(0)
62
+ React.useLayoutEffect(() => {
63
+ const main = rootRef.current?.closest('main')
64
+ const column = main?.parentElement
65
+ if (!main || !column || typeof ResizeObserver === 'undefined') return
66
+ const measure = () => setOffset(Math.max(0, Math.round(main.getBoundingClientRect().left - column.getBoundingClientRect().left)))
67
+ measure()
68
+ const observer = new ResizeObserver(measure)
69
+ observer.observe(column)
70
+ return () => observer.disconnect()
71
+ }, [rootRef])
72
+ return offset
73
+ }
74
+
31
75
  type LoadedEntries = Record<string, GalleryEntry[]>
32
76
 
77
+ const componentFamilies = galleryFamilies.filter(family => !['foundations', 'library'].includes(family.id))
78
+
33
79
  export function GalleryShell() {
34
80
  const t = useT()
81
+ const router = useRouter()
35
82
  const searchParams = useSearchParams()
36
83
  const [loaded, setLoaded] = React.useState<LoadedEntries>({})
37
84
  const [failed, setFailed] = React.useState<Record<string, boolean>>({})
38
85
  const [search, setSearch] = React.useState('')
39
86
  const pendingRef = React.useRef(new Set<string>())
87
+ const rootRef = React.useRef<HTMLDivElement>(null)
88
+ const dockOffset = useShellDockOffset(rootRef)
40
89
 
41
90
  const familyParam = searchParams?.get('family') ?? null
42
91
  const entryParam = searchParams?.get('entry') ?? null
92
+ const variantParam = searchParams?.get('variant') ?? null
93
+ const showingPrinciples = searchParams?.get('view') === 'principles' && !familyParam
94
+ const showingComponents = searchParams?.get('view') === 'components' && !familyParam
95
+ const showingStyleAgents = searchParams?.get('view') === 'style-agents' && !familyParam
96
+ const showingHome = !familyParam && !showingPrinciples && !showingComponents && !showingStyleAgents
97
+ const showingFoundations = familyParam === 'foundations'
98
+ const showingAssets = familyParam === 'icons'
99
+ const showingInventory = familyParam === 'library'
100
+ const showingEntry = Boolean(entryParam) && !showingFoundations && !showingAssets && !showingInventory
101
+
43
102
 
44
- // Clicking a family in the section nav exits search mode — otherwise the
45
- // query keeps precedence and family navigation appears dead.
46
103
  React.useEffect(() => {
47
104
  setSearch('')
48
- }, [familyParam])
105
+ }, [familyParam, entryParam, showingPrinciples, showingComponents, showingStyleAgents])
49
106
  const activeFamily: GalleryFamily | undefined =
50
107
  galleryFamilies.find((family) => family.id === familyParam) ?? galleryFamilies[0]
51
108
  const activeFamilyId = activeFamily?.id ?? null
109
+ const activeNavigationPath = showingStyleAgents ? `${GALLERY_BASE_PATH}?view=style-agents` : showingHome ? GALLERY_BASE_PATH : showingPrinciples ? `${GALLERY_BASE_PATH}?view=principles` : showingComponents ? `${GALLERY_BASE_PATH}?view=components` : `${GALLERY_BASE_PATH}?family=${activeFamilyId}${(showingFoundations || showingEntry) && entryParam ? `&entry=${entryParam}` : ''}`
52
110
 
53
111
  const loadFamily = React.useCallback((family: GalleryFamily) => {
54
112
  if (pendingRef.current.has(family.id)) return
@@ -71,36 +129,54 @@ export function GalleryShell() {
71
129
  React.useEffect(() => {
72
130
  if (searching) {
73
131
  for (const family of galleryFamilies) loadFamily(family)
74
- } else if (activeFamily) {
132
+ } else if (showingComponents) {
133
+ for (const family of componentFamilies) loadFamily(family)
134
+ } else if (activeFamily && !showingHome && !showingPrinciples && !showingStyleAgents) {
75
135
  loadFamily(activeFamily)
76
136
  }
77
- }, [searching, activeFamily, loadFamily])
137
+ }, [searching, activeFamily, loadFamily, showingHome, showingPrinciples, showingComponents, showingStyleAgents])
78
138
 
79
- // Deep link: ?entry=<id> scrolls the entry into view once its family loaded.
80
139
  const activeEntries = activeFamilyId ? loaded[activeFamilyId] : undefined
81
140
  React.useEffect(() => {
82
- if (!entryParam || searching || !activeEntries) return
83
- const node = document.getElementById(`gallery-entry-${entryParam}`)
141
+ if (searching || (showingFoundations && entryParam && !activeEntries)) return
142
+ const variantId = entryParam === 'file-upload' && variantParam === 'drag-and-drop' ? 'default' : variantParam
143
+ const targetId = showingFoundations && entryParam ? `gallery-entry-${entryParam}` : entryParam && variantId && variantId !== '__all' ? `gallery-variant-${entryParam}-${variantId}` : 'gallery-page-heading'
144
+ const node = document.getElementById(targetId) ?? document.getElementById('gallery-page-heading')
84
145
  node?.scrollIntoView({ block: 'start' })
85
- }, [entryParam, searching, activeEntries])
86
-
87
- const sections: SectionNavGroup[] = React.useMemo(
88
- () => [
89
- {
90
- id: 'families',
91
- label: 'Families',
92
- labelKey: 'design_system.gallery.familiesGroup',
93
- items: galleryFamilies.map((family) => ({
94
- id: family.id,
95
- label: familyLabelFallback(family.id),
96
- labelKey: family.labelKey,
97
- icon: family.icon,
98
- href: `${GALLERY_BASE_PATH}?family=${family.id}`,
99
- })),
100
- },
101
- ],
102
- [],
103
- )
146
+ }, [familyParam, entryParam, variantParam, searching, activeEntries, showingFoundations, showingHome, showingPrinciples, showingComponents, showingStyleAgents])
147
+
148
+ const sections: SectionNavGroup[] = React.useMemo(() => {
149
+ const familyItem = (family: GalleryFamily) => ({
150
+ id: family.id,
151
+ label: familyLabelFallback(family.id),
152
+ labelKey: family.labelKey,
153
+ icon: family.icon,
154
+ href: `${GALLERY_BASE_PATH}?family=${family.id}`,
155
+ ...(!['library', 'icons'].includes(family.id) ? { children: (loaded[family.id] ?? []).map(entry => ({
156
+ id: `entry:${entry.id}`, label: entry.title,
157
+ ...(family.id === 'foundations' ? { labelKey: `design_system.foundations.${entry.id}.title` } : {}),
158
+ href: `${GALLERY_BASE_PATH}?family=${family.id}&entry=${entry.id}`,
159
+ })) } : {}),
160
+ })
161
+ return [
162
+ { id: 'guide', label: '', items: [
163
+ { id: '__home', label: 'Start', labelKey: 'design_system.portal.nav.home', icon: <Home className="size-4" />, href: GALLERY_BASE_PATH },
164
+ { id: '__style-agents', label: 'Style agents', labelKey: 'design_system.styleAgents.title', icon: <Palette className="size-4" />, href: `${GALLERY_BASE_PATH}?view=style-agents` },
165
+ ...galleryFamilies.filter(family => family.id === 'foundations').map(familyItem),
166
+ ] },
167
+ { id: 'families', label: 'Components', labelKey: 'design_system.portal.nav.components', items: [
168
+ { id: '__components', label: 'All components', labelKey: 'design_system.gallery.navigation.allComponents', icon: <Grid2X2 className="size-4" />, href: `${GALLERY_BASE_PATH}?view=components` },
169
+ ...componentFamilies.map(familyItem),
170
+ ] },
171
+ { id: 'resources', label: 'Resources', labelKey: 'design_system.gallery.navigation.resources', items: [
172
+ { id: '__principles', label: 'Guidelines', labelKey: 'design_system.portal.nav.principles', icon: <BookOpen className="size-4" />, href: `${GALLERY_BASE_PATH}?view=principles` },
173
+ ...galleryFamilies.filter(family => family.id === 'library').map(familyItem),
174
+ ] },
175
+ ]
176
+ }, [loaded])
177
+ const selectedEntry = entryParam ? activeEntries?.find(entry => entry.id === entryParam) : undefined
178
+ const familyTitle = activeFamily ? t(activeFamily.labelKey, familyLabelFallback(activeFamily.id)) : t('design_system.nav.title')
179
+ const pageTitle = showingComponents ? t('design_system.portal.nav.components') : showingEntry && selectedEntry ? selectedEntry.title : familyTitle
104
180
 
105
181
  const renderFamilySkeleton = () => (
106
182
  <div className="space-y-4" data-testid="gallery-family-skeleton">
@@ -133,7 +209,7 @@ export function GalleryShell() {
133
209
  {t(family.labelKey, familyLabelFallback(family.id))}
134
210
  </h2>
135
211
  {entries.map((entry) => (
136
- <EntryCard key={entry.id} entry={entry} />
212
+ <EntryCard key={entry.id} entry={entry} summary href={`${GALLERY_BASE_PATH}?family=${family.id}&entry=${entry.id}`} onNavigate={() => setSearch('')} />
137
213
  ))}
138
214
  </div>
139
215
  ))}
@@ -157,50 +233,60 @@ export function GalleryShell() {
157
233
  )
158
234
  }
159
235
  if (!entries) return renderFamilySkeleton()
236
+ if (activeFamily.id === 'icons') return <IconsBrowser entryId={entryParam ?? undefined} variantId={variantParam ?? undefined} onNavigate={(entryId, variantId) => {
237
+ const href = `${GALLERY_BASE_PATH}?family=icons&entry=${entryId}${variantId ? `&variant=${variantId}` : ''}`
238
+ if (!navigateGallery(href)) router.push(href, { scroll: false })
239
+ }} />
240
+ if (activeFamily.id === 'foundations') return <FoundationGuide entries={entries} />
241
+ const sourceLibraryRender = activeFamily.id === 'library' && entries.length === 1 && !entryParam
242
+ ? entries[0]?.variants[0]?.render
243
+ : undefined
244
+ if (sourceLibraryRender) {
245
+ const SourceLibraryRender = sourceLibraryRender
246
+ return <div className="space-y-4">
247
+ <SourceLibraryRender />
248
+ </div>
249
+ }
250
+ const selectedEntry = entries.find(entry => entry.id === entryParam) ?? (entries.length === 1 ? entries[0] : undefined)
251
+ const familyFullWidthIds = resolveFullWidthIds(entries.map(entry => entry.id), fullWidthEntryIds)
160
252
  return (
161
253
  <div className="space-y-4">
162
- <h2 className="text-lg font-semibold">
163
- {t(activeFamily.labelKey, familyLabelFallback(activeFamily.id))}
164
- </h2>
165
- {entries.length > 1 ? (
166
- <nav
167
- aria-label={t('design_system.gallery.familyContents', 'Components in this family')}
168
- className="flex flex-wrap items-center gap-1.5 rounded-md border border-border bg-muted/30 p-2"
169
- >
170
- {entries.map((entry) => (
171
- <a
172
- key={entry.id}
173
- href={`#gallery-entry-${entry.id}`}
174
- className="rounded-md border border-border bg-background px-2 py-1 text-xs text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:shadow-focus"
175
- >
176
- {entry.title}
177
- </a>
178
- ))}
179
- </nav>
180
- ) : null}
181
- {entries.map((entry) => (
182
- <EntryCard key={entry.id} entry={entry} />
183
- ))}
254
+ {selectedEntry ? <>
255
+ <EntryCard key={selectedEntry.id} entry={selectedEntry} hideTitle fullWidth={fullWidthEntryIds.has(selectedEntry.id)} />
256
+ </> : <div className="grid grid-cols-1 gap-4 @3xl:grid-cols-2">{entries.map(entry => <EntryCard key={entry.id} entry={entry} summary summaryPreview fullWidth={familyFullWidthIds.has(entry.id)} href={`${GALLERY_BASE_PATH}?family=${activeFamily.id}&entry=${entry.id}`} />)}</div>}
184
257
  </div>
185
258
  )
186
259
  }
187
260
 
188
261
  return (
189
- <SectionPage
190
- title="Design system"
191
- titleKey="design_system.nav.title"
192
- sections={sections}
193
- activePath={activeFamilyId ? `${GALLERY_BASE_PATH}?family=${activeFamilyId}` : GALLERY_BASE_PATH}
194
- >
195
- <div className="mx-auto w-full max-w-5xl space-y-6">
196
- <SearchInput
262
+ <div ref={rootRef} className="flex min-w-0 flex-col lg:flex-row" style={dockOffset ? { marginLeft: -dockOffset, width: `calc(100% + ${dockOffset}px)` } : undefined}>
263
+ <GalleryNavigation sections={sections} activePath={activeNavigationPath} activeFamilyId={familyParam} onNavigate={() => setSearch('')} failed={failed} onExpand={id => {
264
+ const family = galleryFamilies.find(family => family.id === id)
265
+ if (family) loadFamily(family)
266
+ }} onRetry={id => {
267
+ const family = galleryFamilies.find(family => family.id === id)
268
+ if (family) loadFamily(family)
269
+ }} />
270
+ <div className="min-w-0 flex-1 py-4 lg:pl-6">
271
+ <div className="@container mx-auto w-full max-w-6xl space-y-6">
272
+ {showingStyleAgents ? <StyleAgents /> : showingHome ? <DesignSystemHome /> : showingPrinciples ? <div id="gallery-page-heading" className="scroll-mt-36 lg:scroll-mt-24 space-y-8 py-8"><h1 className="text-3xl font-medium tracking-tight">{t('design_system.portal.nav.principles')}</h1><DesignSystemPrinciples /></div> : <>
273
+ <header id="gallery-page-heading" className="scroll-mt-36 lg:scroll-mt-24 space-y-3">
274
+ {selectedEntry && showingEntry ? <Button asChild variant="outline" size="sm"><Link href={`${GALLERY_BASE_PATH}?family=${activeFamilyId}`}><ArrowLeft aria-hidden />{t('design_system.gallery.backToNamedFamily', { family: familyTitle })}</Link></Button> : null}
275
+ {!showingComponents && (!selectedEntry || !showingEntry) ? <Button asChild variant="outline" size="sm"><Link href={`${GALLERY_BASE_PATH}?view=components`}><ArrowLeft aria-hidden />{t('design_system.gallery.backToNamedFamily', { family: t('design_system.portal.nav.components') })}</Link></Button> : null}
276
+ <h1 className="text-3xl font-semibold tracking-tight">{pageTitle}</h1>
277
+ {showingEntry && selectedEntry?.descriptionKey ? <p className="max-w-prose text-sm leading-relaxed text-muted-foreground">{t(selectedEntry.descriptionKey)}</p> : null}
278
+ {showingComponents ? <p className="text-base text-muted-foreground">{t('design_system.portal.componentsDescription')}</p> : null}
279
+ </header>
280
+ {!showingAssets && !showingInventory && !showingEntry ? <SearchInput
197
281
  value={search}
198
282
  onChange={setSearch}
199
283
  placeholder={t('design_system.gallery.searchPlaceholder', 'Search components…')}
200
284
  aria-label={t('design_system.gallery.searchPlaceholder', 'Search components…')}
201
- />
202
- {searching ? renderSearchResults() : renderActiveFamily()}
285
+ /> : null}
286
+ {searching ? renderSearchResults() : showingComponents ? <ComponentIndex families={componentFamilies} loaded={loaded} failed={failed} onRetry={loadFamily} /> : renderActiveFamily()}
287
+ </>}
203
288
  </div>
204
- </SectionPage>
289
+ </div>
290
+ </div>
205
291
  )
206
292
  }
@@ -0,0 +1,40 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
5
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'
6
+ import { SourceIconCatalogue } from '../demos/source-icons'
7
+ import { SourceArtworkCatalogue } from '../demos/source-artwork'
8
+ import { IconGrid } from '../demos/registered-icons'
9
+ import type { SourceArtworkGroup } from '@open-mercato/ui/assets/source-artwork'
10
+
11
+ const collections: { id: string; entryId: string; variantId: string; group?: SourceArtworkGroup }[] = [
12
+ { id: 'icons', entryId: 'source-icons', variantId: 'catalogue' },
13
+ { id: 'brands', entryId: 'source-artwork', variantId: 'brands', group: 'brand' },
14
+ { id: 'flags', entryId: 'source-artwork', variantId: 'flags', group: 'country-flags' },
15
+ { id: 'emoji', entryId: 'source-artwork', variantId: 'emoji', group: 'emojies' },
16
+ { id: 'badges', entryId: 'source-artwork', variantId: 'store-badges', group: 'appstore-badges' },
17
+ { id: 'thumbnails', entryId: 'source-artwork', variantId: 'thumbnails', group: 'thumbnails' },
18
+ { id: 'cursors', entryId: 'source-artwork', variantId: 'cursors', group: 'others' },
19
+ { id: 'registered', entryId: 'icon-registry', variantId: 'registry' },
20
+ ]
21
+
22
+ export function IconsBrowser({ entryId, variantId, onNavigate }: {
23
+ entryId?: string
24
+ variantId?: string
25
+ onNavigate: (entryId: string, variantId?: string) => void
26
+ }) {
27
+ const t = useT()
28
+ const collection = collections.find(item => item.entryId === entryId && item.variantId === variantId)
29
+ ?? collections.find(item => item.entryId === entryId)
30
+ ?? collections[0]
31
+ const collectionControl = <Select value={collection.id} onValueChange={value => {
32
+ const next = collections.find(item => item.id === value)
33
+ if (next) onNavigate(next.entryId, next.variantId)
34
+ }}>
35
+ <SelectTrigger className="w-64 max-w-full" aria-label={t('design_system.gallery.assets.collection')}><SelectValue /></SelectTrigger>
36
+ <SelectContent>{collections.map(item => <SelectItem key={item.id} value={item.id}>{t(`design_system.gallery.assets.${item.id}`)}</SelectItem>)}</SelectContent>
37
+ </Select>
38
+ return collection.group ? <SourceArtworkCatalogue key={collection.id} group={collection.group} collectionControl={collectionControl} />
39
+ : collection.id === 'registered' ? <IconGrid collectionControl={collectionControl} /> : <SourceIconCatalogue collectionControl={collectionControl} />
40
+ }
@@ -0,0 +1,90 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { GalleryLink as Link } from './GalleryLink'
5
+ import { ArrowRight } from 'lucide-react'
6
+ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
7
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
8
+ import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
9
+ import { Pagination } from '@open-mercato/ui/primitives/pagination'
10
+ import { SearchInput } from '@open-mercato/ui/primitives/search-input'
11
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@open-mercato/ui/primitives/select'
12
+ import { GALLERY_BASE_PATH } from '../registry'
13
+ import { sourceLibrary } from '../source-library.generated'
14
+
15
+ const pageSize = 12
16
+ const allGroups = '__all'
17
+ const groupTranslationKey = (group: string) => `design_system.gallery.sourceLibrary.groups.${group.toLowerCase().replace(/\s+/g, '-')}`
18
+
19
+ export function SourceLibrary() {
20
+ const t = useT()
21
+ const storyTitle = (story: { family: string; entryId: string; title: string }) => story.family === 'foundations'
22
+ ? t(`design_system.foundations.${story.entryId}.title`, story.title)
23
+ : story.title
24
+ const [query, setQuery] = React.useState('')
25
+ const [group, setGroup] = React.useState(allGroups)
26
+ const [page, setPage] = React.useState(1)
27
+ const groups = React.useMemo(() => Array.from(new Set(sourceLibrary.pages.map(item => item.group))), [])
28
+ const needle = query.trim().toLowerCase()
29
+ const filtered = sourceLibrary.pages.filter(item => {
30
+ const matchesGroup = group === allGroups || item.group === group
31
+ const translatedName = t(`design_system.gallery.sourceLibrary.names.${item.id}`, item.name)
32
+ const translatedGroup = t(groupTranslationKey(item.group), item.group)
33
+ const searchTerms = t(`design_system.gallery.sourceLibrary.searchTerms.${item.id}`, '')
34
+ const searchable = `${item.name} ${translatedName} ${item.group} ${translatedGroup} ${searchTerms} ${item.stories.map(story => `${story.title} ${storyTitle(story)}`).join(' ')} ${item.sets.map(set => `${set.name} ${Object.values(set.axes).flat().join(' ')}`).join(' ')} ${item.standalone.map(asset => asset.name).join(' ')}`
35
+ return matchesGroup && searchable.toLowerCase().includes(needle)
36
+ })
37
+ const visible = filtered.slice((page - 1) * pageSize, page * pageSize)
38
+
39
+ return <div className="w-full min-w-0 space-y-4" data-example="native-source-library">
40
+ <p className="text-sm text-muted-foreground">{t('design_system.gallery.sourceLibrary.boundary')}</p>
41
+ <div className="flex flex-col gap-3 sm:flex-row">
42
+ <SearchInput className="sm:flex-1" value={query} onChange={value => { setQuery(value); setPage(1) }} aria-label={t('design_system.gallery.sourceLibrary.search')} placeholder={t('design_system.gallery.sourceLibrary.search')} />
43
+ <Select value={group} onValueChange={value => { setGroup(value); setPage(1) }}>
44
+ <SelectTrigger className="w-full sm:w-64" aria-label={t('design_system.gallery.sourceLibrary.group')}><SelectValue /></SelectTrigger>
45
+ <SelectContent>
46
+ <SelectItem value={allGroups}>{t('design_system.gallery.sourceLibrary.allGroups')}</SelectItem>
47
+ {groups.map(value => <SelectItem key={value} value={value}>{t(groupTranslationKey(value), value)}</SelectItem>)}
48
+ </SelectContent>
49
+ </Select>
50
+ </div>
51
+ <p role="status" className="text-sm text-muted-foreground">{t('design_system.gallery.sourceLibrary.count', { visible: filtered.length, total: sourceLibrary.pages.length })}</p>
52
+ {visible.length ? <div className="divide-y divide-border rounded-lg border border-border bg-card">{visible.map(item => {
53
+ const variantCount = item.sets.reduce((total, set) => total + set.count, 0)
54
+ const hasInventory = item.sets.length > 0 || item.standaloneCount > 0 || item.frameCount > 0
55
+ return <article key={item.id} className="grid min-w-0 gap-4 p-5 lg:grid-cols-3">
56
+ <header className="space-y-1">
57
+ <h3 className="text-base font-semibold leading-tight">{t(`design_system.gallery.sourceLibrary.names.${item.id}`, item.name)}</h3>
58
+ <p className="text-xs text-muted-foreground">{t(groupTranslationKey(item.group), item.group)}</p>
59
+ </header>
60
+ <div className="min-w-0 space-y-3 lg:col-span-2">
61
+ {item.stories.length ? <ul className="flex flex-wrap gap-x-6 gap-y-3" aria-label={t('design_system.gallery.sourceLibrary.examples')}>
62
+ {item.stories.map(story => <li key={`${story.family}:${story.entryId}`}>
63
+ <LinkButton asChild variant="black">
64
+ <Link href={`${GALLERY_BASE_PATH}?family=${story.family}&entry=${story.entryId}`}>
65
+ {storyTitle(story)}<ArrowRight className="size-4 text-muted-foreground" aria-hidden="true" />
66
+ </Link>
67
+ </LinkButton>
68
+ </li>)}
69
+ </ul> : <p className="text-sm text-muted-foreground">{t('design_system.gallery.sourceLibrary.referenceOnly')}</p>}
70
+ {hasInventory ? <section className="text-sm">
71
+ <h4 className="text-xs font-medium text-muted-foreground">{t('design_system.gallery.sourceLibrary.details')}</h4>
72
+ <div className="mt-3 space-y-4 border-l border-border pl-4">
73
+ <div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
74
+ {item.sets.length ? <span>{t('design_system.gallery.sourceLibrary.componentSets', { count: item.sets.length })}</span> : null}
75
+ {variantCount ? <span>{t('design_system.gallery.sourceLibrary.variants', { count: variantCount })}</span> : null}
76
+ {item.frameCount ? <span>{t('design_system.gallery.sourceLibrary.frames', { count: item.frameCount })}</span> : null}
77
+ {item.standaloneCount ? <span>{t('design_system.gallery.sourceLibrary.assets', { count: item.standaloneCount })}</span> : null}
78
+ </div>
79
+ {item.sets.map(set => <section key={set.id} className="space-y-2">
80
+ <h4 className="text-sm font-medium">{set.name}</h4>
81
+ <dl className="space-y-2">{Object.entries(set.axes).map(([axis, values]) => <div key={axis} className="text-xs"><dt className="font-medium">{axis}</dt><dd className="break-words text-muted-foreground">{(values as string[]).join(' · ')}</dd></div>)}</dl>
82
+ </section>)}
83
+ </div>
84
+ </section> : null}
85
+ </div>
86
+ </article>
87
+ })}</div> : <EmptyState title={t('design_system.gallery.noResults')} />}
88
+ {filtered.length > pageSize ? <Pagination page={page} pageSize={pageSize} total={filtered.length} showPageSize={false} onPageChange={setPage} aria-label={t('design_system.gallery.sourceLibrary.pages')} /> : null}
89
+ </div>
90
+ }
@@ -0,0 +1,115 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Check, ImagePlus, RotateCcw, Upload, X } from 'lucide-react'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'
8
+ import { getBrandStyle, saveBrandStyle, type BrandStyle } from '@open-mercato/ui/theme/brand-style'
9
+ import { ColorPlayground } from './ColorPlayground'
10
+ import { BrandStylePreview } from './BrandStylePreview'
11
+ import { readThemeTokens, type ThemeTokens } from './themeTokens'
12
+ import type { Shade } from './colorStudio'
13
+ import type { ColorStudio } from './colorStudio'
14
+ import { readBrandLogo, studioBrandStyle } from './styleAgentBranding'
15
+
16
+ function brandPreview(studio: ColorStudio, logo: string | null) {
17
+ const style = studioBrandStyle(studio, logo)
18
+ return { light: style.light, dark: style.dark }
19
+ }
20
+
21
+ export function StyleAgents() {
22
+ const t = useT()
23
+ const input = React.useRef<HTMLInputElement>(null)
24
+ const uploadVersion = React.useRef(0)
25
+ const [initial, setInitial] = React.useState<BrandStyle | null | undefined>(undefined)
26
+ const [hostTokens, setHostTokens] = React.useState<ThemeTokens | null>(null)
27
+ const [logo, setLogo] = React.useState<string | null>(null)
28
+ const [draft, setDraft] = React.useState<{ studio: ColorStudio; valid: boolean } | null>(null)
29
+ const [uploading, setUploading] = React.useState(false)
30
+ const [notice, setNotice] = React.useState('')
31
+ const [error, setError] = React.useState('')
32
+ React.useEffect(() => {
33
+ setHostTokens(readThemeTokens(document.styleSheets))
34
+ const applied = getBrandStyle()
35
+ setInitial(applied)
36
+ setLogo(applied?.logo ?? null)
37
+ return () => { uploadVersion.current += 1 }
38
+ }, [])
39
+ const updateStudio = React.useCallback((studio: ColorStudio, valid: boolean) => {
40
+ setDraft({ studio, valid })
41
+ setNotice('')
42
+ }, [])
43
+
44
+ async function upload(file: File | undefined) {
45
+ if (!file) return
46
+ const version = ++uploadVersion.current
47
+ setUploading(true)
48
+ setError('')
49
+ setNotice('')
50
+ try {
51
+ const data = await readBrandLogo(file)
52
+ if (uploadVersion.current === version) setLogo(data)
53
+ } catch (failure) {
54
+ if (uploadVersion.current === version) setError(failure instanceof Error ? failure.message : 'fileDecode')
55
+ } finally {
56
+ if (uploadVersion.current === version) setUploading(false)
57
+ }
58
+ }
59
+
60
+ function apply() {
61
+ if (!draft?.valid || uploading) return
62
+ try {
63
+ saveBrandStyle(studioBrandStyle(draft.studio, logo))
64
+ setError('')
65
+ setNotice('applied')
66
+ } catch { setError('saveFailed'); setNotice('') }
67
+ }
68
+
69
+ function restore() {
70
+ try {
71
+ saveBrandStyle(null)
72
+ setError('')
73
+ setNotice('restored')
74
+ } catch { setError('saveFailed'); setNotice('') }
75
+ }
76
+
77
+ return <div className="space-y-8 pb-8" data-example="style-agents">
78
+ <header id="gallery-page-heading" className="scroll-mt-36 space-y-3 lg:scroll-mt-24">
79
+ <div className="flex flex-wrap items-start justify-between gap-4">
80
+ <div className="space-y-3">
81
+ <h1 className="text-3xl font-medium tracking-tight">{t('design_system.styleAgents.title')}</h1>
82
+ <p className="max-w-3xl text-sm leading-relaxed text-muted-foreground">{t('design_system.styleAgents.description')}</p>
83
+ </div>
84
+ <div className="flex flex-wrap items-center gap-2">
85
+ <SimpleTooltip content={t('design_system.styleAgents.restoreHint')}>
86
+ <Button type="button" variant="ghost" onClick={restore}><RotateCcw aria-hidden />{t('design_system.styleAgents.restore')}</Button>
87
+ </SimpleTooltip>
88
+ <Button type="button" onClick={apply} disabled={!draft?.valid || uploading}><Check aria-hidden />{t('design_system.styleAgents.apply')}</Button>
89
+ </div>
90
+ </div>
91
+ {notice ? <p role="status" className="text-sm text-status-success-text">{t(`design_system.styleAgents.${notice}`)}</p> : null}
92
+ {error ? <p role="alert" className="text-sm text-status-error-text">{t(`design_system.styleAgents.${error}`)}</p> : null}
93
+ </header>
94
+ <section className="grid gap-6 rounded-xl border border-border bg-card p-5 sm:p-6 lg:grid-cols-2" aria-label={t('design_system.styleAgents.identity')}>
95
+ <div className="space-y-4">
96
+ <div className="space-y-1"><h2 className="text-lg font-medium">{t('design_system.styleAgents.logo')}</h2><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.styleAgents.logoHint')}</p></div>
97
+ <div className="flex min-h-28 items-center justify-center rounded-lg border border-dashed border-border bg-background p-6">
98
+ {logo ? <img src={logo} alt={t('design_system.styleAgents.logoPreview')} className="max-h-20 max-w-full object-contain" /> : <ImagePlus className="size-8 text-muted-foreground" aria-hidden />}
99
+ </div>
100
+ <input ref={input} type="file" accept="image/png,image/jpeg,image/webp" className="sr-only" aria-label={t('design_system.styleAgents.upload')} onChange={event => { void upload(event.target.files?.[0]); event.target.value = '' }} />
101
+ <div className="flex flex-wrap gap-2">
102
+ <Button type="button" variant="outline" onClick={() => input.current?.click()}><Upload aria-hidden />{t(`design_system.styleAgents.${uploading ? 'uploading' : 'upload'}`)}</Button>
103
+ {logo ? <Button type="button" variant="ghost" onClick={() => { uploadVersion.current += 1; setUploading(false); setLogo(null); setNotice(''); setError('') }}><X aria-hidden />{t('design_system.styleAgents.removeLogo')}</Button> : null}
104
+ </div>
105
+ </div>
106
+ <div className="flex flex-col justify-between gap-5">
107
+ <div className="space-y-3"><h2 className="text-lg font-medium">{t('design_system.styleAgents.applyTitle')}</h2><p className="text-sm leading-relaxed text-muted-foreground">{t('design_system.styleAgents.scope')}</p><p className="text-xs leading-relaxed text-muted-foreground">{t('design_system.styleAgents.tokenScope')}</p></div>
108
+
109
+ </div>
110
+ </section>
111
+ {draft ? <BrandStylePreview {...brandPreview(draft.studio, logo)} hostTokens={hostTokens} logo={logo} /> : null}
112
+ {initial !== undefined ? <ColorPlayground initialView="light" initialSeeds={initial?.seeds} initialActionShades={initial?.actionShades as { light?: Shade; dark?: Shade } | undefined} logo={logo} onStudioChange={updateStudio} /> : null}
113
+
114
+ </div>
115
+ }
@@ -0,0 +1,76 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import { DataTable } from '@open-mercato/ui/backend/DataTable'
7
+ import { Button } from '@open-mercato/ui/primitives/button'
8
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
9
+ import { readThemeTokens, type ThemeTokens } from './themeTokens'
10
+
11
+ const referenceTokens = [
12
+ 'brand-lime', 'brand-yellow', 'brand-violet', 'background', 'foreground',
13
+ 'card', 'card-foreground', 'muted', 'muted-foreground', 'primary', 'primary-foreground',
14
+ 'primary-hover', 'border', 'input', 'ring', 'destructive',
15
+ 'status-info-bg', 'status-info-text', 'status-success-bg', 'status-success-text',
16
+ 'status-warning-bg', 'status-warning-text', 'status-error-bg', 'status-error-text',
17
+ ]
18
+ type TokenRow = { token: string; light: string; dark: string }
19
+
20
+ function displayColor(value: string, context: CanvasRenderingContext2D | null) {
21
+ if (!context || !CSS.supports('color', value)) return value
22
+ context.clearRect(0, 0, 1, 1)
23
+ context.fillStyle = value
24
+ context.fillRect(0, 0, 1, 1)
25
+ const [red, green, blue, alpha] = context.getImageData(0, 0, 1, 1).data
26
+ return '#' + [red, green, blue, ...(alpha === 255 ? [] : [alpha])].map(channel => channel.toString(16).padStart(2, '0')).join('').toUpperCase()
27
+ }
28
+
29
+ export function ThemeTokenReference() {
30
+ const t = useT()
31
+ const [tokens, setTokens] = React.useState<ThemeTokens | null>(null)
32
+ React.useEffect(() => {
33
+ const values = readThemeTokens(document.styleSheets)
34
+ const canvas = document.createElement('canvas')
35
+ canvas.width = 1
36
+ canvas.height = 1
37
+ const context = canvas.getContext('2d', { willReadFrequently: true })
38
+ for (const theme of ['light', 'dark'] as const) {
39
+ for (const token of referenceTokens) {
40
+ const value = values[theme][`--${token}`]
41
+ if (value) values[theme][`--${token}`] = displayColor(value, context)
42
+ }
43
+ }
44
+ setTokens(values)
45
+ }, [])
46
+ const rows = referenceTokens.map(token => ({ token: `--${token}`, light: tokens?.light[`--${token}`] ?? '—', dark: tokens?.dark[`--${token}`] ?? '—' }))
47
+ async function copyToken(token: string) {
48
+ try {
49
+ await navigator.clipboard.writeText(token)
50
+ flash(t('design_system.gallery.tokenCopied', { token }), 'success')
51
+ } catch { flash(t('design_system.gallery.tokenCopyFailed'), 'error') }
52
+ }
53
+ const columns: ColumnDef<TokenRow, unknown>[] = [
54
+ { accessorKey: 'token', header: t('design_system.foundations.token'), cell: ({ row }) => <Button type="button" variant="ghost" className="h-auto justify-start px-0 py-2 font-mono text-xs" aria-label={t('design_system.gallery.copyToken', { token: row.original.token })} onClick={() => copyToken(row.original.token)}>{row.original.token}</Button> },
55
+ ...(['light', 'dark'] as const).map(theme => ({
56
+ accessorKey: theme,
57
+ header: t(`design_system.foundations.${theme}`),
58
+ cell: ({ row }: { row: { original: TokenRow } }) => <span className="flex items-center gap-3 py-2"><span aria-hidden className="size-6 shrink-0 rounded-full border border-border" style={{ backgroundColor: row.original[theme] === '—' ? undefined : row.original[theme] }} /><code className="text-xs text-muted-foreground">{row.original[theme]}</code></span>,
59
+ })),
60
+ ]
61
+ return <div className="space-y-4">
62
+ <h3 className="text-xl font-medium">{t('design_system.foundations.themeReference')}</h3>
63
+ <p className="max-w-prose text-sm leading-relaxed text-muted-foreground">{t('design_system.foundations.themeDescription')}</p>
64
+ {tokens ? <div className="grid gap-4 sm:grid-cols-2">
65
+ {(['light', 'dark'] as const).map(theme => <div key={theme} className="space-y-6 rounded-lg border p-6" style={{ backgroundColor: tokens[theme]['--background'], color: tokens[theme]['--foreground'], borderColor: tokens[theme]['--border'] }}>
66
+ <p className="text-base font-medium">{t(`design_system.foundations.${theme}`)}</p>
67
+ <div className="flex flex-wrap items-center gap-3">
68
+ <Button type="button" style={{ backgroundColor: tokens[theme]['--primary'], color: tokens[theme]['--primary-foreground'] }}>{t('design_system.gallery.samples.primaryAction')}</Button>
69
+ <span className="inline-flex h-9 items-center rounded-md border px-4 text-sm" style={{ borderColor: tokens[theme]['--border'] }}>Aa</span>
70
+ </div>
71
+ <code className="text-xs" style={{ color: tokens[theme]['--muted-foreground'] }}>{tokens[theme]['--background']} / {tokens[theme]['--foreground']}</code>
72
+ </div>)}
73
+ </div> : null}
74
+ <DataTable columns={columns} data={rows} isLoading={!tokens} />
75
+ </div>
76
+ }
@@ -6,9 +6,9 @@ import * as React from 'react'
6
6
  * Bordered stage a variant renders inside. Chrome uses semantic tokens only —
7
7
  * the stage must never impose colors of its own on the previewed component.
8
8
  */
9
- export function VariantPreview({ children }: { children: React.ReactNode }) {
9
+ export function VariantPreview({ children, framed = true }: { children: React.ReactNode; framed?: boolean }) {
10
10
  return (
11
- <div className="flex flex-wrap items-center gap-3 rounded-md border border-border bg-background p-4">
11
+ <div className={framed ? 'flex min-w-0 flex-wrap items-start gap-4 rounded-lg border border-border bg-background p-4 sm:p-6 [&>*]:min-w-0' : 'min-w-0 [&>*]:min-w-0'}>
12
12
  {children}
13
13
  </div>
14
14
  )