noecosystem-design 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/apps/showcase/src/catalog/catalog-index.tsx +5 -5
  2. package/apps/showcase/src/catalog/catalog-shell.tsx +7 -2
  3. package/apps/storybook/stories/catalog/calendar.stories.tsx +3 -1
  4. package/apps/storybook/stories/catalog/date-picker.stories.tsx +5 -1
  5. package/docs/design-system/inventory.md +0 -1
  6. package/package.json +2 -2
  7. package/packages/ai-ui/src/activity-feed.tsx +1 -2
  8. package/packages/ai-ui/src/agent-composer.tsx +1 -1
  9. package/packages/ai-ui/src/agent-message.tsx +1 -1
  10. package/packages/ai-ui/src/agent-plan.tsx +1 -1
  11. package/packages/ai-ui/src/agent-workspace.tsx +1 -2
  12. package/packages/ai-ui/src/approval-request.tsx +1 -1
  13. package/packages/ai-ui/src/run-inspector.tsx +1 -1
  14. package/packages/ai-ui/src/tool-call.tsx +1 -1
  15. package/packages/data-ui/src/activity-gauge.tsx +1 -1
  16. package/packages/data-ui/src/chart-frame.tsx +1 -1
  17. package/packages/data-ui/src/chart.tsx +1 -2
  18. package/packages/data-ui/src/dashboard-shell.tsx +1 -2
  19. package/packages/data-ui/src/data-table.tsx +1 -1
  20. package/packages/data-ui/src/filter-bar.tsx +1 -2
  21. package/packages/data-ui/src/metric-card.tsx +1 -1
  22. package/packages/data-ui/src/pagination.tsx +1 -3
  23. package/packages/data-ui/src/pie-chart.tsx +1 -1
  24. package/packages/data-ui/src/radar-chart.tsx +1 -1
  25. package/packages/design-tokens/src/palette.ts +4 -4
  26. package/packages/design-tokens/src/tokens.css +0 -6
  27. package/packages/registry/catalog-index.json +1 -1
  28. package/packages/registry/component-audit.json +1 -1
  29. package/packages/registry/docs-index.json +135 -403
  30. package/packages/registry/manifest.json +1642 -6110
  31. package/packages/registry/r/activity-feed.json +9 -32
  32. package/packages/registry/r/agent-composer.json +9 -34
  33. package/packages/registry/r/agent-message.json +9 -32
  34. package/packages/registry/r/agent-plan.json +9 -32
  35. package/packages/registry/r/agent-workspace.json +9 -32
  36. package/packages/registry/r/alert.json +9 -33
  37. package/packages/registry/r/app-frame.json +9 -32
  38. package/packages/registry/r/approval-request.json +9 -34
  39. package/packages/registry/r/badge.json +9 -33
  40. package/packages/registry/r/bidi-text.json +7 -25
  41. package/packages/registry/r/bottom-navigation.json +9 -32
  42. package/packages/registry/r/button.json +7 -29
  43. package/packages/registry/r/calendar.json +10 -37
  44. package/packages/registry/r/card.json +9 -32
  45. package/packages/registry/r/chart-frame.json +9 -32
  46. package/packages/registry/r/chart.json +8 -30
  47. package/packages/registry/r/code-block.json +9 -32
  48. package/packages/registry/r/context-menu.json +8 -36
  49. package/packages/registry/r/dashboard-01.json +7 -23
  50. package/packages/registry/r/dashboard-02.json +6 -20
  51. package/packages/registry/r/dashboard-shell.json +9 -32
  52. package/packages/registry/r/data-list.json +9 -32
  53. package/packages/registry/r/data-table.json +9 -32
  54. package/packages/registry/r/date-picker.json +10 -37
  55. package/packages/registry/r/dialog.json +11 -45
  56. package/packages/registry/r/direction-provider.json +6 -21
  57. package/packages/registry/r/drawer.json +10 -36
  58. package/packages/registry/r/dropdown.json +9 -37
  59. package/packages/registry/r/empty-state.json +9 -32
  60. package/packages/registry/r/error-state.json +9 -32
  61. package/packages/registry/r/fab.json +9 -34
  62. package/packages/registry/r/featured-icon.json +7 -29
  63. package/packages/registry/r/filter-bar.json +10 -35
  64. package/packages/registry/r/gradient-picker.json +7 -24
  65. package/packages/registry/r/header-navigation.json +9 -32
  66. package/packages/registry/r/hero-header.json +7 -25
  67. package/packages/registry/r/icon-system.json +8 -28
  68. package/packages/registry/r/image-picker.json +7 -24
  69. package/packages/registry/r/inline-cta.json +9 -32
  70. package/packages/registry/r/input.json +9 -33
  71. package/packages/registry/r/landing-page.json +6 -21
  72. package/packages/registry/r/login-page.json +9 -35
  73. package/packages/registry/r/marketing-header.json +8 -26
  74. package/packages/registry/r/menu.json +9 -34
  75. package/packages/registry/r/messaging.json +9 -33
  76. package/packages/registry/r/metric-card.json +9 -32
  77. package/packages/registry/r/mobile-screen.json +9 -32
  78. package/packages/registry/r/not-found.json +7 -25
  79. package/packages/registry/r/page-header.json +9 -32
  80. package/packages/registry/r/pagination.json +10 -37
  81. package/packages/registry/r/progress-steps.json +9 -33
  82. package/packages/registry/r/run-inspector.json +9 -32
  83. package/packages/registry/r/section-header.json +9 -32
  84. package/packages/registry/r/settings-01.json +5 -19
  85. package/packages/registry/r/settings-02.json +5 -19
  86. package/packages/registry/r/settings-page.json +9 -32
  87. package/packages/registry/r/sidebar-navigation.json +9 -32
  88. package/packages/registry/r/sign-in-form.json +9 -34
  89. package/packages/registry/r/skeleton.json +9 -32
  90. package/packages/registry/r/spinner.json +9 -32
  91. package/packages/registry/r/tabs.json +9 -34
  92. package/packages/registry/r/textarea.json +9 -33
  93. package/packages/registry/r/tool-call.json +9 -32
  94. package/packages/registry/r/tooltip.json +9 -33
  95. package/packages/registry/r/utility-button.json +9 -37
  96. package/packages/registry/r/workflow-canvas.json +9 -32
  97. package/packages/registry/r/workflow-node.json +9 -32
  98. package/packages/registry/registry.json +232 -937
  99. package/packages/registry/search-index.json +579 -2194
  100. package/packages/ui/package.json +4 -1
  101. package/packages/ui/src/date-picker.browser.test.tsx +1 -1
  102. package/packages/ui/src/date-picker.tsx +1 -2
  103. package/packages/ui/src/gradient-picker.tsx +6 -2
  104. package/packages/ui/src/theme-provider.browser.test.tsx +12 -4
  105. package/packages/ui/src/theme-provider.tsx +19 -5
  106. package/packages/workflow-ui/src/workflow-canvas.tsx +1 -1
  107. package/packages/workflow-ui/src/workflow-node.tsx +1 -1
  108. package/scripts/enrich-registry-payloads.mjs +9 -3
  109. package/scripts/fix-registry-closure.mjs +2 -1
  110. package/scripts/generate-manifest.mjs +5 -1
  111. package/scripts/validate-manifest.mjs +25 -8
  112. package/scripts/verify-registry-consumer.mjs +33 -7
  113. package/tests/e2e/catalog-typography.spec.ts +5 -2
  114. package/tsconfig.base.json +17 -51
@@ -21,6 +21,9 @@
21
21
  "react-dom": "^19.2.0"
22
22
  },
23
23
  "exports": {
24
- ".": "./src/index.ts"
24
+ ".": "./src/index.ts",
25
+ "./bidi": "./src/bidi.tsx",
26
+ "./button": "./src/button.tsx",
27
+ "./lib/cn": "./src/lib/cn.ts"
25
28
  }
26
29
  }
@@ -27,7 +27,7 @@ test('DatePicker click outside closes popover', async () => {
27
27
  style={{
28
28
  position: 'fixed',
29
29
  top: '0px',
30
- left: '0px',
30
+ insetInlineStart: '0px',
31
31
  width: '50px',
32
32
  height: '50px',
33
33
  background: 'red',
@@ -96,8 +96,7 @@ export function DatePicker({
96
96
  setPopoverStyle({
97
97
  position: 'fixed',
98
98
  top: `${rect.bottom + 8}px`,
99
- left: isRtl ? 'auto' : `${rect.left}px`,
100
- right: isRtl ? `${window.innerWidth - rect.right}px` : 'auto',
99
+ insetInlineStart: `${isRtl ? window.innerWidth - rect.right : rect.left}px`,
101
100
  zIndex: 'var(--noe-z-index-overlay, 50)',
102
101
  });
103
102
  };
@@ -42,14 +42,18 @@ export function GradientPicker({
42
42
  aria-label="Start color"
43
43
  type="color"
44
44
  defaultValue={GRADIENT_START}
45
- onChange={(e) => onValueChange?.(`linear-gradient(90deg, ${e.target.value}, ${GRADIENT_END})`)}
45
+ onChange={(e) =>
46
+ onValueChange?.(`linear-gradient(90deg, ${e.target.value}, ${GRADIENT_END})`)
47
+ }
46
48
  className="size-8 rounded-md border border-border"
47
49
  />
48
50
  <input
49
51
  aria-label="End color"
50
52
  type="color"
51
53
  defaultValue={GRADIENT_END}
52
- onChange={(e) => onValueChange?.(`linear-gradient(90deg, ${GRADIENT_START}, ${e.target.value})`)}
54
+ onChange={(e) =>
55
+ onValueChange?.(`linear-gradient(90deg, ${GRADIENT_START}, ${e.target.value})`)
56
+ }
53
57
  className="size-8 rounded-md border border-border"
54
58
  />
55
59
  <input
@@ -10,7 +10,9 @@ function PaletteProbe({ storageKey }: { storageKey?: string }) {
10
10
  return (
11
11
  <div>
12
12
  <span data-testid="palette">{palette}</span>
13
- <button type="button" onClick={() => setPalette('graphite' as never)}>set graphite</button>
13
+ <button type="button" onClick={() => setPalette('graphite' as never)}>
14
+ set graphite
15
+ </button>
14
16
  {storageKey && <span data-testid="storageKey">{storageKey}</span>}
15
17
  </div>
16
18
  );
@@ -26,7 +28,9 @@ test('default Graphite with no preference', async () => {
26
28
  </ThemeProvider>,
27
29
  );
28
30
  await expect.element(screen.getByTestId('palette')).toHaveTextContent('graphite');
29
- await expect.poll(() => document.documentElement.getAttribute('data-noe-palette')).toBe('graphite');
31
+ await expect
32
+ .poll(() => document.documentElement.getAttribute('data-noe-palette'))
33
+ .toBe('graphite');
30
34
  });
31
35
 
32
36
  test('valid stored preference restoration', async () => {
@@ -49,7 +53,9 @@ test('invalid storage and cookie fallback to Graphite', async () => {
49
53
  );
50
54
  // Should fallback to graphite
51
55
  await expect.element(screen.getByTestId('palette')).toHaveTextContent('graphite');
52
- await expect.poll(() => document.documentElement.getAttribute('data-noe-palette')).toBe('graphite');
56
+ await expect
57
+ .poll(() => document.documentElement.getAttribute('data-noe-palette'))
58
+ .toBe('graphite');
53
59
  });
54
60
 
55
61
  test('custom paletteStorageKey is honored', async () => {
@@ -84,7 +90,9 @@ test('palette/theme independence in light and dark modes', async () => {
84
90
  </ThemeProvider>,
85
91
  );
86
92
  await expect.element(screen.getByTestId('palette')).toHaveTextContent('graphite');
87
- await expect.poll(() => document.documentElement.getAttribute('data-noe-palette')).toBe('graphite');
93
+ await expect
94
+ .poll(() => document.documentElement.getAttribute('data-noe-palette'))
95
+ .toBe('graphite');
88
96
  await expect.poll(() => document.documentElement.dataset.theme).toBe('light');
89
97
  await expect.element(screen.getByTestId('palette')).toHaveTextContent('graphite');
90
98
  });
@@ -160,8 +160,12 @@ function applyThemeToDocument(theme: ResolvedTheme) {
160
160
  root.style.colorScheme = theme;
161
161
  const meta = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]');
162
162
  if (meta) {
163
- const lightFallback = (tokens as unknown as { primitive: { color: { paper: Record<string, string> } } }).primitive?.color?.paper?.['100'] ?? '#f3f0ea';
164
- const darkFallback = (tokens as unknown as { primitive: { color: { ink: Record<string, string> } } }).primitive?.color?.ink?.['950'] ?? '#252423';
163
+ const lightFallback =
164
+ (tokens as unknown as { primitive: { color: { paper: Record<string, string> } } }).primitive
165
+ ?.color?.paper?.['100'] ?? '#f3f0ea';
166
+ const darkFallback =
167
+ (tokens as unknown as { primitive: { color: { ink: Record<string, string> } } }).primitive
168
+ ?.color?.ink?.['950'] ?? '#252423';
165
169
  const light = meta.getAttribute('data-theme-light') ?? lightFallback;
166
170
  const dark = meta.getAttribute('data-theme-dark') ?? darkFallback;
167
171
  meta.setAttribute('content', theme === 'light' ? light : dark);
@@ -202,7 +206,8 @@ export function ThemeProvider({
202
206
  });
203
207
 
204
208
  const [palette, setPaletteState] = React.useState<Palette>(() => {
205
- if (typeof window === 'undefined') return isPalette(defaultPalette) ? defaultPalette : DEFAULT_PALETTE;
209
+ if (typeof window === 'undefined')
210
+ return isPalette(defaultPalette) ? defaultPalette : DEFAULT_PALETTE;
206
211
  const stored = readStoredPalette(paletteStorageKey);
207
212
  if (stored && isPalette(stored)) return stored;
208
213
  if (isPalette(defaultPalette)) return defaultPalette;
@@ -307,7 +312,12 @@ export function ThemeProvider({
307
312
 
308
313
  React.useEffect(() => {
309
314
  const stored = readStoredPalette(paletteStorageKey);
310
- const initialPalette = stored && isPalette(stored) ? stored : isPalette(defaultPalette) ? defaultPalette : DEFAULT_PALETTE;
315
+ const initialPalette =
316
+ stored && isPalette(stored)
317
+ ? stored
318
+ : isPalette(defaultPalette)
319
+ ? defaultPalette
320
+ : DEFAULT_PALETTE;
311
321
  const validated = isPalette(initialPalette) ? initialPalette : DEFAULT_PALETTE;
312
322
  setPaletteState(validated);
313
323
  applyPaletteToDocument(validated);
@@ -318,7 +328,11 @@ export function ThemeProvider({
318
328
 
319
329
  React.useEffect(() => {
320
330
  const onStorage = (event: StorageEvent) => {
321
- if (event.key === paletteStorageKey || event.key === PALETTE_STORAGE_KEY || event.key === PALETTE_COOKIE_KEY) {
331
+ if (
332
+ event.key === paletteStorageKey ||
333
+ event.key === PALETTE_STORAGE_KEY ||
334
+ event.key === PALETTE_COOKIE_KEY
335
+ ) {
322
336
  if (isPalette(event.newValue)) {
323
337
  setPaletteState(event.newValue as Palette);
324
338
  applyPaletteToDocument(event.newValue as Palette);
@@ -48,4 +48,4 @@ export function WorkflowCanvas({
48
48
  ) : null}
49
49
  </section>
50
50
  );
51
- }
51
+ }
@@ -58,4 +58,4 @@ export function WorkflowNode({
58
58
  </div>
59
59
  </article>
60
60
  );
61
- }
61
+ }
@@ -46,9 +46,15 @@ for (const item of manifest.items.filter(
46
46
  ? ['DirectionProvider']
47
47
  : [],
48
48
  // Dependencies — merge manifest and payload, keeping externally installable npm packages; workspace @noe/* are satisfied via included files/registryDependencies
49
- registryDependencies: [...new Set([...(item.registryDependencies || []), ...(payload.registryDependencies || [])])],
50
- dependencies: [...new Set([...(item.dependencies || []), ...(payload.dependencies || [])])].filter((d) => !d.startsWith('@noe/')),
51
- peerDependencies: [...new Set([...(item.peerDependencies || []), ...(payload.peerDependencies || [])])],
49
+ registryDependencies: [
50
+ ...new Set([...(item.registryDependencies || []), ...(payload.registryDependencies || [])]),
51
+ ],
52
+ dependencies: [
53
+ ...new Set([...(item.dependencies || []), ...(payload.dependencies || [])]),
54
+ ].filter((d) => !d.startsWith('@noe/')),
55
+ peerDependencies: [
56
+ ...new Set([...(item.peerDependencies || []), ...(payload.peerDependencies || [])]),
57
+ ],
52
58
  // RTL/i18n
53
59
  rtl: item.internationalization?.rtl || 'required',
54
60
  i18n: {
@@ -7,7 +7,8 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
7
  function extractImports(content) {
8
8
  const imports = [];
9
9
  for (const m of content.matchAll(/from\s+['"]([^'"]+)['"]/g)) imports.push(m[1]);
10
- for (const m of content.matchAll(/import\s+['"]([^'"]+)['"]/g)) if (!imports.includes(m[1])) imports.push(m[1]);
10
+ for (const m of content.matchAll(/import\s+['"]([^'"]+)['"]/g))
11
+ if (!imports.includes(m[1])) imports.push(m[1]);
11
12
  return imports;
12
13
  }
13
14
 
@@ -764,7 +764,11 @@ function defaultEnrichment(auditItem) {
764
764
  deps.push('@noe/icons');
765
765
  if (auditItem.id === 'filter-bar') registryDeps.push('input');
766
766
  // Hard-coded icon consumers discovered via audit (calendar, inline-cta, date-picker)
767
- if (auditItem.id === 'calendar' || auditItem.id === 'inline-cta' || auditItem.id === 'date-picker') {
767
+ if (
768
+ auditItem.id === 'calendar' ||
769
+ auditItem.id === 'inline-cta' ||
770
+ auditItem.id === 'date-picker'
771
+ ) {
768
772
  if (!deps.includes('@noe/icons')) deps.push('@noe/icons');
769
773
  }
770
774
 
@@ -292,7 +292,9 @@ if (errors === 0) pass('semantic search fields');
292
292
  // 13. Source-file and payload existence
293
293
  {
294
294
  let missing = 0;
295
- for (const item of manifest.items.filter((i) => i.status === 'available' || i.status === 'partial')) {
295
+ for (const item of manifest.items.filter(
296
+ (i) => i.status === 'available' || i.status === 'partial',
297
+ )) {
296
298
  if (!item.registryItem) {
297
299
  fail(`available/partial ${item.id} missing registryItem`);
298
300
  errors++;
@@ -323,7 +325,9 @@ if (errors === 0) pass('semantic search fields');
323
325
  // 14. Registry payload closure (relative imports must be included)
324
326
  {
325
327
  let closureErrors = 0;
326
- for (const item of manifest.items.filter((i) => i.status === 'available' || i.status === 'partial')) {
328
+ for (const item of manifest.items.filter(
329
+ (i) => i.status === 'available' || i.status === 'partial',
330
+ )) {
327
331
  if (!item.registryItem) continue;
328
332
  const payloadPath = path.join(root, `packages/registry/r/${item.registryItem}.json`);
329
333
  let payload;
@@ -375,7 +379,9 @@ if (errors === 0) pass('semantic search fields');
375
379
  }
376
380
  });
377
381
  // For simplicity, if not in current payload, check if it's in any other payload that is a dependency (we already include via files)
378
- fail(`closure: ${item.registryItem} ${f.path} imports '${imp}' -> ${resolved} not in payload`);
382
+ fail(
383
+ `closure: ${item.registryItem} ${f.path} imports '${imp}' -> ${resolved} not in payload`,
384
+ );
379
385
  errors++;
380
386
  closureErrors++;
381
387
  }
@@ -390,7 +396,9 @@ if (errors === 0) pass('semantic search fields');
390
396
  let missingStory = 0;
391
397
  const storyFiles = new Set();
392
398
  // Collect story ids from filesystem (reusing earlier logic would duplicate, but we check file existence directly)
393
- for (const item of manifest.items.filter((i) => i.status === 'available' || i.status === 'partial')) {
399
+ for (const item of manifest.items.filter(
400
+ (i) => i.status === 'available' || i.status === 'partial',
401
+ )) {
394
402
  if (!item.storyId) {
395
403
  fail(`available/partial ${item.id} missing storyId`);
396
404
  errors++;
@@ -417,14 +425,18 @@ if (errors === 0) pass('semantic search fields');
417
425
  // 16. Install command correctness
418
426
  {
419
427
  let badInstall = 0;
420
- for (const item of manifest.items.filter((i) => i.status === 'available' || i.status === 'partial')) {
428
+ for (const item of manifest.items.filter(
429
+ (i) => i.status === 'available' || i.status === 'partial',
430
+ )) {
421
431
  if (!item.registryItem) continue;
422
432
  const payloadPath = path.join(root, `packages/registry/r/${item.registryItem}.json`);
423
433
  try {
424
434
  const payload = await json(payloadPath);
425
435
  const expected = `pnpm dlx shadcn@latest add @noe/${item.registryItem}`;
426
436
  if (payload.install !== expected) {
427
- fail(`install command mismatch for ${item.registryItem}: expected "${expected}" got "${payload.install}"`);
437
+ fail(
438
+ `install command mismatch for ${item.registryItem}: expected "${expected}" got "${payload.install}"`,
439
+ );
428
440
  errors++;
429
441
  badInstall++;
430
442
  }
@@ -443,7 +455,9 @@ if (errors === 0) pass('semantic search fields');
443
455
  errors++;
444
456
  plannedLeak++;
445
457
  }
446
- const rPath = item.registryItem ? path.join(root, `packages/registry/r/${item.registryItem}.json`) : null;
458
+ const rPath = item.registryItem
459
+ ? path.join(root, `packages/registry/r/${item.registryItem}.json`)
460
+ : null;
447
461
  if (rPath) {
448
462
  try {
449
463
  await stat(rPath);
@@ -462,7 +476,10 @@ if (errors === 0) pass('semantic search fields');
462
476
  // This check ensures manifest correctly marks preview contract via status
463
477
  let badPreview = 0;
464
478
  for (const item of manifest.items) {
465
- if (item.status === 'planned' && item.examples.some((e) => e.files.some((f) => f.includes('stories')))) {
479
+ if (
480
+ item.status === 'planned' &&
481
+ item.examples.some((e) => e.files.some((f) => f.includes('stories')))
482
+ ) {
466
483
  // Planned items should not have interactive story files that imply implementation
467
484
  // We allow story files for planned only if they are spec placeholders — currently all planned share generic stories, so we skip
468
485
  }
@@ -160,11 +160,22 @@ async function testConsumer({ name, rsc, items }) {
160
160
  await run('pnpm', ['install'], { cwd: consumer });
161
161
 
162
162
  // Batch install all items in one shadcn invocation (serial but batched for speed) — still verifies every payload individually
163
- console.log(`\n[${name}] Installing ${items.length} payloads: ${items.slice(0, 3).join(', ')}...`);
163
+ console.log(
164
+ `\n[${name}] Installing ${items.length} payloads: ${items.slice(0, 3).join(', ')}...`,
165
+ );
164
166
  try {
165
167
  const { stdout, stderr } = await run(
166
168
  'pnpm',
167
- ['dlx', 'shadcn@latest', 'add', ...items.map((i) => `@noe/${i}`), '--yes', '--overwrite', '--cwd', consumer],
169
+ [
170
+ 'dlx',
171
+ 'shadcn@latest',
172
+ 'add',
173
+ ...items.map((i) => `@noe/${i}`),
174
+ '--yes',
175
+ '--overwrite',
176
+ '--cwd',
177
+ consumer,
178
+ ],
168
179
  { cwd: root },
169
180
  );
170
181
  if (stdout) process.stdout.write(stdout);
@@ -180,11 +191,18 @@ async function testConsumer({ name, rsc, items }) {
180
191
  const files = await walk(consumer);
181
192
  let payloadCheck;
182
193
  try {
183
- payloadCheck = JSON.parse(await readFile(path.join(root, `packages/registry/r/${item}.json`), 'utf8'));
194
+ payloadCheck = JSON.parse(
195
+ await readFile(path.join(root, `packages/registry/r/${item}.json`), 'utf8'),
196
+ );
184
197
  } catch {}
185
- const expectedBasenames = payloadCheck ? payloadCheck.files.map((f) => path.basename(f.path)) : [item];
198
+ const expectedBasenames = payloadCheck
199
+ ? payloadCheck.files.map((f) => path.basename(f.path))
200
+ : [item];
186
201
  const found = files.find((f) => expectedBasenames.some((b) => f.endsWith(b)));
187
- assert.ok(found, `[${name}] shadcn did not install ${item}; expected one of ${expectedBasenames.join(', ')}, found: ${files.slice(0, 30).join(', ')}`);
202
+ assert.ok(
203
+ found,
204
+ `[${name}] shadcn did not install ${item}; expected one of ${expectedBasenames.join(', ')}, found: ${files.slice(0, 30).join(', ')}`,
205
+ );
188
206
  console.log(`[${name}] Verified ${item} -> ${found}`);
189
207
  for (const pf of payloadCheck?.files || []) {
190
208
  const targetPath = pf.target || pf.path;
@@ -231,7 +249,10 @@ async function testConsumer({ name, rsc, items }) {
231
249
  }
232
250
 
233
251
  // Build via vite to satisfy production build requirement — per-item import verification already done via tsc
234
- await writeFile(path.join(consumer, 'src/entry.tsx'), `import * as React from 'react';\nexport const ok = true;\n`);
252
+ await writeFile(
253
+ path.join(consumer, 'src/entry.tsx'),
254
+ `import * as React from 'react';\nexport const ok = true;\n`,
255
+ );
235
256
  await run('pnpm', ['exec', 'vite', 'build'], { cwd: consumer });
236
257
  console.log(`[${name}] vite build PASS for all ${items.length} items`);
237
258
 
@@ -307,7 +328,12 @@ try {
307
328
  skipped: planned.length,
308
329
  skippedIds: planned,
309
330
  installable: available,
310
- results: results.map((r) => ({ name: r.name, verified: r.verified, failed: r.failed, failures: r.failures })),
331
+ results: results.map((r) => ({
332
+ name: r.name,
333
+ verified: r.verified,
334
+ failed: r.failed,
335
+ failures: r.failures,
336
+ })),
311
337
  };
312
338
  await writeFile(
313
339
  path.join(root, 'artifacts/registry-consumer-report.json'),
@@ -3,7 +3,9 @@ import { expect, test } from '@playwright/test';
3
3
  import { CATALOG_URL } from '../../playwright.config';
4
4
 
5
5
  test.describe('Catalog locale typography', () => {
6
- test('Persian locale waits for IRANYekanX and renders Persian with correct font', async ({ page }) => {
6
+ test('Persian locale waits for IRANYekanX and renders Persian with correct font', async ({
7
+ page,
8
+ }) => {
7
9
  await page.goto(`${CATALOG_URL}?locale=fa`);
8
10
  // Wait for effectiveLocale to become fa (font ready) — html lang becomes fa only after font check passes
9
11
  await expect(page.locator('html')).toHaveAttribute('lang', 'fa', { timeout: 10000 });
@@ -28,7 +30,8 @@ test.describe('Catalog locale typography', () => {
28
30
  // Find an English technical value inside the Persian page (e.g., Bidi preview's TechnicalValue)
29
31
  const englishInRtl = page.locator('text=a1b2c3d4').first();
30
32
  // If not found, fallback to any LTR isolated element
31
- const target = (await englishInRtl.count()) > 0 ? englishInRtl : page.locator('[dir="ltr"]').first();
33
+ const target =
34
+ (await englishInRtl.count()) > 0 ? englishInRtl : page.locator('[dir="ltr"]').first();
32
35
  await expect(target).toBeVisible();
33
36
  const fontFamily = await target.evaluate((el) => getComputedStyle(el).fontFamily);
34
37
  // Should NOT be IRANYekanX, should be SF Pro or fallback
@@ -1,11 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
- "lib": [
5
- "ES2022",
6
- "DOM",
7
- "DOM.Iterable"
8
- ],
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
9
5
  "module": "ESNext",
10
6
  "moduleResolution": "Bundler",
11
7
  "jsx": "react-jsx",
@@ -19,51 +15,21 @@
19
15
  "declarationMap": true,
20
16
  "sourceMap": true,
21
17
  "paths": {
22
- "@noe/design-tokens": [
23
- "./packages/design-tokens/src/index.ts"
24
- ],
25
- "@noe/design-tokens/*": [
26
- "./packages/design-tokens/src/*"
27
- ],
28
- "@noe/design-tokens/palette": [
29
- "./packages/design-tokens/src/palette.ts"
30
- ],
31
- "@noe/ui": [
32
- "./packages/ui/src/index.ts"
33
- ],
34
- "@noe/ui/*": [
35
- "./packages/ui/src/*"
36
- ],
37
- "@noe/i18n": [
38
- "./packages/i18n/src/index.ts"
39
- ],
40
- "@noe/i18n/*": [
41
- "./packages/i18n/src/*"
42
- ],
43
- "@noe/data-ui": [
44
- "./packages/data-ui/src/index.ts"
45
- ],
46
- "@noe/data-ui/*": [
47
- "./packages/data-ui/src/*"
48
- ],
49
- "@noe/ai-ui": [
50
- "./packages/ai-ui/src/index.ts"
51
- ],
52
- "@noe/ai-ui/*": [
53
- "./packages/ai-ui/src/*"
54
- ],
55
- "@noe/workflow-ui": [
56
- "./packages/workflow-ui/src/index.ts"
57
- ],
58
- "@noe/workflow-ui/*": [
59
- "./packages/workflow-ui/src/*"
60
- ],
61
- "@noe/icons": [
62
- "./packages/icons/src/index.tsx"
63
- ],
64
- "@noe/icons/*": [
65
- "./packages/icons/src/*"
66
- ]
18
+ "@noe/design-tokens": ["./packages/design-tokens/src/index.ts"],
19
+ "@noe/design-tokens/*": ["./packages/design-tokens/src/*"],
20
+ "@noe/design-tokens/palette": ["./packages/design-tokens/src/palette.ts"],
21
+ "@noe/ui": ["./packages/ui/src/index.ts"],
22
+ "@noe/ui/*": ["./packages/ui/src/*"],
23
+ "@noe/i18n": ["./packages/i18n/src/index.ts"],
24
+ "@noe/i18n/*": ["./packages/i18n/src/*"],
25
+ "@noe/data-ui": ["./packages/data-ui/src/index.ts"],
26
+ "@noe/data-ui/*": ["./packages/data-ui/src/*"],
27
+ "@noe/ai-ui": ["./packages/ai-ui/src/index.ts"],
28
+ "@noe/ai-ui/*": ["./packages/ai-ui/src/*"],
29
+ "@noe/workflow-ui": ["./packages/workflow-ui/src/index.ts"],
30
+ "@noe/workflow-ui/*": ["./packages/workflow-ui/src/*"],
31
+ "@noe/icons": ["./packages/icons/src/index.tsx"],
32
+ "@noe/icons/*": ["./packages/icons/src/*"]
67
33
  }
68
34
  }
69
- }
35
+ }