@open-mercato/core 0.6.7-develop.6785.1.1dd7cfac55 → 0.6.7-develop.6795.1.8a3f27921c

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 (196) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities.ids.generated.js +1 -0
  3. package/dist/generated/entities.ids.generated.js.map +2 -2
  4. package/dist/modules/auth/backend/settings/page.js +14 -9
  5. package/dist/modules/auth/backend/settings/page.js.map +2 -2
  6. package/dist/modules/customers/cli.js +17 -7
  7. package/dist/modules/customers/cli.js.map +2 -2
  8. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +229 -216
  9. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  10. package/dist/modules/customers/search.js +50 -29
  11. package/dist/modules/customers/search.js.map +2 -2
  12. package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
  13. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
  14. package/dist/modules/data_sync/api/mappings/route.js +13 -4
  15. package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
  16. package/dist/modules/data_sync/api/options.js +7 -2
  17. package/dist/modules/data_sync/api/options.js.map +2 -2
  18. package/dist/modules/data_sync/api/run.js +7 -2
  19. package/dist/modules/data_sync/api/run.js.map +2 -2
  20. package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
  21. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  22. package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
  23. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  24. package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
  25. package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
  26. package/dist/modules/data_sync/api/runs.js +7 -2
  27. package/dist/modules/data_sync/api/runs.js.map +2 -2
  28. package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
  29. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  30. package/dist/modules/data_sync/api/schedules/route.js +13 -4
  31. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  32. package/dist/modules/data_sync/api/validate.js +9 -4
  33. package/dist/modules/data_sync/api/validate.js.map +2 -2
  34. package/dist/modules/design_system/acl.js +9 -0
  35. package/dist/modules/design_system/acl.js.map +7 -0
  36. package/dist/modules/design_system/backend/design-system/page.js +11 -0
  37. package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
  38. package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
  39. package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
  40. package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
  41. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
  42. package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
  43. package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
  44. package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
  45. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
  46. package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
  47. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
  48. package/dist/modules/design_system/gallery/entries/banners.js +186 -0
  49. package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
  50. package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
  51. package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
  52. package/dist/modules/design_system/gallery/entries/charts.js +457 -0
  53. package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
  54. package/dist/modules/design_system/gallery/entries/dates.js +306 -0
  55. package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
  56. package/dist/modules/design_system/gallery/entries/detail.js +358 -0
  57. package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
  58. package/dist/modules/design_system/gallery/entries/display.js +785 -0
  59. package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
  60. package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
  61. package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
  62. package/dist/modules/design_system/gallery/entries/filters.js +505 -0
  63. package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
  64. package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
  65. package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
  66. package/dist/modules/design_system/gallery/entries/icons.js +140 -0
  67. package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
  68. package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
  69. package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
  70. package/dist/modules/design_system/gallery/entries/messages.js +334 -0
  71. package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
  72. package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
  73. package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
  74. package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
  75. package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
  76. package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
  77. package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
  78. package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
  79. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
  80. package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
  81. package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
  82. package/dist/modules/design_system/gallery/registry.js +139 -0
  83. package/dist/modules/design_system/gallery/registry.js.map +7 -0
  84. package/dist/modules/design_system/gallery/types.js +1 -0
  85. package/dist/modules/design_system/gallery/types.js.map +7 -0
  86. package/dist/modules/design_system/index.js +9 -0
  87. package/dist/modules/design_system/index.js.map +7 -0
  88. package/dist/modules/design_system/setup.js +14 -0
  89. package/dist/modules/design_system/setup.js.map +7 -0
  90. package/dist/modules/inbox_ops/search.js +15 -4
  91. package/dist/modules/inbox_ops/search.js.map +2 -2
  92. package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
  93. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  94. package/dist/modules/integrations/api/[id]/health/route.js +6 -3
  95. package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
  96. package/dist/modules/integrations/api/[id]/route.js +6 -3
  97. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  98. package/dist/modules/integrations/api/[id]/state/route.js +6 -3
  99. package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
  100. package/dist/modules/integrations/api/[id]/version/route.js +6 -3
  101. package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
  102. package/dist/modules/integrations/api/logs/route.js +6 -3
  103. package/dist/modules/integrations/api/logs/route.js.map +2 -2
  104. package/dist/modules/integrations/api/route.js +6 -3
  105. package/dist/modules/integrations/api/route.js.map +2 -2
  106. package/dist/modules/integrations/api/umes-read.js +2 -2
  107. package/dist/modules/integrations/api/umes-read.js.map +2 -2
  108. package/dist/modules/integrations/lib/organization-scope.js +2 -9
  109. package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
  110. package/dist/modules/messages/search.js +10 -5
  111. package/dist/modules/messages/search.js.map +2 -2
  112. package/dist/modules/query_index/data/entities.js +2 -1
  113. package/dist/modules/query_index/data/entities.js.map +2 -2
  114. package/dist/modules/query_index/lib/engine.js +32 -50
  115. package/dist/modules/query_index/lib/engine.js.map +2 -2
  116. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
  117. package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
  118. package/generated/entities.ids.generated.ts +1 -0
  119. package/package.json +16 -16
  120. package/src/modules/auth/backend/settings/page.tsx +17 -8
  121. package/src/modules/customers/cli.ts +17 -7
  122. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +24 -13
  123. package/src/modules/customers/i18n/de.json +17 -0
  124. package/src/modules/customers/i18n/en.json +17 -0
  125. package/src/modules/customers/i18n/es.json +17 -0
  126. package/src/modules/customers/i18n/pl.json +17 -0
  127. package/src/modules/customers/search.ts +59 -27
  128. package/src/modules/data_sync/AGENTS.md +1 -0
  129. package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
  130. package/src/modules/data_sync/api/mappings/route.ts +13 -4
  131. package/src/modules/data_sync/api/options.ts +7 -2
  132. package/src/modules/data_sync/api/run.ts +7 -2
  133. package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
  134. package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
  135. package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
  136. package/src/modules/data_sync/api/runs.ts +7 -2
  137. package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
  138. package/src/modules/data_sync/api/schedules/route.ts +13 -4
  139. package/src/modules/data_sync/api/validate.ts +9 -4
  140. package/src/modules/design_system/AGENTS.md +34 -0
  141. package/src/modules/design_system/acl.ts +5 -0
  142. package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
  143. package/src/modules/design_system/backend/design-system/page.tsx +14 -0
  144. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
  145. package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
  146. package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
  147. package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
  148. package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
  149. package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
  150. package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
  151. package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
  152. package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
  153. package/src/modules/design_system/gallery/entries/display.tsx +893 -0
  154. package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
  155. package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
  156. package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
  157. package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
  158. package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
  159. package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
  160. package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
  161. package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
  162. package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
  163. package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
  164. package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
  165. package/src/modules/design_system/gallery/registry.ts +152 -0
  166. package/src/modules/design_system/gallery/types.ts +32 -0
  167. package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
  168. package/src/modules/design_system/i18n/de.json +46 -0
  169. package/src/modules/design_system/i18n/en.json +46 -0
  170. package/src/modules/design_system/i18n/es.json +46 -0
  171. package/src/modules/design_system/i18n/pl.json +46 -0
  172. package/src/modules/design_system/index.ts +7 -0
  173. package/src/modules/design_system/setup.ts +12 -0
  174. package/src/modules/inbox_ops/i18n/de.json +3 -0
  175. package/src/modules/inbox_ops/i18n/en.json +3 -0
  176. package/src/modules/inbox_ops/i18n/es.json +3 -0
  177. package/src/modules/inbox_ops/i18n/pl.json +3 -0
  178. package/src/modules/inbox_ops/search.ts +20 -4
  179. package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
  180. package/src/modules/integrations/api/[id]/health/route.ts +6 -3
  181. package/src/modules/integrations/api/[id]/route.ts +6 -3
  182. package/src/modules/integrations/api/[id]/state/route.ts +6 -3
  183. package/src/modules/integrations/api/[id]/version/route.ts +6 -3
  184. package/src/modules/integrations/api/logs/route.ts +6 -3
  185. package/src/modules/integrations/api/route.ts +6 -3
  186. package/src/modules/integrations/api/umes-read.ts +2 -2
  187. package/src/modules/integrations/lib/organization-scope.ts +5 -25
  188. package/src/modules/messages/i18n/de.json +2 -0
  189. package/src/modules/messages/i18n/en.json +2 -0
  190. package/src/modules/messages/i18n/es.json +2 -0
  191. package/src/modules/messages/i18n/pl.json +2 -0
  192. package/src/modules/messages/search.ts +11 -5
  193. package/src/modules/query_index/data/entities.ts +7 -0
  194. package/src/modules/query_index/lib/engine.ts +40 -63
  195. package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
  196. package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
@@ -0,0 +1,529 @@
1
+ import * as React from 'react'
2
+ import { Circle } from 'lucide-react'
3
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
4
+ import type { GalleryEntry } from '../types'
5
+
6
+ // Token names are proper nouns from the codebase and are deliberately not
7
+ // translated. Structure mirrors the DS Figma color-system sheet (node
8
+ // 553:14956): Brand Colors → Color Tokens by role → State Color Tokens.
9
+ // Figma state names map to code tokens per the contract documented in
10
+ // globals.css: state/{x}/base→icon, /light→border, /lighter→bg.
11
+
12
+ const TOKENS_IMPORT = 'apps/mercato/src/app/globals.css'
13
+ const FIGMA_COLORS_NODE = '553:14956'
14
+
15
+ function SwatchCard({ copyText, label, children }: { copyText: string; label: string; children: React.ReactNode }) {
16
+ const onCopy = React.useCallback(async () => {
17
+ try {
18
+ await navigator.clipboard.writeText(copyText)
19
+ flash(`${copyText} copied`, 'success')
20
+ } catch {
21
+ flash('Could not copy the token', 'error')
22
+ }
23
+ }, [copyText])
24
+ return (
25
+ <button
26
+ type="button"
27
+ onClick={onCopy}
28
+ className="flex w-36 flex-col items-start gap-1 rounded-md border border-border bg-background p-2 text-left transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:shadow-focus"
29
+ title={`Copy ${copyText}`}
30
+ >
31
+ {children}
32
+ <code className="break-all text-[11px] leading-tight text-muted-foreground">{label}</code>
33
+ </button>
34
+ )
35
+ }
36
+
37
+ function TokenSwatch({ tokenClass, label }: { tokenClass: string; label: string }) {
38
+ return (
39
+ <SwatchCard copyText={label} label={label}>
40
+ <span aria-hidden className={`h-10 w-full rounded-sm border border-border ${tokenClass}`} />
41
+ </SwatchCard>
42
+ )
43
+ }
44
+
45
+ // A text-color token shown in its actual role — as type, not as a fill.
46
+ function TextSwatch({ textClass, label }: { textClass: string; label: string }) {
47
+ return (
48
+ <SwatchCard copyText={textClass} label={label}>
49
+ <span aria-hidden className="flex h-10 w-full items-center justify-center rounded-sm border border-border bg-background">
50
+ <span className={`text-xl font-semibold leading-none ${textClass}`}>Aa</span>
51
+ </span>
52
+ </SwatchCard>
53
+ )
54
+ }
55
+
56
+ // A border-color token shown as a 2px outline on the neutral surface.
57
+ function BorderSwatch({ borderClass, label }: { borderClass: string; label: string }) {
58
+ return (
59
+ <SwatchCard copyText={borderClass} label={label}>
60
+ <span aria-hidden className={`h-10 w-full rounded-sm border-2 bg-background ${borderClass}`} />
61
+ </SwatchCard>
62
+ )
63
+ }
64
+
65
+ // An icon-color token shown on a real glyph.
66
+ function IconSwatch({ iconClass, label }: { iconClass: string; label: string }) {
67
+ return (
68
+ <SwatchCard copyText={iconClass} label={label}>
69
+ <span aria-hidden className="flex h-10 w-full items-center justify-center rounded-sm border border-border bg-background">
70
+ <Circle className={`size-4 fill-current ${iconClass}`} />
71
+ </span>
72
+ </SwatchCard>
73
+ )
74
+ }
75
+
76
+ function SwatchRow({ items }: { items: Array<{ tokenClass: string; label: string }> }) {
77
+ return (
78
+ <div className="flex flex-wrap gap-2">
79
+ {items.map((item) => (
80
+ <TokenSwatch key={item.label} tokenClass={item.tokenClass} label={item.label} />
81
+ ))}
82
+ </div>
83
+ )
84
+ }
85
+
86
+ const brandColorsEntry: GalleryEntry = {
87
+ id: 'brand-colors',
88
+ title: 'Brand colors',
89
+ importPath: TOKENS_IMPORT,
90
+ usage: {
91
+ do: ['brand-violet 10/30/100 pattern (bg/border/text) for proposed and feature accents.'],
92
+ dont: ['Never theme or repurpose accent-indigo — it is the selection-control contract.'],
93
+ },
94
+ figmaNodeId: FIGMA_COLORS_NODE,
95
+ variants: [
96
+ {
97
+ id: 'gradient',
98
+ title: 'gradient 135°',
99
+ render: () => (
100
+ <div className="space-y-2">
101
+ <div
102
+ aria-hidden
103
+ className="h-16 w-full max-w-md rounded-md border border-border bg-linear-135 from-brand-lime from-0% via-brand-yellow via-35% to-brand-violet to-70%"
104
+ />
105
+ <code className="text-xs text-muted-foreground">brand-lime 0%, brand-yellow 35%, brand-violet 70%</code>
106
+ </div>
107
+ ),
108
+ code: `<div className="bg-linear-135 from-brand-lime from-0% via-brand-yellow via-35% to-brand-violet to-70%" />`,
109
+ },
110
+ {
111
+ id: 'identity',
112
+ title: 'identity',
113
+ render: () => (
114
+ <SwatchRow
115
+ items={[
116
+ { tokenClass: 'bg-brand-lime', label: 'brand-lime' },
117
+ { tokenClass: 'bg-brand-yellow', label: 'brand-yellow' },
118
+ { tokenClass: 'bg-brand-violet', label: 'brand-violet' },
119
+ { tokenClass: 'bg-brand-violet-foreground', label: 'brand-violet-foreground' },
120
+ { tokenClass: 'bg-accent-indigo', label: 'accent-indigo' },
121
+ ]}
122
+ />
123
+ ),
124
+ code: `<span className="bg-brand-violet/10 border-brand-violet/30 text-brand-violet" />`,
125
+ },
126
+ {
127
+ id: 'social',
128
+ title: 'social',
129
+ render: () => (
130
+ <SwatchRow
131
+ items={[
132
+ { tokenClass: 'bg-brand-apple', label: 'brand-apple' },
133
+ { tokenClass: 'bg-brand-github', label: 'brand-github' },
134
+ { tokenClass: 'bg-brand-facebook', label: 'brand-facebook' },
135
+ { tokenClass: 'bg-brand-dropbox', label: 'brand-dropbox' },
136
+ { tokenClass: 'bg-brand-linkedin', label: 'brand-linkedin' },
137
+ { tokenClass: 'bg-brand-x', label: 'brand-x' },
138
+ ]}
139
+ />
140
+ ),
141
+ code: `<SocialButton provider="github" />`,
142
+ },
143
+ ],
144
+ }
145
+
146
+ const colorTokensEntry: GalleryEntry = {
147
+ id: 'color-tokens',
148
+ title: 'Color tokens',
149
+ importPath: TOKENS_IMPORT,
150
+ usage: {
151
+ do: ['Pair every surface with its -foreground counterpart.', 'Borders come from border-border / border-input — nothing else.'],
152
+ dont: ['Never border-gray-* or any raw palette shade.'],
153
+ },
154
+ figmaNodeId: FIGMA_COLORS_NODE,
155
+ variants: [
156
+ {
157
+ id: 'primary',
158
+ title: 'primary',
159
+ render: () => (
160
+ <SwatchRow
161
+ items={[
162
+ { tokenClass: 'bg-primary', label: 'primary' },
163
+ { tokenClass: 'bg-primary-foreground', label: 'primary-foreground' },
164
+ { tokenClass: 'bg-primary-hover', label: 'primary-hover' },
165
+ { tokenClass: 'bg-destructive', label: 'destructive' },
166
+ ]}
167
+ />
168
+ ),
169
+ code: `<div className="bg-primary text-primary-foreground hover:bg-primary-hover" />`,
170
+ },
171
+ {
172
+ id: 'background',
173
+ title: 'background (bg)',
174
+ render: () => (
175
+ <SwatchRow
176
+ items={[
177
+ { tokenClass: 'bg-background', label: 'background' },
178
+ { tokenClass: 'bg-card', label: 'card' },
179
+ { tokenClass: 'bg-popover', label: 'popover' },
180
+ { tokenClass: 'bg-muted', label: 'muted' },
181
+ { tokenClass: 'bg-accent', label: 'accent' },
182
+ { tokenClass: 'bg-sidebar', label: 'sidebar' },
183
+ { tokenClass: 'bg-bg-disabled', label: 'bg-disabled' },
184
+ ]}
185
+ />
186
+ ),
187
+ code: `<div className="bg-card text-card-foreground" />`,
188
+ },
189
+ {
190
+ id: 'text',
191
+ title: 'text',
192
+ render: () => (
193
+ <div className="flex flex-wrap gap-2">
194
+ <TextSwatch textClass="text-foreground" label="text-foreground" />
195
+ <TextSwatch textClass="text-muted-foreground" label="text-muted-foreground" />
196
+ <TextSwatch textClass="text-card-foreground" label="text-card-foreground" />
197
+ <TextSwatch textClass="text-accent-foreground" label="text-accent-foreground" />
198
+ <TextSwatch textClass="text-text-disabled" label="text-text-disabled" />
199
+ </div>
200
+ ),
201
+ code: `<p className="text-muted-foreground">Secondary copy</p>`,
202
+ },
203
+ {
204
+ id: 'stroke',
205
+ title: 'stroke',
206
+ render: () => (
207
+ <div className="flex flex-wrap gap-2">
208
+ <BorderSwatch borderClass="border-border" label="border-border" />
209
+ <BorderSwatch borderClass="border-input" label="border-input" />
210
+ <BorderSwatch borderClass="border-ring" label="border-ring" />
211
+ <BorderSwatch borderClass="border-border-disabled" label="border-border-disabled" />
212
+ </div>
213
+ ),
214
+ code: `<div className="border-border focus-visible:ring-ring" />`,
215
+ },
216
+ ],
217
+ }
218
+
219
+ // Literal class strings on purpose: the Tailwind scanner only sees complete
220
+ // literals, so `bg-status-${x}` variants would silently never be generated.
221
+ type StatusFamily = {
222
+ figma: string
223
+ status: string
224
+ chipLabel: string
225
+ bg: string
226
+ text: string
227
+ border: string
228
+ icon: string
229
+ dot: string
230
+ }
231
+
232
+ const FIGMA_STATE_TO_CODE: StatusFamily[] = [
233
+ { figma: 'Faded', status: 'neutral', chipLabel: 'Neutral', bg: 'bg-status-neutral-bg', text: 'text-status-neutral-text', border: 'border-status-neutral-border', icon: 'text-status-neutral-icon', dot: 'bg-status-neutral-icon' },
234
+ { figma: 'Information', status: 'info', chipLabel: 'Info', bg: 'bg-status-info-bg', text: 'text-status-info-text', border: 'border-status-info-border', icon: 'text-status-info-icon', dot: 'bg-status-info-icon' },
235
+ { figma: 'Warning', status: 'warning', chipLabel: 'Warning', bg: 'bg-status-warning-bg', text: 'text-status-warning-text', border: 'border-status-warning-border', icon: 'text-status-warning-icon', dot: 'bg-status-warning-icon' },
236
+ { figma: 'Error', status: 'error', chipLabel: 'Error', bg: 'bg-status-error-bg', text: 'text-status-error-text', border: 'border-status-error-border', icon: 'text-status-error-icon', dot: 'bg-status-error-icon' },
237
+ { figma: 'Success', status: 'success', chipLabel: 'Success', bg: 'bg-status-success-bg', text: 'text-status-success-text', border: 'border-status-success-border', icon: 'text-status-success-icon', dot: 'bg-status-success-icon' },
238
+ { figma: 'Highlighted', status: 'pink', chipLabel: 'Highlighted', bg: 'bg-status-pink-bg', text: 'text-status-pink-text', border: 'border-status-pink-border', icon: 'text-status-pink-icon', dot: 'bg-status-pink-icon' },
239
+ ]
240
+
241
+ // One status family: the composed chip first (how the roles work together),
242
+ // then each token presented in its actual role.
243
+ function StatusFamilyPreview({ family }: { family: StatusFamily }) {
244
+ return (
245
+ <div className="space-y-3">
246
+ <span
247
+ className={`inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-sm font-medium ${family.bg} ${family.text} ${family.border}`}
248
+ >
249
+ <span aria-hidden className={`size-2 rounded-full ${family.dot}`} />
250
+ {family.chipLabel}
251
+ </span>
252
+ <div className="flex flex-wrap gap-2">
253
+ <TokenSwatch tokenClass={family.bg} label={family.bg} />
254
+ <TextSwatch textClass={family.text} label={family.text} />
255
+ <BorderSwatch borderClass={family.border} label={family.border} />
256
+ <IconSwatch iconClass={family.icon} label={family.icon} />
257
+ </div>
258
+ </div>
259
+ )
260
+ }
261
+
262
+ const stateTokensEntry: GalleryEntry = {
263
+ id: 'state-tokens',
264
+ title: 'State color tokens',
265
+ importPath: TOKENS_IMPORT,
266
+ usage: {
267
+ do: [
268
+ 'Pair -bg with -text of the same family — the shades are contrast-tested together.',
269
+ '-icon for glyphs and dots, -border for outlines; both handle dark mode themselves.',
270
+ ],
271
+ dont: [
272
+ 'Never hardcode Tailwind status colors (text-red-*, bg-green-*, text-amber-*).',
273
+ 'No dark: overrides on status tokens — they already theme.',
274
+ ],
275
+ },
276
+ figmaNodeId: FIGMA_COLORS_NODE,
277
+ variants: [
278
+ ...FIGMA_STATE_TO_CODE.map((family) => ({
279
+ id: family.status,
280
+ title: `${family.figma} → status-${family.status}`,
281
+ render: () => <StatusFamilyPreview family={family} />,
282
+ code: `<span className="${family.bg} ${family.text} ${family.border}" />`,
283
+ })),
284
+ {
285
+ id: 'feature',
286
+ title: 'Feature → brand-violet pattern',
287
+ render: () => (
288
+ <div className="flex max-w-md items-center gap-2 rounded-md border border-brand-violet/30 bg-brand-violet/10 p-3 text-sm text-brand-violet">
289
+ Feature state uses the brand-violet 10/30/100 pattern, not a status family.
290
+ </div>
291
+ ),
292
+ code: `<Alert status="feature" />`,
293
+ },
294
+ ],
295
+ }
296
+
297
+ const chartPaletteEntry: GalleryEntry = {
298
+ id: 'chart-palette',
299
+ title: 'Chart palette',
300
+ importPath: TOKENS_IMPORT,
301
+ usage: {
302
+ dont: [
303
+ 'Never build token class names dynamically (`bg-chart-${n}`) — the Tailwind scanner only sees complete literals, so the class is silently never generated.',
304
+ ],
305
+ },
306
+ variants: [
307
+ {
308
+ id: 'numbered',
309
+ title: 'numbered',
310
+ render: () => (
311
+ <SwatchRow
312
+ items={[
313
+ { tokenClass: 'bg-chart-1', label: 'chart-1' },
314
+ { tokenClass: 'bg-chart-2', label: 'chart-2' },
315
+ { tokenClass: 'bg-chart-3', label: 'chart-3' },
316
+ { tokenClass: 'bg-chart-4', label: 'chart-4' },
317
+ { tokenClass: 'bg-chart-5', label: 'chart-5' },
318
+ ]}
319
+ />
320
+ ),
321
+ code: `<BarChart data={data} />`,
322
+ },
323
+ {
324
+ id: 'named',
325
+ title: 'named',
326
+ render: () => (
327
+ <SwatchRow
328
+ items={[
329
+ { tokenClass: 'bg-chart-blue', label: 'chart-blue' },
330
+ { tokenClass: 'bg-chart-emerald', label: 'chart-emerald' },
331
+ { tokenClass: 'bg-chart-amber', label: 'chart-amber' },
332
+ { tokenClass: 'bg-chart-rose', label: 'chart-rose' },
333
+ { tokenClass: 'bg-chart-violet', label: 'chart-violet' },
334
+ { tokenClass: 'bg-chart-cyan', label: 'chart-cyan' },
335
+ { tokenClass: 'bg-chart-indigo', label: 'chart-indigo' },
336
+ { tokenClass: 'bg-chart-pink', label: 'chart-pink' },
337
+ { tokenClass: 'bg-chart-teal', label: 'chart-teal' },
338
+ { tokenClass: 'bg-chart-orange', label: 'chart-orange' },
339
+ ]}
340
+ />
341
+ ),
342
+ code: `<Sparkline className="text-chart-blue" data={points} />`,
343
+ },
344
+ ],
345
+ }
346
+
347
+ type ColorRole = {
348
+ surface: string
349
+ onSurface: string
350
+ surfaceCls: string
351
+ onSurfaceCls: string
352
+ borderCls?: string
353
+ }
354
+
355
+ function RoleCard({ role }: { role: ColorRole }) {
356
+ const label = `${role.surface} / ${role.onSurface}`
357
+ const onCopy = React.useCallback(async () => {
358
+ try {
359
+ await navigator.clipboard.writeText(`${role.surfaceCls} ${role.onSurfaceCls}`)
360
+ flash(`${role.surfaceCls} ${role.onSurfaceCls} copied`, 'success')
361
+ } catch {
362
+ flash('Could not copy the classes', 'error')
363
+ }
364
+ }, [role])
365
+ return (
366
+ <button
367
+ type="button"
368
+ onClick={onCopy}
369
+ title={`Copy ${role.surfaceCls} ${role.onSurfaceCls}`}
370
+ className={`flex h-24 w-full flex-col justify-between rounded-md border p-3 text-left transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:shadow-focus ${role.surfaceCls} ${role.onSurfaceCls} ${role.borderCls ?? 'border-border'}`}
371
+ >
372
+ <span className="text-sm font-medium leading-tight">{role.surface}</span>
373
+ <code className="text-xs opacity-80">{role.onSurface}</code>
374
+ </button>
375
+ )
376
+ }
377
+
378
+ function RoleGrid({ roles }: { roles: ColorRole[] }) {
379
+ return (
380
+ <div className="grid w-full gap-2 sm:grid-cols-2 lg:grid-cols-3">
381
+ {roles.map((role) => (
382
+ <RoleCard key={role.surface + role.onSurface} role={role} />
383
+ ))}
384
+ </div>
385
+ )
386
+ }
387
+
388
+ const colorRolesEntry: GalleryEntry = {
389
+ id: 'color-roles',
390
+ title: 'Color roles',
391
+ importPath: TOKENS_IMPORT,
392
+ figmaNodeId: FIGMA_COLORS_NODE,
393
+ keywords: ['roles', 'pairs', 'on-color', 'material'],
394
+ usage: {
395
+ do: [
396
+ 'A role is a surface paired with its content color — always use them together, never mix pairs.',
397
+ 'Click a card to copy both classes of the pair.',
398
+ ],
399
+ dont: [
400
+ 'Never put foreground on a surface from a different pair — contrast is only tested within a pair.',
401
+ 'Never use a -foreground token as a standalone accent color.',
402
+ ],
403
+ },
404
+ variants: [
405
+ {
406
+ id: 'action',
407
+ title: 'action',
408
+ render: () => (
409
+ <RoleGrid
410
+ roles={[
411
+ { surface: 'primary', onSurface: 'primary-foreground', surfaceCls: 'bg-primary', onSurfaceCls: 'text-primary-foreground' },
412
+ { surface: 'destructive', onSurface: 'white', surfaceCls: 'bg-destructive', onSurfaceCls: 'text-white' },
413
+ { surface: 'brand-violet', onSurface: 'brand-violet-foreground', surfaceCls: 'bg-brand-violet', onSurfaceCls: 'text-brand-violet-foreground' },
414
+ { surface: 'accent-indigo', onSurface: 'accent-indigo-foreground', surfaceCls: 'bg-accent-indigo', onSurfaceCls: 'text-accent-indigo-foreground' },
415
+ ]}
416
+ />
417
+ ),
418
+ code: `<button className="bg-primary text-primary-foreground" />`,
419
+ },
420
+ {
421
+ id: 'surfaces',
422
+ title: 'surfaces',
423
+ render: () => (
424
+ <RoleGrid
425
+ roles={[
426
+ { surface: 'background', onSurface: 'foreground', surfaceCls: 'bg-background', onSurfaceCls: 'text-foreground' },
427
+ { surface: 'card', onSurface: 'card-foreground', surfaceCls: 'bg-card', onSurfaceCls: 'text-card-foreground' },
428
+ { surface: 'popover', onSurface: 'popover-foreground', surfaceCls: 'bg-popover', onSurfaceCls: 'text-popover-foreground' },
429
+ { surface: 'muted', onSurface: 'muted-foreground', surfaceCls: 'bg-muted', onSurfaceCls: 'text-muted-foreground' },
430
+ { surface: 'accent', onSurface: 'accent-foreground', surfaceCls: 'bg-accent', onSurfaceCls: 'text-accent-foreground' },
431
+ { surface: 'sidebar', onSurface: 'sidebar-foreground', surfaceCls: 'bg-sidebar', onSurfaceCls: 'text-sidebar-foreground' },
432
+ ]}
433
+ />
434
+ ),
435
+ code: `<div className="bg-card text-card-foreground" />`,
436
+ },
437
+ {
438
+ id: 'status',
439
+ title: 'status',
440
+ render: () => (
441
+ <RoleGrid
442
+ roles={['error', 'success', 'warning', 'info', 'neutral', 'pink'].map((status) => ({
443
+ surface: `status-${status}-bg`,
444
+ onSurface: `status-${status}-text`,
445
+ surfaceCls: `bg-status-${status}-bg`,
446
+ onSurfaceCls: `text-status-${status}-text`,
447
+ borderCls: `border-status-${status}-border`,
448
+ }))}
449
+ />
450
+ ),
451
+ code: `<span className="bg-status-error-bg text-status-error-text border-status-error-border" />`,
452
+ },
453
+ ],
454
+ }
455
+
456
+ const RADIUS_SCALE: Array<{ cls: string; label: string; px: string }> = [
457
+ { cls: 'rounded-none', label: 'rounded-none', px: '0px' },
458
+ { cls: 'rounded-sm', label: 'rounded-sm', px: '6px' },
459
+ { cls: 'rounded-md', label: 'rounded-md', px: '8px' },
460
+ { cls: 'rounded-lg', label: 'rounded-lg', px: '10px' },
461
+ { cls: 'rounded-xl', label: 'rounded-xl', px: '16px' },
462
+ { cls: 'rounded-full', label: 'rounded-full', px: '999px' },
463
+ ]
464
+
465
+ function RadiusCard({ cls, label, px }: { cls: string; label: string; px: string }) {
466
+ const onCopy = React.useCallback(async () => {
467
+ try {
468
+ await navigator.clipboard.writeText(label)
469
+ flash(`${label} copied`, 'success')
470
+ } catch {
471
+ flash('Could not copy the class', 'error')
472
+ }
473
+ }, [label])
474
+ return (
475
+ <button
476
+ type="button"
477
+ onClick={onCopy}
478
+ title={`Copy ${label}`}
479
+ className="flex w-36 flex-col items-start gap-2 rounded-md border border-border bg-background p-2 text-left transition-colors hover:bg-muted/40"
480
+ >
481
+ <span aria-hidden className={`h-14 w-full border-2 border-dashed border-status-pink-icon bg-status-pink-bg ${cls}`} />
482
+ <span className="space-y-0.5">
483
+ <code className="block text-xs text-foreground">{label}</code>
484
+ <span className="block text-xs text-muted-foreground">{px}</span>
485
+ </span>
486
+ </button>
487
+ )
488
+ }
489
+
490
+ const radiusEntry: GalleryEntry = {
491
+ id: 'corner-radius',
492
+ title: 'Corner radius',
493
+ importPath: TOKENS_IMPORT,
494
+ keywords: ['radius', 'rounded', 'border-radius', 'corners'],
495
+ usage: {
496
+ do: [
497
+ 'The scale derives from the --radius base token (10px): sm 6px, md 8px, lg 10px, xl 16px.',
498
+ 'Cards and panels use rounded-lg; controls, chips and filter buttons use rounded-md.',
499
+ 'rounded-full is reserved for Badge, Tag, SegmentedControl, Avatar and status dots.',
500
+ ],
501
+ dont: [
502
+ 'No arbitrary values (rounded-[24px]) — pick from the scale.',
503
+ 'No full-pill radii on filter chips or custom controls outside the reserved primitives.',
504
+ ],
505
+ },
506
+ variants: [
507
+ {
508
+ id: 'scale',
509
+ title: 'scale',
510
+ render: () => (
511
+ <div className="flex flex-wrap gap-2">
512
+ {RADIUS_SCALE.map((item) => (
513
+ <RadiusCard key={item.cls} {...item} />
514
+ ))}
515
+ </div>
516
+ ),
517
+ code: `<div className="rounded-lg border border-border" />`,
518
+ },
519
+ ],
520
+ }
521
+
522
+ export const entries: GalleryEntry[] = [
523
+ brandColorsEntry,
524
+ colorRolesEntry,
525
+ colorTokensEntry,
526
+ stateTokensEntry,
527
+ chartPaletteEntry,
528
+ radiusEntry,
529
+ ]