@veevarts/design-system 2.0.0-alpha.57 → 2.0.0-alpha.59

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.
@@ -0,0 +1,6 @@
1
+ import { JSX } from 'react';
2
+ import { TimeFieldProps } from './types';
3
+ export declare function TimeField({ value: valueProp, defaultValue, onChange, label, 'aria-label': ariaLabel, placeholder, emptyContent, invalidMessage, minTime, minTimeMessage, listStart, listEnd, listStep, locale, isRequired, isDisabled, isInvalid, errorMessage, isClearable, clearLabel, onBlur, size, variant, labelPlacement, className, inputProps, popoverProps, 'data-testid': dataTestId, }: TimeFieldProps): JSX.Element;
4
+ export declare namespace TimeField {
5
+ var displayName: string;
6
+ }
@@ -0,0 +1,3 @@
1
+ export { TimeField } from './TimeField';
2
+ export { parseTimeInput } from './time';
3
+ export type { TimeFieldProps } from './types';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The time logic behind {@link TimeField}, kept free of React so it can be
3
+ * tested and reused on its own. Every time in and out is 24-hour `"HH:mm"`.
4
+ */
5
+ /** Minutes since midnight of an `"HH:mm"` time. */
6
+ export declare function toMinutes(time: string): number;
7
+ /**
8
+ * Read typed text as a time: `"HH:mm"`, or `null` when it is not one.
9
+ *
10
+ * Forgiving on purpose: `"7:15 pm"`, `"7:15pm"`, `"715p"`, `"7p"`, `"19:15"`,
11
+ * `"1915"` and `"7"` all work; case, spaces and dots do not matter
12
+ * (`"7:15 P.M."`). Without am/pm, hours 1–6 read as afternoon (no venue opens
13
+ * at 3 AM) and every other hour as 24-hour: `"3"` is 15:00, `"7"` is 07:00,
14
+ * `"19"` is 19:00, `"0"` is midnight.
15
+ *
16
+ * @example
17
+ * parseTimeInput('715p'); // '19:15'
18
+ * parseTimeInput('3'); // '15:00'
19
+ * parseTimeInput('25'); // null
20
+ */
21
+ export declare function parseTimeInput(input: string): string | null;
22
+ /**
23
+ * The suggested times: `start` to `end` inclusive, every `step` whole
24
+ * minutes. Empty when `step` is under a minute or `end` is before `start`.
25
+ */
26
+ export declare function buildTimeOptions(start: string, end: string, step: number): string[];
27
+ /**
28
+ * Whether the list keeps `time` while `query` is typed. It compares against
29
+ * the 12-hour form (`"7:30"`) and the 24-hour forms (`"19:30"`, `"07:30"`), so
30
+ * it does not depend on how the label is formatted: `"7"` keeps 7:00 and
31
+ * 7:30 AM and PM, `"7p"` only the PM ones, `"19"` the 7 PM ones, `"p"` every
32
+ * PM time.
33
+ */
34
+ export declare function matchesTimeQuery(time: string, query: string): boolean;
35
+ /**
36
+ * The label for a time in `locale`: `"7:15 PM"` in `en-US`, `"19:15"` in
37
+ * `en-GB`, `"7:15 p. m."` in `es-CO`. No-break spaces become plain ones, so
38
+ * the label matches what people type and what tests search for.
39
+ */
40
+ export declare function formatTimeLabel(time: string, locale: string): string;
@@ -0,0 +1,77 @@
1
+ import { FocusEvent, ReactNode } from 'react';
2
+ import { AutocompleteProps } from '../../components';
3
+ /** One row of the list: the `"HH:mm"` key and its locale label. */
4
+ export interface TimeFieldOption {
5
+ key: string;
6
+ label: string;
7
+ }
8
+ export interface TimeFieldProps {
9
+ /**
10
+ * The time as 24-hour `"HH:mm"` text, or `null` for none. Omit for
11
+ * uncontrolled mode.
12
+ */
13
+ value?: string | null;
14
+ /** Starting time in uncontrolled mode. */
15
+ defaultValue?: string | null;
16
+ /** Called with the committed `"HH:mm"`, or `null` when a clearable field is emptied. */
17
+ onChange?: (time: string | null) => void;
18
+ /** Visible label. Without one, pass `aria-label`. */
19
+ label?: ReactNode;
20
+ 'aria-label'?: string;
21
+ /** "e.g. 7:15 PM". */
22
+ placeholder?: string;
23
+ /**
24
+ * Shown in the list when nothing matches the typed text, or the text was
25
+ * erased ("Type a time like 7:15 PM"). Required and non-empty: HeroUI's
26
+ * own is English, and an Autocomplete that hides an empty list loses its
27
+ * options to `aria-hidden` when they come back.
28
+ */
29
+ emptyContent: ReactNode;
30
+ /** Shown when the typed text is not a time ("Enter a time like 7:15 PM."). */
31
+ invalidMessage: ReactNode;
32
+ /**
33
+ * Earliest allowed `"HH:mm"`. Earlier times leave the list, and typing
34
+ * one, or receiving one as `value`, shows `minTimeMessage`.
35
+ */
36
+ minTime?: string;
37
+ /** Shown for a time before `minTime` ("Ends after it starts."). Defaults to `invalidMessage`. */
38
+ minTimeMessage?: ReactNode;
39
+ /** First time the list suggests. @default '07:00' */
40
+ listStart?: string;
41
+ /** Last time the list suggests, inclusive. @default '21:00' */
42
+ listEnd?: string;
43
+ /** Minutes between suggested times. @default 30 */
44
+ listStep?: number;
45
+ /**
46
+ * BCP 47 locale the times are written in: "7:15 PM" in `en-US`, "19:15"
47
+ * in `en-GB`. Pass the same one as `HeroUIProvider`. @default 'en-US'
48
+ */
49
+ locale?: string;
50
+ isRequired?: boolean;
51
+ isDisabled?: boolean;
52
+ /** Marks the field invalid from outside; `errorMessage` is shown with it. */
53
+ isInvalid?: boolean;
54
+ errorMessage?: ReactNode;
55
+ /**
56
+ * Whether the field may be emptied: a clear button shows, and leaving it
57
+ * empty commits `null`. Otherwise leaving it empty restores the time it
58
+ * had. @default false
59
+ */
60
+ isClearable?: boolean;
61
+ /** Accessible name of the clear button ("Clear time"); HeroUI gives it none. */
62
+ clearLabel?: string;
63
+ /** Called after the field has committed what was typed (react-hook-form's `field.onBlur`). */
64
+ onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
65
+ size?: AutocompleteProps['size'];
66
+ /** @default 'bordered' */
67
+ variant?: AutocompleteProps['variant'];
68
+ /** @default 'outside-top' */
69
+ labelPlacement?: AutocompleteProps['labelPlacement'];
70
+ className?: string;
71
+ /** Passed to HeroUI's input: `{ classNames: { inputWrapper: 'h-11' } }`. */
72
+ inputProps?: AutocompleteProps['inputProps'];
73
+ /** Passed to HeroUI's popover. */
74
+ popoverProps?: AutocompleteProps['popoverProps'];
75
+ /** Lands on the `<input>`. Options get `${data-testid}-option-${HH:mm}`. */
76
+ 'data-testid'?: string;
77
+ }
@@ -71,6 +71,8 @@ export { UnderlineTabs } from './UnderlineTabs';
71
71
  export type { UnderlineTabsProps, UnderlineTabItem, UnderlineTabsSize, UnderlineTabsLevel } from './UnderlineTabs';
72
72
  export { AdminFieldLabel, AdminTextField, adminFieldErrorClass, adminFieldHelpButtonClass, adminFieldHelpContentClass, adminFieldHelpIconClass, adminFieldHintClass, adminFieldLabelClass, adminFieldLabelNoAutoMarkClass, adminFieldLabelRowClass, adminFieldLabelSuffixClass, adminFieldRequiredClass, adminInputClass, adminInputReadOnlyClass, adminInputWrapperClass, } from './AdminTextField';
73
73
  export type { AdminTextFieldProps, AdminFieldLabelProps, AdminFieldRequiredProps, AdminFieldHelpProps, } from './AdminTextField';
74
+ export { TimeField, parseTimeInput } from './TimeField';
75
+ export type { TimeFieldProps } from './TimeField';
74
76
  export { RowActions } from './RowActions';
75
77
  export type { RowActionsProps, RowAction } from './RowActions';
76
78
  export { PickerRow } from './PickerRow';
@@ -23,6 +23,9 @@ __export(index_exports, {
23
23
  adminSurfaceCss: () => adminSurfaceCss,
24
24
  adminSurfaceTailwindColors: () => adminSurfaceTailwindColors,
25
25
  adminSurfaceTokens: () => adminSurfaceTokens,
26
+ assistantSurfacePlugin: () => assistantSurfacePlugin,
27
+ assistantSurfaceTailwindColors: () => assistantSurfaceTailwindColors,
28
+ assistantSurfaceTokens: () => assistantSurfaceTokens,
26
29
  colors: () => colors,
27
30
  createHeroUIConfig: () => createHeroUIConfig,
28
31
  deepMerge: () => deepMerge,
@@ -258,12 +261,12 @@ function adminSurfaceCss({
258
261
  scope = ".acct",
259
262
  fontFamily = "'NeueMontreal', system-ui, sans-serif"
260
263
  } = {}) {
261
- const declarations = Object.entries(adminSurfaceTokens).map(([name, value]) => ` ${name}: ${value};`).join("\n");
264
+ const declarations2 = Object.entries(adminSurfaceTokens).map(([name, value]) => ` ${name}: ${value};`).join("\n");
262
265
  const typography2 = fontFamily ? `
263
266
  font-family: ${fontFamily};
264
267
  color: var(--ink);` : "";
265
268
  return `${scope} {
266
- ${declarations}${typography2}
269
+ ${declarations2}${typography2}
267
270
  }
268
271
  `;
269
272
  }
@@ -308,6 +311,70 @@ var posSurfaceTokens = {
308
311
  };
309
312
  var posSurfaceTailwindColors = { ...posSurfaceTokens };
310
313
 
314
+ // src/tokens/assistant-surface.ts
315
+ var info = colors.palettes.info;
316
+ var HEROUI_CONTENT = {
317
+ light: { content1: "#ffffff", content2: "#f4f4f5" },
318
+ dark: { content1: "#18181b", content2: "#27272a" }
319
+ };
320
+ var ASSISTANT_SURFACE_TOKEN_NAMES = [
321
+ /** Text and icon tone that marks something as the assistant's. */
322
+ "accent",
323
+ /** Tint behind small assistant marks (an avatar disc, an info notice). */
324
+ "fill",
325
+ /** Hairline around assistant regions. */
326
+ "border",
327
+ /** Fill of the person's own turn. */
328
+ "user-bubble",
329
+ /** Fill of the assistant's turn, where an app draws it as a bubble. */
330
+ "assistant-bubble",
331
+ /** Focus ring inside assistant surfaces. */
332
+ "focus"
333
+ ];
334
+ var assistantSurfaceTokens = {
335
+ light: {
336
+ accent: info[600],
337
+ fill: info[100],
338
+ border: info[200],
339
+ "user-bubble": HEROUI_CONTENT.light.content2,
340
+ "assistant-bubble": HEROUI_CONTENT.light.content1,
341
+ focus: info[500]
342
+ },
343
+ dark: {
344
+ accent: info[300],
345
+ fill: info[900],
346
+ border: info[800],
347
+ "user-bubble": HEROUI_CONTENT.dark.content2,
348
+ "assistant-bubble": HEROUI_CONTENT.dark.content1,
349
+ focus: info[400]
350
+ }
351
+ };
352
+ function hexToChannels(hex) {
353
+ if (!/^#[0-9a-f]{6}$/i.test(hex)) throw new Error(`bad hex: ${hex}`);
354
+ return [1, 3, 5].map((start) => parseInt(hex.slice(start, start + 2), 16)).join(" ");
355
+ }
356
+ var assistantSurfaceVar = (name) => `--ai-${name}`;
357
+ var declarations = (theme) => Object.fromEntries(
358
+ ASSISTANT_SURFACE_TOKEN_NAMES.map((name) => [
359
+ assistantSurfaceVar(name),
360
+ hexToChannels(assistantSurfaceTokens[theme][name])
361
+ ])
362
+ );
363
+ var ASSISTANT_SURFACE_SELECTORS = {
364
+ light: ':root, .light, [data-theme="light"]',
365
+ dark: ".dark"
366
+ };
367
+ var assistantSurfaceBase = {
368
+ [ASSISTANT_SURFACE_SELECTORS.light]: declarations("light"),
369
+ [ASSISTANT_SURFACE_SELECTORS.dark]: declarations("dark")
370
+ };
371
+ var assistantSurfaceTailwindColors = Object.fromEntries(
372
+ ASSISTANT_SURFACE_TOKEN_NAMES.map((name) => [
373
+ name,
374
+ `rgb(var(${assistantSurfaceVar(name)}, ${hexToChannels(assistantSurfaceTokens.light[name])}) / <alpha-value>)`
375
+ ])
376
+ );
377
+
311
378
  // src/tailwind/index.ts
312
379
  var tailwindTheme = {
313
380
  colors: {
@@ -323,7 +390,11 @@ var tailwindTheme = {
323
390
  // `danger`/`info`/`warn` do not shadow the semantic palettes, and fed
324
391
  // from the token object so utility and token cannot disagree.
325
392
  // See src/tokens/pos-surface.ts.
326
- pos: posSurfaceTailwindColors
393
+ pos: posSurfaceTailwindColors,
394
+ // Assistant palette. Unlike `acct`/`pos` these resolve to CSS custom
395
+ // properties, because they have a dark value: the preset's plugin
396
+ // below writes the properties. See src/tokens/assistant-surface.ts.
397
+ ai: assistantSurfaceTailwindColors
327
398
  },
328
399
  fontFamily: typography.fontFamily,
329
400
  spacing,
@@ -402,11 +473,15 @@ var veevartContent = [
402
473
  "./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
403
474
  "./node_modules/@veevarts/design-system/dist/**/*.{js,mjs,jsx,tsx}"
404
475
  ];
476
+ function assistantSurfacePlugin({ addBase }) {
477
+ addBase(assistantSurfaceBase);
478
+ }
405
479
  var veevartPreset = {
406
480
  theme: {
407
481
  extend: tailwindTheme
408
482
  },
409
- darkMode: "class"
483
+ darkMode: "class",
484
+ plugins: [assistantSurfacePlugin]
410
485
  };
411
486
  var tokens = {
412
487
  colors,
@@ -419,6 +494,9 @@ var tokens = {
419
494
  adminSurfaceCss,
420
495
  adminSurfaceTailwindColors,
421
496
  adminSurfaceTokens,
497
+ assistantSurfacePlugin,
498
+ assistantSurfaceTailwindColors,
499
+ assistantSurfaceTokens,
422
500
  colors,
423
501
  createHeroUIConfig,
424
502
  deepMerge,
@@ -3,6 +3,7 @@ import { typography } from '../tokens/typography';
3
3
  import { spacing } from '../tokens/spacing';
4
4
  export { colors, typography, spacing };
5
5
  export { adminSurfaceCss, adminSurfaceTokens, adminSurfaceTailwindColors } from '../tokens/admin-surface';
6
+ export { assistantSurfaceTokens, assistantSurfaceTailwindColors } from '../tokens/assistant-surface';
6
7
  export interface ColorPalette {
7
8
  50: string;
8
9
  100: string;
@@ -48,6 +49,7 @@ export declare const tailwindTheme: {
48
49
  colors: {
49
50
  acct: Record<string, string>;
50
51
  pos: Record<string, string>;
52
+ ai: Record<"accent" | "fill" | "border" | "user-bubble" | "assistant-bubble" | "focus", string>;
51
53
  secondary: "#000000";
52
54
  success: "#057A54";
53
55
  warning: "#F5A524";
@@ -251,11 +253,28 @@ export declare function createHeroUIConfig(overrides?: HeroUIConfigOverrides): H
251
253
  * Includes HeroUI theme and Design System components
252
254
  */
253
255
  export declare const veevartContent: string[];
256
+ /** The slice of Tailwind's plugin API the preset's plugins use. */
257
+ export interface PresetPluginApi {
258
+ addBase: (base: Record<string, Record<string, string>>) => void;
259
+ }
260
+ /**
261
+ * Writes the `--ai-*` custom properties: light values on `:root`, dark values
262
+ * under `.dark`. The `ai-*` colours read them, so without this plugin they
263
+ * stay on their light fallbacks.
264
+ *
265
+ * A plain function, not `tailwindcss/plugin`, so this bundle does not pull
266
+ * Tailwind in. Add it to `plugins` yourself only if you extend
267
+ * `tokens.tailwindTheme` instead of using `veevartPreset`.
268
+ */
269
+ export declare function assistantSurfacePlugin({ addBase }: PresetPluginApi): void;
254
270
  /**
255
271
  * Veevart Design System Tailwind preset
256
272
  *
257
273
  * Use this preset in your tailwind.config.js to get all Veevart tokens
258
274
  * and styling defaults. Use veevartContent in your content array.
275
+ *
276
+ * Tailwind runs a preset's plugins alongside the app's own (HeroUI's, for
277
+ * instance), so the app keeps declaring `plugins` as before.
259
278
  */
260
279
  export declare const veevartPreset: {
261
280
  theme: {
@@ -263,6 +282,7 @@ export declare const veevartPreset: {
263
282
  colors: {
264
283
  acct: Record<string, string>;
265
284
  pos: Record<string, string>;
285
+ ai: Record<"accent" | "fill" | "border" | "user-bubble" | "assistant-bubble" | "focus", string>;
266
286
  secondary: "#000000";
267
287
  success: "#057A54";
268
288
  warning: "#F5A524";
@@ -351,6 +371,7 @@ export declare const veevartPreset: {
351
371
  };
352
372
  };
353
373
  darkMode: "class";
374
+ plugins: (typeof assistantSurfacePlugin)[];
354
375
  };
355
376
  /**
356
377
  * All tokens bundled together for convenience
@@ -508,6 +529,7 @@ export declare const tokens: {
508
529
  colors: {
509
530
  acct: Record<string, string>;
510
531
  pos: Record<string, string>;
532
+ ai: Record<"accent" | "fill" | "border" | "user-bubble" | "assistant-bubble" | "focus", string>;
511
533
  secondary: "#000000";
512
534
  success: "#057A54";
513
535
  warning: "#F5A524";
@@ -219,12 +219,12 @@ function adminSurfaceCss({
219
219
  scope = ".acct",
220
220
  fontFamily = "'NeueMontreal', system-ui, sans-serif"
221
221
  } = {}) {
222
- const declarations = Object.entries(adminSurfaceTokens).map(([name, value]) => ` ${name}: ${value};`).join("\n");
222
+ const declarations2 = Object.entries(adminSurfaceTokens).map(([name, value]) => ` ${name}: ${value};`).join("\n");
223
223
  const typography2 = fontFamily ? `
224
224
  font-family: ${fontFamily};
225
225
  color: var(--ink);` : "";
226
226
  return `${scope} {
227
- ${declarations}${typography2}
227
+ ${declarations2}${typography2}
228
228
  }
229
229
  `;
230
230
  }
@@ -269,6 +269,70 @@ var posSurfaceTokens = {
269
269
  };
270
270
  var posSurfaceTailwindColors = { ...posSurfaceTokens };
271
271
 
272
+ // src/tokens/assistant-surface.ts
273
+ var info = colors.palettes.info;
274
+ var HEROUI_CONTENT = {
275
+ light: { content1: "#ffffff", content2: "#f4f4f5" },
276
+ dark: { content1: "#18181b", content2: "#27272a" }
277
+ };
278
+ var ASSISTANT_SURFACE_TOKEN_NAMES = [
279
+ /** Text and icon tone that marks something as the assistant's. */
280
+ "accent",
281
+ /** Tint behind small assistant marks (an avatar disc, an info notice). */
282
+ "fill",
283
+ /** Hairline around assistant regions. */
284
+ "border",
285
+ /** Fill of the person's own turn. */
286
+ "user-bubble",
287
+ /** Fill of the assistant's turn, where an app draws it as a bubble. */
288
+ "assistant-bubble",
289
+ /** Focus ring inside assistant surfaces. */
290
+ "focus"
291
+ ];
292
+ var assistantSurfaceTokens = {
293
+ light: {
294
+ accent: info[600],
295
+ fill: info[100],
296
+ border: info[200],
297
+ "user-bubble": HEROUI_CONTENT.light.content2,
298
+ "assistant-bubble": HEROUI_CONTENT.light.content1,
299
+ focus: info[500]
300
+ },
301
+ dark: {
302
+ accent: info[300],
303
+ fill: info[900],
304
+ border: info[800],
305
+ "user-bubble": HEROUI_CONTENT.dark.content2,
306
+ "assistant-bubble": HEROUI_CONTENT.dark.content1,
307
+ focus: info[400]
308
+ }
309
+ };
310
+ function hexToChannels(hex) {
311
+ if (!/^#[0-9a-f]{6}$/i.test(hex)) throw new Error(`bad hex: ${hex}`);
312
+ return [1, 3, 5].map((start) => parseInt(hex.slice(start, start + 2), 16)).join(" ");
313
+ }
314
+ var assistantSurfaceVar = (name) => `--ai-${name}`;
315
+ var declarations = (theme) => Object.fromEntries(
316
+ ASSISTANT_SURFACE_TOKEN_NAMES.map((name) => [
317
+ assistantSurfaceVar(name),
318
+ hexToChannels(assistantSurfaceTokens[theme][name])
319
+ ])
320
+ );
321
+ var ASSISTANT_SURFACE_SELECTORS = {
322
+ light: ':root, .light, [data-theme="light"]',
323
+ dark: ".dark"
324
+ };
325
+ var assistantSurfaceBase = {
326
+ [ASSISTANT_SURFACE_SELECTORS.light]: declarations("light"),
327
+ [ASSISTANT_SURFACE_SELECTORS.dark]: declarations("dark")
328
+ };
329
+ var assistantSurfaceTailwindColors = Object.fromEntries(
330
+ ASSISTANT_SURFACE_TOKEN_NAMES.map((name) => [
331
+ name,
332
+ `rgb(var(${assistantSurfaceVar(name)}, ${hexToChannels(assistantSurfaceTokens.light[name])}) / <alpha-value>)`
333
+ ])
334
+ );
335
+
272
336
  // src/tailwind/index.ts
273
337
  var tailwindTheme = {
274
338
  colors: {
@@ -284,7 +348,11 @@ var tailwindTheme = {
284
348
  // `danger`/`info`/`warn` do not shadow the semantic palettes, and fed
285
349
  // from the token object so utility and token cannot disagree.
286
350
  // See src/tokens/pos-surface.ts.
287
- pos: posSurfaceTailwindColors
351
+ pos: posSurfaceTailwindColors,
352
+ // Assistant palette. Unlike `acct`/`pos` these resolve to CSS custom
353
+ // properties, because they have a dark value: the preset's plugin
354
+ // below writes the properties. See src/tokens/assistant-surface.ts.
355
+ ai: assistantSurfaceTailwindColors
288
356
  },
289
357
  fontFamily: typography.fontFamily,
290
358
  spacing,
@@ -363,11 +431,15 @@ var veevartContent = [
363
431
  "./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
364
432
  "./node_modules/@veevarts/design-system/dist/**/*.{js,mjs,jsx,tsx}"
365
433
  ];
434
+ function assistantSurfacePlugin({ addBase }) {
435
+ addBase(assistantSurfaceBase);
436
+ }
366
437
  var veevartPreset = {
367
438
  theme: {
368
439
  extend: tailwindTheme
369
440
  },
370
- darkMode: "class"
441
+ darkMode: "class",
442
+ plugins: [assistantSurfacePlugin]
371
443
  };
372
444
  var tokens = {
373
445
  colors,
@@ -379,6 +451,9 @@ export {
379
451
  adminSurfaceCss,
380
452
  adminSurfaceTailwindColors,
381
453
  adminSurfaceTokens,
454
+ assistantSurfacePlugin,
455
+ assistantSurfaceTailwindColors,
456
+ assistantSurfaceTokens,
382
457
  colors,
383
458
  createHeroUIConfig,
384
459
  deepMerge,
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The assistant ("AI") palette: the one place an assistant surface takes its
3
+ * accent, tints and focus ring from, in light AND dark (INI-0581).
4
+ *
5
+ * PLACEHOLDER HUE. Design has not picked the assistant colour yet. These
6
+ * values are the `info` blue scale, the only hue in the DS that no product
7
+ * already uses for something else, and they ship so the assistant pieces can
8
+ * be built and measured now. When Design signs a hue off, change the two maps
9
+ * below and nothing else: every `ai-*` utility, the CSS custom properties and
10
+ * the contrast tests all read from here. The two bubbles are neutrals on
11
+ * purpose (only the assistant carries the hue); Design may retune them too.
12
+ *
13
+ * WHY IT CARRIES ITS OWN DARK VALUES. The DS has no dark theme:
14
+ * `createHeroUIConfig` builds `themes.light` only. HeroUI's semantic colours
15
+ * (`content1`, `foreground`, `default-*`, `divider`) still flip under `.dark`,
16
+ * so the assistant pieces style with those plus this palette, and this
17
+ * palette flips with them. A full DS dark theme is a separate, larger piece
18
+ * of work.
19
+ *
20
+ * HOW IT REACHES THE PAGE. The preset adds a plugin that writes each token as
21
+ * a custom property and maps `ai-*` colours to those properties. **The class
22
+ * is the switch:** dark values under `.dark`, light values on `:root`,
23
+ * `.light` and `[data-theme="light"]`. Those are the selectors every HeroUI
24
+ * 2.x in the DS peer range writes its own variables under, so the palette and
25
+ * HeroUI's colours flip together; `src/tailwind/__test__/assistant-heroui-parity.test.ts`
26
+ * holds that against the installed HeroUI. `[data-theme="dark"]` is NOT a
27
+ * switch: HeroUI 2.8 no longer writes dark variables there (2.7 still did),
28
+ * so honouring it would turn `ai-*` dark on white HeroUI cards. An app that
29
+ * puts `.dark` on a subtree gets dark values on that subtree only.
30
+ *
31
+ * Kept apart from `adminSurfaceTokens` on purpose: consumers test that they
32
+ * override every admin token, and a new key there would break them.
33
+ */
34
+ /** The token names, in the order the docs list them. */
35
+ export declare const ASSISTANT_SURFACE_TOKEN_NAMES: readonly ["accent", "fill", "border", "user-bubble", "assistant-bubble", "focus"];
36
+ export type AssistantSurfaceTokenName = (typeof ASSISTANT_SURFACE_TOKEN_NAMES)[number];
37
+ export type AssistantSurfaceTheme = 'light' | 'dark';
38
+ /** Token name → hex, per theme. Exported so tests and tooling can assert on it directly. */
39
+ export declare const assistantSurfaceTokens: Record<AssistantSurfaceTheme, Record<AssistantSurfaceTokenName, string>>;
40
+ /**
41
+ * `#1d4ed8` → `29 78 216`: the channel form Tailwind's `<alpha-value>` needs.
42
+ * Throws on anything but `#rrggbb`, so a typo in a hue change fails the tests
43
+ * instead of shipping a colour that resolves to nothing.
44
+ */
45
+ export declare function hexToChannels(hex: string): string;
46
+ /** The custom property behind a token: `accent` → `--ai-accent`. */
47
+ export declare const assistantSurfaceVar: (name: AssistantSurfaceTokenName) => string;
48
+ /**
49
+ * Where each theme's values are written: the selectors every HeroUI 2.x in
50
+ * the peer range writes its own light and dark variables under (see the module
51
+ * comment for why `[data-theme="dark"]` is left out). A `.light` island inside
52
+ * a `.dark` page flips back for both palettes at once.
53
+ */
54
+ export declare const ASSISTANT_SURFACE_SELECTORS: Record<AssistantSurfaceTheme, string>;
55
+ /** The custom properties as a CSS-in-JS rule set, ready for Tailwind's `addBase`. */
56
+ export declare const assistantSurfaceBase: Record<string, Record<string, string>>;
57
+ /**
58
+ * The palette as Tailwind colours, namespaced under `ai`: `text-ai-accent`,
59
+ * `bg-ai-user-bubble`, `ring-ai-focus`, with opacity modifiers working.
60
+ *
61
+ * Each colour falls back to its LIGHT value when the property is missing, so
62
+ * an app that extends `tokens.tailwindTheme` without the preset's plugin still
63
+ * gets a readable light palette instead of colours that resolve to nothing.
64
+ * Dark values need the plugin.
65
+ */
66
+ export declare const assistantSurfaceTailwindColors: Record<AssistantSurfaceTokenName, string>;
@@ -3,5 +3,7 @@ export { spacing } from './spacing';
3
3
  export { typography } from './typography';
4
4
  export { adminSurfaceCss, adminSurfaceTokens } from './admin-surface';
5
5
  export { posSurfaceTokens, posSurfaceTailwindColors } from './pos-surface';
6
+ export { assistantSurfaceTokens } from './assistant-surface';
6
7
  export type { ColorToken } from './colors';
7
8
  export type { AdminSurfaceCssOptions } from './admin-surface';
9
+ export type { AssistantSurfaceTheme, AssistantSurfaceTokenName } from './assistant-surface';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@veevarts/design-system",
3
3
  "private": false,
4
- "version": "2.0.0-alpha.57",
4
+ "version": "2.0.0-alpha.59",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",