@almadar/ui 6.53.1 → 6.55.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 (120) hide show
  1. package/dist/{AwaitingSkeletonContext-Bh399p1x.d.ts → AwaitingSkeletonContext-DPgW__WV.d.ts} +3 -1
  2. package/dist/{Canvas3DHost-B-y2TNij.d.ts → Canvas3DHost-DfAhg6EH.d.ts} +2 -0
  3. package/dist/Typography-7PDT62PG.js +5 -0
  4. package/dist/{UISlotRenderer-P7A4SZSK.js → UISlotRenderer-QTHFT6Z4.js} +8 -8
  5. package/dist/avl/index.cjs +3031 -1790
  6. package/dist/avl/index.js +57 -745
  7. package/dist/{avl-3d-context-MAt_NuU9.d.ts → avl-3d-context-BpBKZIbc.d.ts} +1 -0
  8. package/dist/{chunk-ERXIHZW3.js → chunk-D2E44ODB.js} +29 -15
  9. package/dist/{chunk-UOTYAPV5.js → chunk-F4ZTPKZL.js} +3 -3
  10. package/dist/{chunk-53VTY5CC.js → chunk-FQM37C56.js} +352 -162
  11. package/dist/{chunk-HB4C27KU.js → chunk-HCSJHRC4.js} +1 -1
  12. package/dist/{chunk-7KSLPY56.js → chunk-HRCR53OW.js} +11 -1
  13. package/dist/{chunk-O7OLVVBR.js → chunk-Q47ZN2DW.js} +24 -22
  14. package/dist/{chunk-DQTV6QVS.js → chunk-QGH4NAQO.js} +53 -2
  15. package/dist/{chunk-QVXLKFUQ.js → chunk-RNY5LJXJ.js} +1 -1
  16. package/dist/{chunk-FUQASYEW.js → chunk-SGJHVPHC.js} +2 -2
  17. package/dist/{chunk-6O37FCX4.js → chunk-V3FGTQUE.js} +37 -2
  18. package/dist/{chunk-HUG25TT3.js → chunk-XZKGRSIO.js} +2745 -1644
  19. package/dist/{chunk-7LM6I3NA.js → chunk-Y53Q55H5.js} +522 -24
  20. package/dist/components/index.cjs +4614 -2858
  21. package/dist/components/index.d.ts +122 -53
  22. package/dist/components/index.js +16 -17
  23. package/dist/context/index.cjs +38 -1
  24. package/dist/context/index.d.ts +1 -1
  25. package/dist/context/index.js +3 -1
  26. package/dist/design-classes/index.cjs +29 -15
  27. package/dist/design-classes/index.d.cts +1 -0
  28. package/dist/design-classes/index.d.ts +1 -0
  29. package/dist/design-classes/index.js +29 -15
  30. package/dist/hooks/index.cjs +16 -1
  31. package/dist/hooks/index.d.ts +10 -1
  32. package/dist/hooks/index.js +3 -4
  33. package/dist/lib/drawable/three/index.cjs +444 -82
  34. package/dist/lib/drawable/three/index.d.ts +4 -4
  35. package/dist/lib/drawable/three/index.js +28 -24
  36. package/dist/lib/index.cjs +53 -3
  37. package/dist/lib/index.d.ts +1 -1
  38. package/dist/lib/index.js +4 -4
  39. package/dist/locales/index.cjs +33 -3
  40. package/dist/locales/index.js +23 -3
  41. package/dist/providers/index.cjs +4987 -3183
  42. package/dist/providers/index.d.ts +2 -2
  43. package/dist/providers/index.js +14 -11
  44. package/dist/runtime/index.cjs +8973 -7000
  45. package/dist/runtime/index.d.ts +28 -6
  46. package/dist/runtime/index.js +16 -13
  47. package/dist/ssr/index.cjs +26 -9
  48. package/dist/ssr/index.js +26 -9
  49. package/dist/{themeTokens-Ccu7EmGY.d.ts → themeTokens-D6MFwKrC.d.ts} +6 -2
  50. package/locales/ar.json +11 -1
  51. package/locales/en.json +11 -1
  52. package/locales/sl.json +11 -1
  53. package/package.json +8 -7
  54. package/scripts/audit-tailwind-safelist.ts +21 -1
  55. package/scripts/theme-contrast-audit.mjs +20 -1
  56. package/tailwind-preset.cjs +5 -6
  57. package/themes/_base.css +61 -0
  58. package/themes/_contract.md +37 -0
  59. package/themes/_fonts.css +6 -4
  60. package/themes/_presets.css +2 -0
  61. package/themes/almadar-heritage.css +455 -0
  62. package/themes/almadar-website.css +82 -0
  63. package/themes/almadar.css +82 -0
  64. package/themes/aqua.css +82 -0
  65. package/themes/arabesque.css +82 -0
  66. package/themes/art-deco.css +82 -0
  67. package/themes/art-nouveau.css +82 -0
  68. package/themes/atelier.css +82 -0
  69. package/themes/bauhaus.css +82 -0
  70. package/themes/bloomberg-dense.css +83 -1
  71. package/themes/blueprint.css +84 -0
  72. package/themes/clay.css +82 -0
  73. package/themes/comic.css +82 -0
  74. package/themes/constructivist.css +82 -0
  75. package/themes/corporate.css +82 -0
  76. package/themes/cyberpunk.css +82 -0
  77. package/themes/frutiger-aero.css +83 -0
  78. package/themes/game-adventure.css +15 -0
  79. package/themes/game-rpg.css +16 -0
  80. package/themes/game-sci-fi.css +15 -0
  81. package/themes/game-ui-pack.css +15 -0
  82. package/themes/gazette.css +82 -0
  83. package/themes/glass.css +82 -0
  84. package/themes/gothic.css +82 -0
  85. package/themes/ink-wash.css +82 -0
  86. package/themes/kawaii.css +82 -0
  87. package/themes/kiosk.css +82 -0
  88. package/themes/linear-clean.css +82 -0
  89. package/themes/luxury.css +82 -0
  90. package/themes/mac-classic.css +90 -8
  91. package/themes/material.css +82 -0
  92. package/themes/memphis.css +82 -0
  93. package/themes/mid-century.css +82 -0
  94. package/themes/minimalist.css +82 -0
  95. package/themes/mudcloth.css +82 -0
  96. package/themes/neon.css +83 -0
  97. package/themes/neumorphic.css +82 -0
  98. package/themes/newsprint.css +82 -0
  99. package/themes/notion-editorial.css +82 -0
  100. package/themes/orb.css +455 -0
  101. package/themes/pixel.css +82 -0
  102. package/themes/prism.css +82 -0
  103. package/themes/retro.css +83 -0
  104. package/themes/risograph.css +82 -0
  105. package/themes/scandi.css +82 -0
  106. package/themes/sketch.css +82 -0
  107. package/themes/solarpunk.css +82 -0
  108. package/themes/steampunk.css +82 -0
  109. package/themes/swiss.css +82 -0
  110. package/themes/terminal.css +84 -1
  111. package/themes/trait-wars.css +82 -0
  112. package/themes/ukiyo-e.css +82 -0
  113. package/themes/vaporwave.css +82 -0
  114. package/themes/victorian.css +82 -0
  115. package/themes/wabi-sabi.css +82 -0
  116. package/themes/win95.css +84 -0
  117. package/themes/wireframe.css +82 -0
  118. package/themes/zine.css +82 -0
  119. package/dist/Typography-7QEWUTW3.js +0 -5
  120. package/dist/chunk-E37BYSXY.js +0 -51
@@ -1,9 +1,9 @@
1
- import { EventPayload, OrbitalDefinition, ServiceParams, TraitConfig, UserContext, ResolvedTraitBinding, ResolvedPage, ResolvedEntity, ResolvedTrait, ResolvedIR, OrbitalSchema, PatternConfig, ResolvedPatternProps, EventSource, SExpr, EntityData } from '@almadar/core';
1
+ import { EventPayload, OrbitalDefinition, ServiceParams, TraitConfig, UserContext, ResolvedTraitBinding, ResolvedPage, ResolvedEntity, ResolvedTrait, ResolvedIR, OrbitalSchema, PatternConfig, ResolvedPatternProps, EventSource, SExpr, EntityData, MessageCatalogs } from '@almadar/core';
2
2
  export { ResolvedEntity, ResolvedIR, ResolvedPage, ResolvedTrait, ResolvedTraitBinding } from '@almadar/core';
3
- import { TraitState, EventTransport, PersistenceAdapter, AccessTokenProvider } from '@almadar/runtime';
3
+ import { TraitState, EventTransport, PersistenceAdapter, AccessTokenProvider, SchemaLoader } from '@almadar/runtime';
4
4
  export { AccessTokenProvider, ClientEventBus, CreateClientEffectHandlersOptions, SlotSetter, createClientEffectHandlers } from '@almadar/runtime';
5
- import { E as EntityBindingSource, A as AwaitingSkeletonSource, S as ServerBridgeTransport } from '../AwaitingSkeletonContext-Bh399p1x.js';
6
- export { a as EntitySchemaContextValue, b as EntitySchemaProvider, c as EntitySchemaProviderProps, d as ServerBridgeContextValue, e as ServerBridgeProvider, T as TraitContext, f as TraitContextValue, g as TraitInstance, h as TraitProvider, i as TraitProviderProps, u as useEntitySchema, j as useEntitySchemaOptional, k as useServerBridge, l as useTrait, m as useTraitContext } from '../AwaitingSkeletonContext-Bh399p1x.js';
5
+ import { E as EntityBindingSource, A as AwaitingSkeletonSource, S as ServerBridgeTransport } from '../AwaitingSkeletonContext-DPgW__WV.js';
6
+ export { a as EntitySchemaContextValue, b as EntitySchemaProvider, c as EntitySchemaProviderProps, d as ServerBridgeContextValue, e as ServerBridgeProvider, T as TraitContext, f as TraitContextValue, g as TraitInstance, h as TraitProvider, i as TraitProviderProps, u as useEntitySchema, j as useEntitySchemaOptional, k as useServerBridge, l as useTrait, m as useTraitContext } from '../AwaitingSkeletonContext-DPgW__WV.js';
7
7
  import { c as useUISlots } from '../UISlotContext-CS487Uoc.js';
8
8
  export { PERF_NAMESPACE, PerfEntry, PreparedPreviewSchema, adjustSchemaForMockData, buildMockData, clearPerf, perfEnd, perfStart, perfTime, prepareSchemaForPreview, wrapCallbackForEvent } from '@almadar/runtime/ui';
9
9
  import React__default, { ReactNode } from 'react';
@@ -44,6 +44,8 @@ interface UseTraitStateMachineOptions {
44
44
  /** Hold every dispatch until the transport's topology is known. */
45
45
  awaitTopology?: boolean;
46
46
  persistence?: PersistenceAdapter;
47
+ /** The browser store for `[persistent: x, local]` entities (see `useCircuitKernel`). */
48
+ browserStore?: PersistenceAdapter;
47
49
  callService?: (service: string, action: string, params?: ServiceParams) => Promise<EventPayload>;
48
50
  traitConfigsByName?: Record<string, TraitConfig>;
49
51
  embeddedTraits?: ReadonlySet<string>;
@@ -232,6 +234,18 @@ interface OrbPreviewProps {
232
234
  * picker). Omitted → each page renders in its own orbital's theme.
233
235
  */
234
236
  themeOverride?: string;
237
+ /**
238
+ * Loads the behavior behind a `lazyPages` entry (`uses lazy`) when its page
239
+ * is opened. Without it, opening a lazy page shows a load error.
240
+ */
241
+ lazyLoader?: SchemaLoader;
242
+ /** Where `schema` was loaded from — a lazy page's `orbRef` resolves against it. */
243
+ schemaPath?: string;
244
+ /**
245
+ * Called with a path that matches none of this schema's pages — a lazily
246
+ * loaded behavior hands links back to the schema that imported it.
247
+ */
248
+ onUnmatchedNavigate?: (path: string) => void;
235
249
  }
236
250
  /**
237
251
  * Renders a live preview of an Orbital schema.
@@ -246,7 +260,7 @@ interface OrbPreviewProps {
246
260
  * <OrbPreview schema={schema} serverUrl="/api/orbitals" />
247
261
  * ```
248
262
  */
249
- declare function OrbPreview({ schema, mockData, autoMock, height, className, serverUrl, transport, getAccessToken, initialPagePath, isolated, fit, onPageChange, user, localFallbackTimeoutMs, themeOverride, }: OrbPreviewProps): React__default.ReactElement;
263
+ declare function OrbPreview({ schema, mockData, autoMock, height, className, serverUrl, transport, getAccessToken, initialPagePath, isolated, fit, onPageChange, user, localFallbackTimeoutMs, themeOverride, lazyLoader, schemaPath, onUnmatchedNavigate, }: OrbPreviewProps): React__default.ReactElement;
250
264
  declare namespace OrbPreview {
251
265
  var displayName: string;
252
266
  }
@@ -290,8 +304,16 @@ interface BrowserPlaygroundProps {
290
304
  onPageChange?: (path: string) => void;
291
305
  /** Who is viewing (`@user`). Default: the app's first seeded persona. */
292
306
  viewer?: UserContext;
307
+ /** The program's message catalogs (`orb resolve`'s `<stem>.<locale>.json`), for a schema that declares `locales`. */
308
+ messages?: MessageCatalogs;
309
+ /** The viewer's locale. Default: the schema's first declared locale. */
310
+ locale?: string;
311
+ /** Loads each `lazyPages` behavior (`uses lazy`): the in-browser runtime runs them with the program, as a server does. */
312
+ lazyLoader?: SchemaLoader;
313
+ /** Where `schema` was loaded from — a lazy page's `orbRef` resolves against it. */
314
+ schemaPath?: string;
293
315
  }
294
- declare function BrowserPlayground({ schema, mode, initialPagePath, height, className, paused, fit, onPageChange, viewer, }: BrowserPlaygroundProps): React__default.ReactElement;
316
+ declare function BrowserPlayground({ schema, mode, initialPagePath, height, className, paused, fit, onPageChange, viewer, messages, locale, lazyLoader, schemaPath, }: BrowserPlaygroundProps): React__default.ReactElement;
295
317
 
296
318
  /**
297
319
  * OrbitalPluginHost — runs an ordinary behavior headless against the
@@ -1,20 +1,20 @@
1
- export { BrowserPlayground, OrbPreview, adjustSchemaForMockData, buildMockData, clearSchemaCache, prepareSchemaForPreview, useResolvedSchema, useTraitStateMachine } from '../chunk-53VTY5CC.js';
1
+ export { BrowserPlayground, OrbPreview, adjustSchemaForMockData, buildMockData, clearSchemaCache, prepareSchemaForPreview, useResolvedSchema, useTraitStateMachine } from '../chunk-FQM37C56.js';
2
2
  export { assertUniqueSlotsPerHost } from '../chunk-O2EIWXWE.js';
3
- import '../chunk-DQTV6QVS.js';
3
+ import '../chunk-QGH4NAQO.js';
4
4
  import '../chunk-VHTJRS2N.js';
5
- export { wrapCallbackForEvent } from '../chunk-HUG25TT3.js';
6
- import '../chunk-PQKU62LK.js';
7
- import '../chunk-7LM6I3NA.js';
8
- import '../chunk-HB4C27KU.js';
5
+ export { wrapCallbackForEvent } from '../chunk-XZKGRSIO.js';
6
+ import '../chunk-Y53Q55H5.js';
7
+ import '../chunk-HCSJHRC4.js';
9
8
  import '../chunk-JRW33U35.js';
10
9
  import '../chunk-XJTHMW4G.js';
11
10
  import { useEventBus } from '../chunk-6EV3OEBQ.js';
12
- import '../chunk-PEST66PN.js';
13
11
  export { PERF_NAMESPACE, clearPerf, perfEnd, perfStart, perfTime, profilerOnRender, usePerfBuffer } from '../chunk-KQXXO42S.js';
14
- import '../chunk-O7OLVVBR.js';
12
+ import '../chunk-Q47ZN2DW.js';
15
13
  import '../chunk-64WACXM7.js';
16
- import '../chunk-QVXLKFUQ.js';
17
- import '../chunk-7KSLPY56.js';
14
+ import '../chunk-RNY5LJXJ.js';
15
+ import '../chunk-HRCR53OW.js';
16
+ import '../chunk-PQKU62LK.js';
17
+ import '../chunk-PEST66PN.js';
18
18
  import '../chunk-QPXIAUJD.js';
19
19
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
20
20
  export { createClientEffectHandlers } from '@almadar/runtime';
@@ -22,6 +22,7 @@ import { createContext, useMemo, useState, useRef, useCallback, useContext, useE
22
22
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
23
23
  import { UI_SLOTS } from '@almadar/core';
24
24
  import { createLogger } from '@almadar/logger';
25
+ import { useTranslate } from '@almadar/ui/hooks';
25
26
  import { useUISlots } from '@almadar/ui/context';
26
27
  import { jsxs, jsx } from 'react/jsx-runtime';
27
28
 
@@ -145,6 +146,7 @@ function PluginRuntimeMount({
145
146
  onError
146
147
  }) {
147
148
  const bus = useEventBus();
149
+ const { locale } = useTranslate();
148
150
  const slots = useUISlots();
149
151
  const slotsRef = useRef(slots);
150
152
  slotsRef.current = slots;
@@ -271,10 +273,11 @@ function PluginRuntimeMount({
271
273
  response = await mockRuntime.processOrbitalEvent(row.orbital, {
272
274
  event: row.trigger,
273
275
  payload,
274
- targetTrait: row.trait
276
+ targetTrait: row.trait,
277
+ locale
275
278
  });
276
279
  } else if (mode === "server" && transport) {
277
- response = await transport.send(row.orbital, { event: row.trigger, payload });
280
+ response = await transport.send(row.orbital, { event: row.trigger, payload, locale });
278
281
  } else {
279
282
  return;
280
283
  }
@@ -298,7 +301,7 @@ function PluginRuntimeMount({
298
301
  }
299
302
  onDispatched(plugin.id, row.trigger, states);
300
303
  },
301
- [registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, inboundBusEvents, onError, onTransition, onDispatched]
304
+ [registrationReady, mockRuntime, mode, transport, locale, plugin, bus, ownOrbitals, inboundBusEvents, onError, onTransition, onDispatched]
302
305
  );
303
306
  useEffect(() => {
304
307
  const unsubs = plugin.inbound.map(
@@ -3144,6 +3144,7 @@ var en_default = {
3144
3144
  "status.draft": "Draft",
3145
3145
  "status.archived": "Archived",
3146
3146
  "error.generic": "Something went wrong",
3147
+ "error.traitMountFailed": "{{trait}} failed to start",
3147
3148
  "error.retry": "Try again",
3148
3149
  "error.notFound": "Not found",
3149
3150
  "error.loadFailed": "Failed to load: {{message}}",
@@ -3272,6 +3273,7 @@ var en_default = {
3272
3273
  "template.ourTeam": "Our Team",
3273
3274
  "template.caseStudies": "Case Studies",
3274
3275
  "versionDiff.compare": "Compare",
3276
+ "versionDiff.changes": "Changes",
3275
3277
  "versionDiff.to": "to",
3276
3278
  "versionDiff.beforeRevision": "Before revision",
3277
3279
  "versionDiff.afterRevision": "After revision",
@@ -3998,6 +4000,7 @@ var en_default = {
3998
4000
  "gridPicker.moreRefine": "+{{count}} more \u2014 refine your search",
3999
4001
  "graphCanvas.noNodes": "No nodes to display.",
4000
4002
  "codeBlock.noCode": "No code to display.",
4003
+ "codeBlock.region": "Code",
4001
4004
  "fab.action": "Action",
4002
4005
  "fab.closeActions": "Close actions",
4003
4006
  "fab.openActions": "Open actions",
@@ -4314,7 +4317,14 @@ var en_default = {
4314
4317
  "agentActivity.earlierConversations": "Earlier conversations ({{count}})",
4315
4318
  "agentActivity.earlierActivity": "Earlier activity",
4316
4319
  "tabs.scrollStart": "Scroll tabs back",
4317
- "tabs.scrollEnd": "Scroll tabs forward"
4320
+ "tabs.scrollEnd": "Scroll tabs forward",
4321
+ "agentChat.workedThrough": "Worked through {{count}} steps",
4322
+ "agentChat.newMessages": "New messages",
4323
+ "agentChat.copy": "Copy reply",
4324
+ "agentChat.copied": "Copied",
4325
+ "agentChat.today": "Today",
4326
+ "agentChat.yesterday": "Yesterday",
4327
+ "agentChat.emptyDescription": "Ask in your own words \u2014 the assistant can look things up and make changes for you."
4318
4328
  };
4319
4329
 
4320
4330
  // lib/format.ts
@@ -4596,12 +4606,16 @@ function themeStrokeApplies() {
4596
4606
  return familyStrokeApplies(themeIconFamily());
4597
4607
  }
4598
4608
  function scopedIconFamily(el) {
4599
- for (let node = el; node; node = node.parentElement) {
4600
- const raw = getComputedStyle(node).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
4601
- if (raw !== "") return raw;
4602
- }
4603
- return "";
4609
+ const scope = el.closest("[data-theme]");
4610
+ if (!scope || scope === document.documentElement) return null;
4611
+ const known = scopedFamilies.get(scope);
4612
+ if (known && known.version === themeVersion) return known.family;
4613
+ const raw = getComputedStyle(scope).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
4614
+ const family = raw === "" ? null : raw;
4615
+ scopedFamilies.set(scope, { version: themeVersion, family });
4616
+ return family;
4604
4617
  }
4618
+ var scopedFamilies = /* @__PURE__ */ new WeakMap();
4605
4619
  var cachedFamily = null;
4606
4620
  var cachedStrokeApplies = null;
4607
4621
  var themeVersion = 0;
@@ -4665,7 +4679,9 @@ function useThemeIconStrokeApplies(ref) {
4665
4679
  const version = React17.useSyncExternalStore(subscribeIconFamily, getThemeVersionSnapshot, () => 0);
4666
4680
  const [scopedApplies, setScopedApplies] = React17.useState(null);
4667
4681
  React17.useLayoutEffect(() => {
4668
- if (ref.current) setScopedApplies(familyStrokeApplies(scopedIconFamily(ref.current)));
4682
+ if (!ref.current) return;
4683
+ const scoped = scopedIconFamily(ref.current);
4684
+ setScopedApplies(scoped === null ? null : familyStrokeApplies(scoped));
4669
4685
  }, [ref, version]);
4670
4686
  return scopedApplies ?? documentApplies;
4671
4687
  }
@@ -5044,7 +5060,8 @@ var variantStyles2 = {
5044
5060
  "active:scale-[var(--active-scale)] active:shadow-elevation-pressed"
5045
5061
  ].join(" "),
5046
5062
  secondary: [
5047
- "bg-transparent text-accent",
5063
+ // The accent is often a signature bright that fails as text on a light page.
5064
+ "bg-transparent text-foreground",
5048
5065
  "border border-accent",
5049
5066
  "hover:bg-accent hover:text-accent-foreground hover:border-accent",
5050
5067
  "active:scale-[var(--active-scale)]"
@@ -5193,7 +5210,7 @@ var Button = React17__namespace.default.forwardRef(
5193
5210
  const testId = dataTestId ?? (action ? `action-${action}` : void 0);
5194
5211
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5195
5212
  busy && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "almadar-busy-indicator absolute inset-0 flex items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons2.Loader2, { className: "h-icon-default w-icon-default animate-spin" }) }),
5196
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("inline-flex items-center justify-center gap-2", busy && "almadar-busy-content"), children: [
5213
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("inline-flex flex-1 min-w-0 items-center [justify-content:inherit] gap-2", busy && "almadar-busy-content"), children: [
5197
5214
  resolvedLeftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
5198
5215
  (Array.isArray(children) ? children.length > 0 : Boolean(children)) ? children : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { ...{ [INLINE_TEXT_ATTR]: "label" }, children: label }) : label,
5199
5216
  resolvedRightIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
package/dist/ssr/index.js CHANGED
@@ -3123,6 +3123,7 @@ var en_default = {
3123
3123
  "status.draft": "Draft",
3124
3124
  "status.archived": "Archived",
3125
3125
  "error.generic": "Something went wrong",
3126
+ "error.traitMountFailed": "{{trait}} failed to start",
3126
3127
  "error.retry": "Try again",
3127
3128
  "error.notFound": "Not found",
3128
3129
  "error.loadFailed": "Failed to load: {{message}}",
@@ -3251,6 +3252,7 @@ var en_default = {
3251
3252
  "template.ourTeam": "Our Team",
3252
3253
  "template.caseStudies": "Case Studies",
3253
3254
  "versionDiff.compare": "Compare",
3255
+ "versionDiff.changes": "Changes",
3254
3256
  "versionDiff.to": "to",
3255
3257
  "versionDiff.beforeRevision": "Before revision",
3256
3258
  "versionDiff.afterRevision": "After revision",
@@ -3977,6 +3979,7 @@ var en_default = {
3977
3979
  "gridPicker.moreRefine": "+{{count}} more \u2014 refine your search",
3978
3980
  "graphCanvas.noNodes": "No nodes to display.",
3979
3981
  "codeBlock.noCode": "No code to display.",
3982
+ "codeBlock.region": "Code",
3980
3983
  "fab.action": "Action",
3981
3984
  "fab.closeActions": "Close actions",
3982
3985
  "fab.openActions": "Open actions",
@@ -4293,7 +4296,14 @@ var en_default = {
4293
4296
  "agentActivity.earlierConversations": "Earlier conversations ({{count}})",
4294
4297
  "agentActivity.earlierActivity": "Earlier activity",
4295
4298
  "tabs.scrollStart": "Scroll tabs back",
4296
- "tabs.scrollEnd": "Scroll tabs forward"
4299
+ "tabs.scrollEnd": "Scroll tabs forward",
4300
+ "agentChat.workedThrough": "Worked through {{count}} steps",
4301
+ "agentChat.newMessages": "New messages",
4302
+ "agentChat.copy": "Copy reply",
4303
+ "agentChat.copied": "Copied",
4304
+ "agentChat.today": "Today",
4305
+ "agentChat.yesterday": "Yesterday",
4306
+ "agentChat.emptyDescription": "Ask in your own words \u2014 the assistant can look things up and make changes for you."
4297
4307
  };
4298
4308
 
4299
4309
  // lib/format.ts
@@ -4575,12 +4585,16 @@ function themeStrokeApplies() {
4575
4585
  return familyStrokeApplies(themeIconFamily());
4576
4586
  }
4577
4587
  function scopedIconFamily(el) {
4578
- for (let node = el; node; node = node.parentElement) {
4579
- const raw = getComputedStyle(node).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
4580
- if (raw !== "") return raw;
4581
- }
4582
- return "";
4588
+ const scope = el.closest("[data-theme]");
4589
+ if (!scope || scope === document.documentElement) return null;
4590
+ const known = scopedFamilies.get(scope);
4591
+ if (known && known.version === themeVersion) return known.family;
4592
+ const raw = getComputedStyle(scope).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
4593
+ const family = raw === "" ? null : raw;
4594
+ scopedFamilies.set(scope, { version: themeVersion, family });
4595
+ return family;
4583
4596
  }
4597
+ var scopedFamilies = /* @__PURE__ */ new WeakMap();
4584
4598
  var cachedFamily = null;
4585
4599
  var cachedStrokeApplies = null;
4586
4600
  var themeVersion = 0;
@@ -4644,7 +4658,9 @@ function useThemeIconStrokeApplies(ref) {
4644
4658
  const version = useSyncExternalStore(subscribeIconFamily, getThemeVersionSnapshot, () => 0);
4645
4659
  const [scopedApplies, setScopedApplies] = useState(null);
4646
4660
  useLayoutEffect(() => {
4647
- if (ref.current) setScopedApplies(familyStrokeApplies(scopedIconFamily(ref.current)));
4661
+ if (!ref.current) return;
4662
+ const scoped = scopedIconFamily(ref.current);
4663
+ setScopedApplies(scoped === null ? null : familyStrokeApplies(scoped));
4648
4664
  }, [ref, version]);
4649
4665
  return scopedApplies ?? documentApplies;
4650
4666
  }
@@ -5023,7 +5039,8 @@ var variantStyles2 = {
5023
5039
  "active:scale-[var(--active-scale)] active:shadow-elevation-pressed"
5024
5040
  ].join(" "),
5025
5041
  secondary: [
5026
- "bg-transparent text-accent",
5042
+ // The accent is often a signature bright that fails as text on a light page.
5043
+ "bg-transparent text-foreground",
5027
5044
  "border border-accent",
5028
5045
  "hover:bg-accent hover:text-accent-foreground hover:border-accent",
5029
5046
  "active:scale-[var(--active-scale)]"
@@ -5172,7 +5189,7 @@ var Button = React17__default.forwardRef(
5172
5189
  const testId = dataTestId ?? (action ? `action-${action}` : void 0);
5173
5190
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
5174
5191
  busy && /* @__PURE__ */ jsx("span", { className: "almadar-busy-indicator absolute inset-0 flex items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Loader2, { className: "h-icon-default w-icon-default animate-spin" }) }),
5175
- /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center justify-center gap-2", busy && "almadar-busy-content"), children: [
5192
+ /* @__PURE__ */ jsxs("span", { className: cn("inline-flex flex-1 min-w-0 items-center [justify-content:inherit] gap-2", busy && "almadar-busy-content"), children: [
5176
5193
  resolvedLeftIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: resolvedLeftIcon }),
5177
5194
  (Array.isArray(children) ? children.length > 0 : Boolean(children)) ? children : label ? /* @__PURE__ */ jsx("span", { ...{ [INLINE_TEXT_ATTR]: "label" }, children: label }) : label,
5178
5195
  resolvedRightIcon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: resolvedRightIcon })
@@ -1,4 +1,4 @@
1
- import { ThemeRef, ThemeDefinition, ThemeTokens, ThemeVariant, DensityTokens, ElevationTokens, GeometryTokens, IconographyTokens, MotionDurationKey, MotionEasingKey, MotionIntentMap, MotionShapeTokens, SurfaceTokens, TypeScaleTokens, TypeIntentMap, TypeSizeKey, EnterAnimation } from '@almadar/core';
1
+ import { ThemeRef, ThemeDefinition, ThemeTokens, ThemeVariant, DensityTokens, DiagramTokens, ElevationTokens, GeometryTokens, IconographyTokens, MotionDurationKey, MotionEasingKey, MotionIntentMap, MotionShapeTokens, SceneTokens, SurfaceTokens, TypeScaleTokens, TypeIntentMap, TypeSizeKey, EnterAnimation } from '@almadar/core';
2
2
 
3
3
  /**
4
4
  * themeTokens — runtime mirror of the orbital compiler's theme codegen.
@@ -81,6 +81,10 @@ declare const ELEVATION_VARS: ReadonlyArray<FlatVarEntry<ElevationTokens>>;
81
81
  declare const GEOMETRY_VARS: ReadonlyArray<FlatVarEntry<GeometryTokens>>;
82
82
  /** Surface axis — panel/page material. */
83
83
  declare const SURFACE_VARS: ReadonlyArray<FlatVarEntry<SurfaceTokens>>;
84
+ /** Diagram axis — how marks are drawn on every drawing surface. */
85
+ declare const DIAGRAM_VARS: ReadonlyArray<FlatVarEntry<DiagramTokens>>;
86
+ /** Scene axis — 3D material and lighting. */
87
+ declare const SCENE_VARS: ReadonlyArray<FlatVarEntry<SceneTokens>>;
84
88
  /** Legacy free-form map prefixes (`ThemeTokens.colors`/`radii`/`spacing`/`shadows`). `typography` has no prefix — see below. */
85
89
  declare const LEGACY_PREFIXES: {
86
90
  readonly colors: "--color-";
@@ -117,4 +121,4 @@ declare function enterKeyframe(enter: EnterAnimation): string;
117
121
  */
118
122
  declare function resolveThemeForRuntime(theme: ThemeRef | undefined): ThemeDefinition | undefined;
119
123
 
120
- export { DENSITY_ELEMENT_VARS as D, ELEVATION_VARS as E, type FlatVarEntry as F, GEOMETRY_VARS as G, ICONOGRAPHY_VARS as I, LEGACY_PREFIXES as L, MOTION_DURATION_KEYS as M, SURFACE_VARS as S, type ThemeMode as T, DENSITY_SPACING_VARS as a, MOTION_EASING_KEYS as b, MOTION_INTENT_VARS as c, MOTION_SHAPE_VARS as d, type StringValueKey as e, TYPE_FAMILY_VARS as f, TYPE_HEADING_VARS as g, TYPE_INTENT_VARS as h, TYPE_SIZE_KEYS as i, enterKeyframe as j, resolveThemeForRuntime as r, themeTokensToCssVars as t };
124
+ export { DENSITY_ELEMENT_VARS as D, ELEVATION_VARS as E, type FlatVarEntry as F, GEOMETRY_VARS as G, ICONOGRAPHY_VARS as I, LEGACY_PREFIXES as L, MOTION_DURATION_KEYS as M, SCENE_VARS as S, type ThemeMode as T, DENSITY_SPACING_VARS as a, DIAGRAM_VARS as b, MOTION_EASING_KEYS as c, MOTION_INTENT_VARS as d, MOTION_SHAPE_VARS as e, SURFACE_VARS as f, type StringValueKey as g, TYPE_FAMILY_VARS as h, TYPE_HEADING_VARS as i, TYPE_INTENT_VARS as j, TYPE_SIZE_KEYS as k, enterKeyframe as l, resolveThemeForRuntime as r, themeTokensToCssVars as t };
package/locales/ar.json CHANGED
@@ -57,6 +57,7 @@
57
57
  "status.draft": "مسودة",
58
58
  "status.archived": "مؤرشف",
59
59
  "error.generic": "حدث خطأ ما",
60
+ "error.traitMountFailed": "تعذّر بدء {{trait}}",
60
61
  "error.retry": "حاول مرة أخرى",
61
62
  "error.notFound": "غير موجود",
62
63
  "error.loadFailed": "فشل التحميل: {{message}}",
@@ -182,6 +183,7 @@
182
183
  "template.ourTeam": "فريقنا",
183
184
  "template.caseStudies": "دراسات الحالة",
184
185
  "versionDiff.compare": "مقارنة",
186
+ "versionDiff.changes": "التغييرات",
185
187
  "versionDiff.to": "إلى",
186
188
  "versionDiff.beforeRevision": "المراجعة السابقة",
187
189
  "versionDiff.afterRevision": "المراجعة اللاحقة",
@@ -912,6 +914,7 @@
912
914
  "gridPicker.moreRefine": "+{{count}} أخرى — دقّق بحثك",
913
915
  "graphCanvas.noNodes": "لا توجد عقد لعرضها.",
914
916
  "codeBlock.noCode": "لا يوجد كود لعرضه.",
917
+ "codeBlock.region": "الشيفرة البرمجية",
915
918
  "fab.action": "إجراء",
916
919
  "fab.closeActions": "إغلاق الإجراءات",
917
920
  "fab.openActions": "فتح الإجراءات",
@@ -1228,5 +1231,12 @@
1228
1231
  "agentActivity.earlierConversations": "المحادثات السابقة ({{count}})",
1229
1232
  "agentActivity.earlierActivity": "نشاط سابق",
1230
1233
  "tabs.scrollStart": "مرّر علامات التبويب للخلف",
1231
- "tabs.scrollEnd": "مرّر علامات التبويب للأمام"
1234
+ "tabs.scrollEnd": "مرّر علامات التبويب للأمام",
1235
+ "agentChat.workedThrough": "أنجز {{count}} خطوات",
1236
+ "agentChat.newMessages": "رسائل جديدة",
1237
+ "agentChat.copy": "نسخ الرد",
1238
+ "agentChat.copied": "تم النسخ",
1239
+ "agentChat.today": "اليوم",
1240
+ "agentChat.yesterday": "أمس",
1241
+ "agentChat.emptyDescription": "اسأل بكلماتك — يستطيع المساعد البحث عن المعلومات وإجراء التغييرات نيابةً عنك."
1232
1242
  }
package/locales/en.json CHANGED
@@ -58,6 +58,7 @@
58
58
  "status.draft": "Draft",
59
59
  "status.archived": "Archived",
60
60
  "error.generic": "Something went wrong",
61
+ "error.traitMountFailed": "{{trait}} failed to start",
61
62
  "error.retry": "Try again",
62
63
  "error.notFound": "Not found",
63
64
  "error.loadFailed": "Failed to load: {{message}}",
@@ -186,6 +187,7 @@
186
187
  "template.ourTeam": "Our Team",
187
188
  "template.caseStudies": "Case Studies",
188
189
  "versionDiff.compare": "Compare",
190
+ "versionDiff.changes": "Changes",
189
191
  "versionDiff.to": "to",
190
192
  "versionDiff.beforeRevision": "Before revision",
191
193
  "versionDiff.afterRevision": "After revision",
@@ -912,6 +914,7 @@
912
914
  "gridPicker.moreRefine": "+{{count}} more — refine your search",
913
915
  "graphCanvas.noNodes": "No nodes to display.",
914
916
  "codeBlock.noCode": "No code to display.",
917
+ "codeBlock.region": "Code",
915
918
  "fab.action": "Action",
916
919
  "fab.closeActions": "Close actions",
917
920
  "fab.openActions": "Open actions",
@@ -1228,5 +1231,12 @@
1228
1231
  "agentActivity.earlierConversations": "Earlier conversations ({{count}})",
1229
1232
  "agentActivity.earlierActivity": "Earlier activity",
1230
1233
  "tabs.scrollStart": "Scroll tabs back",
1231
- "tabs.scrollEnd": "Scroll tabs forward"
1234
+ "tabs.scrollEnd": "Scroll tabs forward",
1235
+ "agentChat.workedThrough": "Worked through {{count}} steps",
1236
+ "agentChat.newMessages": "New messages",
1237
+ "agentChat.copy": "Copy reply",
1238
+ "agentChat.copied": "Copied",
1239
+ "agentChat.today": "Today",
1240
+ "agentChat.yesterday": "Yesterday",
1241
+ "agentChat.emptyDescription": "Ask in your own words — the assistant can look things up and make changes for you."
1232
1242
  }
package/locales/sl.json CHANGED
@@ -57,6 +57,7 @@
57
57
  "status.draft": "Osnutek",
58
58
  "status.archived": "Arhivirano",
59
59
  "error.generic": "Nekaj je šlo narobe",
60
+ "error.traitMountFailed": "{{trait}} se ni zagnal",
60
61
  "error.retry": "Poskusite znova",
61
62
  "error.notFound": "Ni najdeno",
62
63
  "error.loadFailed": "Nalaganje ni uspelo: {{message}}",
@@ -182,6 +183,7 @@
182
183
  "template.ourTeam": "Naša ekipa",
183
184
  "template.caseStudies": "Primeri uporabe",
184
185
  "versionDiff.compare": "Primerjaj",
186
+ "versionDiff.changes": "Spremembe",
185
187
  "versionDiff.to": "z",
186
188
  "versionDiff.beforeRevision": "Prejšnja revizija",
187
189
  "versionDiff.afterRevision": "Naslednja revizija",
@@ -912,6 +914,7 @@
912
914
  "gridPicker.moreRefine": "+še {{count}} — zožite iskanje",
913
915
  "graphCanvas.noNodes": "Ni vozlišč za prikaz.",
914
916
  "codeBlock.noCode": "Ni kode za prikaz.",
917
+ "codeBlock.region": "Koda",
915
918
  "fab.action": "Dejanje",
916
919
  "fab.closeActions": "Zapri dejanja",
917
920
  "fab.openActions": "Odpri dejanja",
@@ -1228,5 +1231,12 @@
1228
1231
  "agentActivity.earlierConversations": "Prejšnji pogovori ({{count}})",
1229
1232
  "agentActivity.earlierActivity": "Prejšnja dejavnost",
1230
1233
  "tabs.scrollStart": "Pomakni zavihke nazaj",
1231
- "tabs.scrollEnd": "Pomakni zavihke naprej"
1234
+ "tabs.scrollEnd": "Pomakni zavihke naprej",
1235
+ "agentChat.workedThrough": "Opravil {{count}} korakov",
1236
+ "agentChat.newMessages": "Nova sporočila",
1237
+ "agentChat.copy": "Kopiraj odgovor",
1238
+ "agentChat.copied": "Kopirano",
1239
+ "agentChat.today": "Danes",
1240
+ "agentChat.yesterday": "Včeraj",
1241
+ "agentChat.emptyDescription": "Vprašajte s svojimi besedami — pomočnik lahko poišče podatke in namesto vas naredi spremembe."
1232
1242
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "6.53.1",
3
+ "version": "6.55.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -126,11 +126,11 @@
126
126
  "access": "public"
127
127
  },
128
128
  "dependencies": {
129
- "@almadar/core": "^10.115.0",
130
- "@almadar/evaluator": "^2.53.0",
131
- "@almadar/logger": "^1.15.0",
132
- "@almadar/runtime": "^6.101.0",
133
- "@almadar/std": "^16.239.0",
129
+ "@almadar/core": "^10.117.0",
130
+ "@almadar/evaluator": "^2.54.0",
131
+ "@almadar/logger": "^1.16.0",
132
+ "@almadar/runtime": "^6.103.0",
133
+ "@almadar/std": "^16.242.0",
134
134
  "@almadar/syntax": "^1.19.0",
135
135
  "@dnd-kit/core": "^6.3.1",
136
136
  "@dnd-kit/sortable": "^10.0.0",
@@ -191,7 +191,7 @@
191
191
  }
192
192
  },
193
193
  "devDependencies": {
194
- "@almadar-io/behaviors": "^0.16.186",
194
+ "@almadar-io/behaviors": "^0.16.189",
195
195
  "@almadar/eslint-plugin": "^2.21.0",
196
196
  "@react-three/drei": "^10.7.7",
197
197
  "@react-three/fiber": "^9.6.0",
@@ -217,6 +217,7 @@
217
217
  "babel-plugin-react-compiler": "19.0.0-beta-af1b7da-20250417",
218
218
  "eslint": "10.0.0",
219
219
  "eslint-plugin-react-compiler": "19.1.0-rc.2",
220
+ "fake-indexeddb": "6.2.5",
220
221
  "jsdom": "^24.0.0",
221
222
  "katex": "^0.16.0",
222
223
  "postcss": "^8.4.35",
@@ -26,8 +26,15 @@
26
26
  */
27
27
 
28
28
  import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
29
+ import { createRequire } from 'module';
29
30
  import { join, relative } from 'path';
30
31
 
32
+ const requireCjs = createRequire(import.meta.url);
33
+ const { compileTailwindClasses } = requireCjs('../tailwind-compile.cjs') as {
34
+ compileTailwindClasses: (classes: string[]) => Promise<string>;
35
+ };
36
+ const { safelist: loadedSafelist } = requireCjs('../tailwind-preset.cjs') as { safelist: Array<string | { pattern: RegExp }> };
37
+
31
38
  const ROOT = join(import.meta.dirname, '..');
32
39
  const PRESET_PATH = join(ROOT, 'tailwind-preset.cjs');
33
40
  const SCAN_DIRS = ['components', 'runtime', 'renderer', 'context', 'providers', 'hooks'];
@@ -38,7 +45,8 @@ const SCAN_DIRS = ['components', 'runtime', 'renderer', 'context', 'providers',
38
45
 
39
46
  // Matches arbitrary value classes: bg-[var(...)], min-h-[200px], w-[300px], etc.
40
47
  // Requires a hyphen before the bracket to avoid matching JS array access like arr[0]
41
- const ARBITRARY_RE = /(?:^|\s|["'`])([a-z][a-z0-9]*(?::[a-z][a-z0-9-]*)+-\[[^\]]+\]|[a-z][a-z0-9]*-[a-z0-9-]*-\[[^\]]+\]|[a-z][a-z0-9]*-\[[^\]]+\])/g;
48
+ // A bracket followed by `:` is a variant (`data-[active=true]:`), not a class.
49
+ const ARBITRARY_RE = /(?:^|\s|["'`])([a-z][a-z0-9]*(?::[a-z][a-z0-9-]*)+-\[[^\]]+\]|[a-z][a-z0-9]*-[a-z0-9-]*-\[[^\]]+\]|[a-z][a-z0-9]*-\[[^\]]+\])(?!:)/g;
42
50
 
43
51
  // Matches opacity modifiers on semantic colors: bg-primary/10, text-foreground/60
44
52
  const OPACITY_RE = /(?:^|\s|["'`])((?:bg|text|border|ring|from|to|via|hover:bg|hover:text|hover:border|dark:bg|dark:hover:bg|group-hover:bg|group-hover:text|peer-focus:ring|focus:ring)-(?:primary|secondary|muted|accent|foreground|card|surface|background|border|error|success|warning|info|ring|input|muted-foreground|card-foreground|primary-foreground|secondary-foreground|error-foreground|success-foreground|warning-foreground|info-foreground)\/\d+)/g;
@@ -184,6 +192,18 @@ const skipped = missing.filter(m => !needsSafelist(m.cls));
184
192
  missing.length = 0;
185
193
  missing.push(...filtered);
186
194
 
195
+ // A class Tailwind generates no CSS for paints nothing whether or not it is safelisted
196
+ // (`justify-[inherit]`: Tailwind 3 has no arbitrary justify-content) — fail on it.
197
+ const dead: Array<{ cls: string; files: string[] }> = [];
198
+ for (const cls of new Set([...loadedSafelist.filter((e): e is string => typeof e === 'string'), ...missing.map((m) => m.cls)])) {
199
+ if ((await compileTailwindClasses([cls])) === '') dead.push({ cls, files: Array.from(allCandidates.get(cls) ?? []) });
200
+ }
201
+ if (dead.length > 0) {
202
+ console.log('Classes that generate no CSS (fix the class at its source, or drop the safelist entry):');
203
+ for (const { cls, files } of dead) console.log(` ${cls}${files.length > 0 ? ` <- ${files.join(', ')}` : ' (safelist only)'}`);
204
+ process.exit(1);
205
+ }
206
+
187
207
  // Report
188
208
  console.log(`Current safelist: ${currentSafelist.size} entries`);
189
209
  console.log(`Already safelisted: ${alreadySafe.length}`);
@@ -47,6 +47,16 @@ const PAIRS = [
47
47
  ['--color-foreground', '--color-surface'],
48
48
  ];
49
49
 
50
+ // Drawing surfaces: labels are text (4.5:1); marks are graphics (WCAG 1.4.11, 3:1). Measured
51
+ // against the drawing ground, which falls back to the card when a theme sets none.
52
+ const DIAGRAM_TEXT = ['--color-diagram-label'];
53
+ const DIAGRAM_GRAPHICS = [
54
+ '--color-diagram-ink',
55
+ '--color-diagram-axis',
56
+ '--color-diagram-highlight',
57
+ ...Array.from({ length: 8 }, (_, i) => `--color-series-${i + 1}`),
58
+ ];
59
+
50
60
  let violations = 0;
51
61
  for (const mode of loadThemeModes(themesDir, filter)) {
52
62
  const results = [];
@@ -57,11 +67,20 @@ for (const mode of loadThemeModes(themesDir, filter)) {
57
67
  results.push({ pair: `${fgName} on ${bgName}`, ratio: ratio.toFixed(2), fg: mode.raw(fgName), bg: mode.raw(bgName) });
58
68
  }
59
69
  }
70
+ const groundName = mode.color('--surface-diagram') ? '--surface-diagram' : '--color-card';
71
+ for (const [names, min] of [[DIAGRAM_TEXT, 4.5], [DIAGRAM_GRAPHICS, 3]]) {
72
+ for (const fgName of names) {
73
+ const ratio = pairContrast(mode, mode.color(fgName), mode.color(groundName));
74
+ if (ratio !== null && ratio < min) {
75
+ results.push({ pair: `${fgName} on ${groundName} (needs ${min}:1)`, ratio: ratio.toFixed(2), fg: mode.raw(fgName), bg: mode.raw(groundName) });
76
+ }
77
+ }
78
+ }
60
79
  if (results.length) {
61
80
  violations += results.length;
62
81
  console.log(`\n${mode.name} (${mode.file})`);
63
82
  for (const r of results) console.log(` FAIL ${r.ratio}:1 ${r.pair} fg=${r.fg} bg=${r.bg}`);
64
83
  }
65
84
  }
66
- console.log(violations === 0 ? '\nAll theme pairs pass WCAG AA (4.5:1).' : `\n${violations} violation(s).`);
85
+ console.log(violations === 0 ? '\nAll theme pairs pass WCAG AA (text 4.5:1, drawing marks 3:1).' : `\n${violations} violation(s).`);
67
86
  process.exit(violations === 0 ? 0 : 1);
@@ -73,10 +73,8 @@ module.exports = {
73
73
  'hover:border-[var(--color-border-hover)]',
74
74
  'hover:border-l-[var(--color-muted)]',
75
75
  // Gradient partials with opacity
76
- 'from-[var(--color-primary)]/5',
77
- 'to-[var(--color-secondary)]/5',
78
76
  // Opacity variants on semantic colors (used in components)
79
- 'bg-primary/10', 'bg-primary/8', 'bg-accent/10',
77
+ 'bg-primary/10', 'bg-accent/10',
80
78
  'bg-muted/20', 'bg-muted/30', 'bg-muted/50', 'bg-muted/60',
81
79
  'bg-surface/60', 'bg-surface/80',
82
80
  'bg-background/10', 'bg-background/80',
@@ -150,7 +148,6 @@ module.exports = {
150
148
  'bg-muted/60',
151
149
  'bg-primary/10',
152
150
  'bg-primary/20',
153
- 'bg-primary/8',
154
151
  'bg-success/10',
155
152
  'bg-success/20',
156
153
  'bg-surface/60',
@@ -177,7 +174,6 @@ module.exports = {
177
174
  'border-x-[length:var(--border-width)]',
178
175
  'dark:bg-foreground/70',
179
176
  'dark:hover:bg-error/20',
180
- 'data-[active=true]',
181
177
  'duration-[var(--transition-fast)]',
182
178
  'duration-[var(--transition-normal)]',
183
179
  'focus:ring-[length:var(--focus-ring-width)]',
@@ -332,7 +328,6 @@ module.exports = {
332
328
  'border-warning/60',
333
329
  'border-y-[length:var(--border-width)]',
334
330
  'bottom-[15%]',
335
- 'data-[empty=true]',
336
331
  'focus-visible:ring-[length:var(--focus-ring-width)]',
337
332
  'focus-visible:ring-offset-[length:var(--focus-ring-offset)]',
338
333
  'focus-within:border-[var(--color-primary)]',
@@ -460,6 +455,10 @@ module.exports = {
460
455
  'shadow-[var(--shadow-lg)]',
461
456
  'text-[var(--color-primary-foreground)]',
462
457
  'z-[850]',
458
+
459
+ // Auto-added by audit-tailwind-safelist.ts (2026-10-03)
460
+ '[justify-content:inherit]',
461
+ 'bg-primary/[0.08]',
463
462
  ],
464
463
  theme: {
465
464
  fontFamily: {