@calliopelabs/ui 0.13.0 → 0.14.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 (73) hide show
  1. package/README.md +50 -3
  2. package/dist/agent/AgentChat.d.ts +56 -0
  3. package/dist/agent/AgentChat.js +76 -0
  4. package/dist/agent/AgentLauncher.d.ts +25 -0
  5. package/dist/agent/AgentLauncher.js +25 -0
  6. package/dist/agent/AgentProvider.d.ts +82 -0
  7. package/dist/agent/AgentProvider.js +176 -0
  8. package/dist/agent/CalliopeAgent.d.ts +43 -0
  9. package/dist/agent/CalliopeAgent.js +111 -0
  10. package/dist/agent/controller.d.ts +148 -0
  11. package/dist/agent/controller.js +463 -0
  12. package/dist/agent/copilotkit/CopilotKitAgentProvider.d.ts +41 -0
  13. package/dist/agent/copilotkit/CopilotKitAgentProvider.js +145 -0
  14. package/dist/agent/copilotkit/index.d.ts +6 -0
  15. package/dist/agent/copilotkit/index.js +6 -0
  16. package/dist/agent/engine.d.ts +41 -0
  17. package/dist/agent/engine.js +93 -0
  18. package/dist/agent/index.d.ts +15 -0
  19. package/dist/agent/index.js +15 -0
  20. package/dist/agent/mock.d.ts +34 -0
  21. package/dist/agent/mock.js +112 -0
  22. package/dist/agent/surface.d.ts +30 -0
  23. package/dist/agent/surface.js +90 -0
  24. package/dist/agent/tool.d.ts +98 -0
  25. package/dist/agent/tool.js +81 -0
  26. package/dist/appearance/brands.d.ts +14 -12
  27. package/dist/appearance/brands.js +18 -51
  28. package/dist/chat/AgentProgress.d.ts +19 -0
  29. package/dist/chat/AgentProgress.js +34 -0
  30. package/dist/chat/Composer.d.ts +31 -0
  31. package/dist/chat/Composer.js +44 -0
  32. package/dist/chat/MessageBubble.d.ts +23 -0
  33. package/dist/chat/MessageBubble.js +26 -0
  34. package/dist/chat/MessageList.d.ts +36 -0
  35. package/dist/chat/MessageList.js +68 -0
  36. package/dist/chat/ModelPicker.d.ts +17 -0
  37. package/dist/chat/ModelPicker.js +13 -0
  38. package/dist/chat/ToolCallCard.d.ts +27 -0
  39. package/dist/chat/ToolCallCard.js +52 -0
  40. package/dist/chat/model.d.ts +123 -0
  41. package/dist/chat/model.js +337 -0
  42. package/dist/chat/patch.d.ts +12 -0
  43. package/dist/chat/patch.js +149 -0
  44. package/dist/chat/useAgUiRun.d.ts +52 -0
  45. package/dist/chat/useAgUiRun.js +137 -0
  46. package/dist/index.d.ts +9 -0
  47. package/dist/index.js +10 -0
  48. package/dist/primitives/Button.d.ts +1 -1
  49. package/dist/provider/messages.d.ts +66 -0
  50. package/dist/provider/messages.js +71 -0
  51. package/dist/theme/mappings/palette.d.ts +52 -0
  52. package/dist/theme/mappings/palette.js +208 -0
  53. package/dist/theme/mappings/render.d.ts +9 -0
  54. package/dist/theme/mappings/render.js +520 -0
  55. package/dist/theme/mappings/targets.d.ts +43 -0
  56. package/dist/theme/mappings/targets.js +222 -0
  57. package/dist/theme/tokens.d.ts +10 -9
  58. package/dist/theme/tokens.generated.d.ts +353 -0
  59. package/dist/theme/tokens.generated.js +357 -0
  60. package/dist/theme/tokens.js +20 -264
  61. package/dist/tokens/README.jupyterlab.md +125 -0
  62. package/dist/tokens/README.md +15 -0
  63. package/dist/tokens/README.native.md +29 -0
  64. package/dist/tokens/README.plain.md +84 -0
  65. package/dist/tokens/README.python.md +33 -0
  66. package/dist/tokens/README.vscode.md +114 -0
  67. package/dist/tokens/jupyterlab.css +1710 -0
  68. package/dist/tokens/plain.css +930 -0
  69. package/dist/tokens/tokens.native.d.ts +85 -0
  70. package/dist/tokens/tokens.native.js +773 -0
  71. package/dist/tokens/tokens.py +1043 -0
  72. package/dist/tokens/vscode.css +1138 -0
  73. package/package.json +35 -2
@@ -0,0 +1,137 @@
1
+ "use client";
2
+ import { EventType } from "@ag-ui/core";
3
+ import * as React from "react";
4
+ import { createChatView, foldChat, reduceChat } from "./model.js";
5
+ /** The person's own message as the events that fold it into the view. */
6
+ export function userMessageEvents(id, text) {
7
+ return [
8
+ { type: EventType.TEXT_MESSAGE_START, messageId: id, role: "user" },
9
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: id, delta: text },
10
+ { type: EventType.TEXT_MESSAGE_END, messageId: id },
11
+ ];
12
+ }
13
+ const message = (error) => error instanceof Error && error.message
14
+ ? error.message
15
+ : typeof error === "string" && error
16
+ ? error
17
+ : "The event stream failed.";
18
+ /**
19
+ * Holds a `ChatView` and folds a host-provided AG-UI event source into it,
20
+ * one run at a time. No network code: the host connects, authenticates and
21
+ * sends input, and hands the hook what comes back.
22
+ *
23
+ * const chat = useAgUiRun();
24
+ * onSubmit={(text) => {
25
+ * chat.dispatch(userMessageEvents(id(), text));
26
+ * void chat.run(agent.stream({ threadId, text }));
27
+ * }}
28
+ */
29
+ export function useAgUiRun({ initialView, onError } = {}) {
30
+ const [view, setView] = React.useState(() => initialView ?? createChatView());
31
+ const [running, setRunning] = React.useState(false);
32
+ const current = React.useRef(null);
33
+ const onErrorRef = React.useRef(onError);
34
+ React.useEffect(() => {
35
+ onErrorRef.current = onError;
36
+ });
37
+ const release = React.useCallback(() => {
38
+ const active = current.current;
39
+ current.current = null;
40
+ active?.release();
41
+ }, []);
42
+ React.useEffect(() => release, [release]);
43
+ const run = React.useCallback((source) => {
44
+ release();
45
+ let live = true;
46
+ let cleanup = () => { };
47
+ let stopped = () => { };
48
+ const halted = new Promise((resolve) => {
49
+ stopped = () => resolve("stopped");
50
+ });
51
+ const token = {
52
+ release: () => {
53
+ live = false;
54
+ cleanup();
55
+ stopped();
56
+ },
57
+ };
58
+ current.current = token;
59
+ setRunning(true);
60
+ const fold = (event) => {
61
+ if (live)
62
+ setView((v) => reduceChat(v, event));
63
+ };
64
+ const end = (error) => {
65
+ if (!live)
66
+ return;
67
+ live = false;
68
+ if (current.current === token)
69
+ current.current = null;
70
+ if (error !== undefined) {
71
+ setView((v) => reduceChat(v, { type: EventType.RUN_ERROR, message: message(error) }));
72
+ onErrorRef.current?.(error);
73
+ }
74
+ setRunning(false);
75
+ };
76
+ if (typeof source === "function") {
77
+ let finished = () => { };
78
+ const ended = new Promise((resolve) => {
79
+ finished = resolve;
80
+ });
81
+ try {
82
+ const unsubscribe = source(fold, (error) => {
83
+ end(error);
84
+ finished();
85
+ });
86
+ if (live)
87
+ cleanup = unsubscribe;
88
+ else
89
+ unsubscribe();
90
+ }
91
+ catch (error) {
92
+ end(error);
93
+ finished();
94
+ }
95
+ return Promise.race([ended, halted]).then(() => { });
96
+ }
97
+ return (async () => {
98
+ try {
99
+ const iterator = source[Symbol.asyncIterator]();
100
+ cleanup = () => void iterator.return?.()?.catch(() => { });
101
+ for (;;) {
102
+ const next = await Promise.race([iterator.next(), halted]);
103
+ if (next === "stopped" || next.done)
104
+ break;
105
+ fold(next.value);
106
+ }
107
+ end();
108
+ }
109
+ catch (error) {
110
+ end(error);
111
+ }
112
+ })();
113
+ }, [release]);
114
+ const stop = React.useCallback(() => {
115
+ if (!current.current)
116
+ return;
117
+ release();
118
+ setRunning(false);
119
+ setView((v) => v.status === "running"
120
+ ? reduceChat(v, {
121
+ type: EventType.RUN_FINISHED,
122
+ threadId: v.threadId ?? "",
123
+ runId: v.runId ?? "",
124
+ outcome: { type: "cancelled" },
125
+ })
126
+ : v);
127
+ }, [release]);
128
+ const dispatch = React.useCallback((events) => {
129
+ setView((v) => foldChat(Array.isArray(events) ? events : [events], v));
130
+ }, []);
131
+ const reset = React.useCallback((next) => {
132
+ release();
133
+ setRunning(false);
134
+ setView(next ?? createChatView());
135
+ }, [release]);
136
+ return { view, running, run, stop, dispatch, reset };
137
+ }
package/dist/index.d.ts CHANGED
@@ -90,3 +90,12 @@ export { documentStatus, type WorkflowDocumentStatus, type WorkflowRevision, } f
90
90
  export { WorkflowDiff, type WorkflowDiffProps } from "./workflow/editor/WorkflowDiff.js";
91
91
  export { WorkflowImportPreview, type WorkflowImportPreviewProps, } from "./workflow/editor/WorkflowImportPreview.js";
92
92
  export { WorkflowWorkspace, type WorkflowWorkspaceProps, } from "./workflow/editor/WorkflowWorkspace.js";
93
+ export { contentText, createChatView, foldChat, isChatRunning, parseToolCallArgs, reduceChat, type AgUiEvent, type AgUiInterrupt, type ChatApproval, type ChatMessage, type ChatRole, type ChatRunStatus, type ChatStep, type ChatStepStatus, type ChatTimelineItem, type ChatToolCall, type ChatToolCallStatus, type ChatView, } from "./chat/model.js";
94
+ export { applyJsonPatch } from "./chat/patch.js";
95
+ export { MessageBubble, type ChatContentRenderer, type MessageBubbleProps, } from "./chat/MessageBubble.js";
96
+ export { MessageList, type MessageListProps } from "./chat/MessageList.js";
97
+ export { Composer, type ComposerProps } from "./chat/Composer.js";
98
+ export { ToolCallCard, type ToolCallCardProps } from "./chat/ToolCallCard.js";
99
+ export { AgentProgress, CHAT_RUN_TONE, chatStepStages, type AgentProgressProps, } from "./chat/AgentProgress.js";
100
+ export { ModelPicker, type ModelPickerProps } from "./chat/ModelPicker.js";
101
+ export { useAgUiRun, userMessageEvents, type AgUiEventSource, type AgUiRun, type AgUiSubscribe, type UseAgUiRunOptions, } from "./chat/useAgUiRun.js";
package/dist/index.js CHANGED
@@ -99,3 +99,13 @@ export { documentStatus, } from "./workflow/workspace.js";
99
99
  export { WorkflowDiff } from "./workflow/editor/WorkflowDiff.js";
100
100
  export { WorkflowImportPreview, } from "./workflow/editor/WorkflowImportPreview.js";
101
101
  export { WorkflowWorkspace, } from "./workflow/editor/WorkflowWorkspace.js";
102
+ // Chat (AG-UI)
103
+ export { contentText, createChatView, foldChat, isChatRunning, parseToolCallArgs, reduceChat, } from "./chat/model.js";
104
+ export { applyJsonPatch } from "./chat/patch.js";
105
+ export { MessageBubble, } from "./chat/MessageBubble.js";
106
+ export { MessageList } from "./chat/MessageList.js";
107
+ export { Composer } from "./chat/Composer.js";
108
+ export { ToolCallCard } from "./chat/ToolCallCard.js";
109
+ export { AgentProgress, CHAT_RUN_TONE, chatStepStages, } from "./chat/AgentProgress.js";
110
+ export { ModelPicker } from "./chat/ModelPicker.js";
111
+ export { useAgUiRun, userMessageEvents, } from "./chat/useAgUiRun.js";
@@ -6,7 +6,7 @@ import type * as React from "react";
6
6
  * reaches buttons as it reaches panels.
7
7
  */
8
8
  export declare const buttonVariants: (props?: ({
9
- variant?: "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined;
9
+ variant?: "destructive" | "link" | "default" | "outline" | "secondary" | "ghost" | null | undefined;
10
10
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
11
11
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
12
12
  export interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
@@ -1,4 +1,5 @@
1
1
  import type { ProductTheme } from "../appearance/product.js";
2
+ import type { ChatRole, ChatRunStatus, ChatToolCallStatus } from "../chat/model.js";
2
3
  import type { WorkflowQuestionState } from "../workflow/question.js";
3
4
  import type { WorkflowAction, WorkflowAvailability, WorkflowScope, WorkflowStatus } from "../workflow/model.js";
4
5
  import type { ArtifactStatus, GateStatus, ReviewDecision, ReviewVerdict, RunStageStatus } from "../workflow/run.js";
@@ -550,6 +551,71 @@ export interface Messages {
550
551
  /** A field extraction found nothing in. */
551
552
  empty: string;
552
553
  };
554
+ chat: {
555
+ /** The message list's name. */
556
+ conversation: string;
557
+ empty: string;
558
+ emptyDescription: string;
559
+ /** Who said it: "You", "Assistant", "System", "Developer", "Tool". */
560
+ role: (role: ChatRole) => string;
561
+ /** A message's accessible name. */
562
+ messageFrom: (speaker: string) => string;
563
+ /** An answer that has started but has no text yet. */
564
+ thinking: string;
565
+ /** Read after a message still being written. */
566
+ writing: string;
567
+ jumpToLatest: string;
568
+ runFailed: string;
569
+ retry: string;
570
+ /** The composer's field. */
571
+ composer: string;
572
+ placeholder: string;
573
+ composerHint: string;
574
+ send: string;
575
+ stop: string;
576
+ attachments: string;
577
+ /** A tool call's accessible name. */
578
+ toolCall: (name: string) => string;
579
+ toolStatus: (status: ChatToolCallStatus | "approval") => string;
580
+ details: string;
581
+ arguments: string;
582
+ noArguments: string;
583
+ result: string;
584
+ toolError: string;
585
+ approvalNeeded: string;
586
+ approve: string;
587
+ deny: string;
588
+ approveTitle: (name: string) => string;
589
+ approveDescription: string;
590
+ denyTitle: (name: string) => string;
591
+ denyDescription: string;
592
+ denyReason: string;
593
+ /** The agent progress section's name. */
594
+ progress: string;
595
+ steps: string;
596
+ runStatus: (status: ChatRunStatus) => string;
597
+ model: string;
598
+ };
599
+ agent: {
600
+ /** The assistant's name: the panel's title and the launcher's. Takes the product's name when there is one. */
601
+ title: (product?: string) => string;
602
+ /** The launcher's name when closed and open. */
603
+ open: (title: string) => string;
604
+ close: string;
605
+ /** Read with the launcher's name: "2 unread". */
606
+ unread: (count: number) => string;
607
+ newConversation: string;
608
+ expand: string;
609
+ restore: string;
610
+ /** The suggestion chips' group. */
611
+ suggestions: string;
612
+ emptyTitle: string;
613
+ emptyDescription: string;
614
+ /** Under the composer while a tool waits on the person. */
615
+ waitingOnYou: string;
616
+ /** The shortcut as people read it, from the key list ("⌘/", "Ctrl+/"). */
617
+ shortcut: (keys: string) => string;
618
+ };
553
619
  }
554
620
  export declare const defaultMessages: Messages;
555
621
  /** Any subset of the messages, one level deep. */
@@ -781,6 +781,77 @@ export const defaultMessages = {
781
781
  cancel: "Cancel",
782
782
  empty: "Nothing found",
783
783
  },
784
+ chat: {
785
+ conversation: "Conversation",
786
+ empty: "No messages yet",
787
+ emptyDescription: "Ask a question to start.",
788
+ role: (role) => ({
789
+ user: "You",
790
+ assistant: "Assistant",
791
+ system: "System",
792
+ developer: "Developer",
793
+ tool: "Tool",
794
+ })[role],
795
+ messageFrom: (speaker) => `Message from ${speaker}`,
796
+ thinking: "Thinking",
797
+ writing: "Still writing",
798
+ jumpToLatest: "Jump to latest",
799
+ runFailed: "The run failed",
800
+ retry: "Retry",
801
+ composer: "Message",
802
+ placeholder: "Ask anything",
803
+ composerHint: "Enter to send, Shift+Enter for a new line.",
804
+ send: "Send",
805
+ stop: "Stop",
806
+ attachments: "Attachments",
807
+ toolCall: (name) => `Tool call ${name}`,
808
+ toolStatus: (status) => ({
809
+ streaming: "preparing",
810
+ running: "running",
811
+ complete: "done",
812
+ failed: "failed",
813
+ cancelled: "cancelled",
814
+ approval: "needs approval",
815
+ })[status],
816
+ details: "Details",
817
+ arguments: "Arguments",
818
+ noArguments: "No arguments",
819
+ result: "Result",
820
+ toolError: "Error",
821
+ approvalNeeded: "The agent is waiting for your approval to run this.",
822
+ approve: "Approve",
823
+ deny: "Deny",
824
+ approveTitle: (name) => `Run ${name}?`,
825
+ approveDescription: "The agent runs it with the arguments shown and continues.",
826
+ denyTitle: (name) => `Deny ${name}?`,
827
+ denyDescription: "The agent does not run it and is told why.",
828
+ denyReason: "Reason",
829
+ progress: "Agent progress",
830
+ steps: "Steps",
831
+ runStatus: (status) => ({
832
+ idle: "idle",
833
+ running: "running",
834
+ finished: "finished",
835
+ interrupted: "waiting for you",
836
+ cancelled: "stopped",
837
+ error: "failed",
838
+ })[status],
839
+ model: "Model",
840
+ },
841
+ agent: {
842
+ title: (product) => (product ? `${product} assistant` : "Assistant"),
843
+ open: (title) => `Open ${title}`,
844
+ close: "Close",
845
+ unread: (count) => `${count.toLocaleString("en")} unread`,
846
+ newConversation: "New conversation",
847
+ expand: "Expand",
848
+ restore: "Restore",
849
+ suggestions: "Suggestions",
850
+ emptyTitle: "How can I help?",
851
+ emptyDescription: "Ask about what is on this page, or pick a starting point.",
852
+ waitingOnYou: "Answer the card above to continue.",
853
+ shortcut: (keys) => `Shortcut ${keys}`,
854
+ },
784
855
  };
785
856
  export function mergeMessages(overrides) {
786
857
  if (!overrides)
@@ -0,0 +1,52 @@
1
+ import { ACCENT_KEYS, DEFAULT_ACCENT, GROUNDS, GROUND_KEYS, type FamilyAccent, type Ground, type StatusName } from "../tokens.js";
2
+ export declare const COLOR_TOKENS: readonly ["background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary", "primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent", "accent-foreground", "destructive", "border", "input", "ring", "chart-1", "chart-2", "chart-3", "chart-4", "chart-5", "sidebar", "sidebar-foreground", "sidebar-primary", "sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring", "sidebar-muted-foreground", "surface-0", "surface-1", "surface-2", ...(StatusName | "success-fg" | "warning-fg" | "info-fg" | "danger-fg" | "success-bg" | "warning-bg" | "info-bg" | "danger-bg" | "success-border" | "warning-border" | "info-border" | "danger-border")[]];
3
+ export type ColorToken = (typeof COLOR_TOKENS)[number];
4
+ export type Palette = Record<ColorToken, string>;
5
+ export { ACCENT_KEYS, DEFAULT_ACCENT, GROUNDS, GROUND_KEYS };
6
+ export type { FamilyAccent, Ground };
7
+ /** The ground `:root` paints when nothing chooses one (the preset's default). */
8
+ export declare const DEFAULT_GROUND: Ground;
9
+ /**
10
+ * The type stacks of the preset (`--font-sans`, `--font-mono`, `--font-head`
11
+ * in preset.css; a test holds them equal). Each names the app's loaded font
12
+ * variable first, then the family, then system fallbacks.
13
+ */
14
+ export declare const FONTS: {
15
+ readonly sans: "var(--font-plex-sans, \"IBM Plex Sans\"), system-ui, sans-serif";
16
+ readonly mono: "var(--font-plex-mono, \"IBM Plex Mono\"), ui-monospace, SFMono-Regular, Menlo, monospace";
17
+ readonly head: "var(--font-dm-sans, \"DM Sans\"), system-ui, sans-serif";
18
+ };
19
+ /** The family a stack names, for targets that take one font name (React Native). */
20
+ export declare function fontFamily(stack: string): string;
21
+ /** `--radius` in px, and the motion durations in ms, as numbers. */
22
+ export declare function staticNumbers(): {
23
+ radius: number;
24
+ motion: Record<"fast" | "base" | "slow", number>;
25
+ };
26
+ export interface Rgba {
27
+ r: number;
28
+ g: number;
29
+ b: number;
30
+ a: number;
31
+ }
32
+ /** Parses `#rrggbb` or `rgba(r, g, b, a)`; anything else is a token this module does not know. */
33
+ export declare function parseColor(value: string): Rgba;
34
+ /**
35
+ * A translucent token laid on its surface, for targets with no alpha
36
+ * (a terminal, Tk): `sidebar-*` tokens on the sidebar, the rest on the
37
+ * background. Opaque tokens come back as they are.
38
+ */
39
+ export declare function flatten(palette: Palette, token: ColorToken): Rgba;
40
+ /** The concrete palette of one ground with one family accent, with no product brand. */
41
+ export declare function resolvePalette(ground: Ground, accent?: FamilyAccent): Palette;
42
+ /** Every ground with every family accent. */
43
+ export declare function allPalettes(): Record<Ground, Record<FamilyAccent, Palette>>;
44
+ /**
45
+ * A target's values split for compact output: per ground, the entries every
46
+ * accent agrees on, and per ground and accent, the entries that move with
47
+ * the accent.
48
+ */
49
+ export declare function splitByAccent<K extends string, V>(values: (ground: Ground, accent: FamilyAccent) => Record<K, V>): {
50
+ ground: Record<Ground, Partial<Record<K, V>>>;
51
+ accent: Record<Ground, Record<FamilyAccent, Partial<Record<K, V>>>>;
52
+ };
@@ -0,0 +1,208 @@
1
+ import { ACCENTS, ACCENT_KEYS, DEFAULT_ACCENT, GROUNDS, GROUND_KEYS, MODE_TOKENS, STATIC_TOKENS, SURFACES, } from "../tokens.js";
2
+ /**
3
+ * The palette a page paints on one ground with one family accent, as
4
+ * concrete colours: what the preset's custom properties compute to on that
5
+ * page with no product brand set. Every token mapping (JupyterLab, VS Code,
6
+ * Python, React Native, plain CSS) is generated from this, and a test holds
7
+ * it to the preset's own cascade.
8
+ *
9
+ * Values are `#rrggbb` or `rgba(r, g, b, a)`. The preset's one colour
10
+ * function, `color-mix(in oklab, var(--primary) N%, transparent)`, is the
11
+ * primary at alpha N% (mixing with transparent only scales alpha), so it is
12
+ * written that way here.
13
+ */
14
+ const STATUS_NAMES = ["success", "warning", "info", "danger"];
15
+ export const COLOR_TOKENS = [
16
+ "background",
17
+ "foreground",
18
+ "card",
19
+ "card-foreground",
20
+ "popover",
21
+ "popover-foreground",
22
+ "primary",
23
+ "primary-foreground",
24
+ "secondary",
25
+ "secondary-foreground",
26
+ "muted",
27
+ "muted-foreground",
28
+ "accent",
29
+ "accent-foreground",
30
+ "destructive",
31
+ "border",
32
+ "input",
33
+ "ring",
34
+ "chart-1",
35
+ "chart-2",
36
+ "chart-3",
37
+ "chart-4",
38
+ "chart-5",
39
+ "sidebar",
40
+ "sidebar-foreground",
41
+ "sidebar-primary",
42
+ "sidebar-primary-foreground",
43
+ "sidebar-accent",
44
+ "sidebar-accent-foreground",
45
+ "sidebar-border",
46
+ "sidebar-ring",
47
+ "sidebar-muted-foreground",
48
+ "surface-0",
49
+ "surface-1",
50
+ "surface-2",
51
+ ...STATUS_NAMES.flatMap((name) => [name, `${name}-fg`, `${name}-bg`, `${name}-border`]),
52
+ ];
53
+ export { ACCENT_KEYS, DEFAULT_ACCENT, GROUNDS, GROUND_KEYS };
54
+ /** The ground `:root` paints when nothing chooses one (the preset's default). */
55
+ export const DEFAULT_GROUND = "black";
56
+ /**
57
+ * The type stacks of the preset (`--font-sans`, `--font-mono`, `--font-head`
58
+ * in preset.css; a test holds them equal). Each names the app's loaded font
59
+ * variable first, then the family, then system fallbacks.
60
+ */
61
+ export const FONTS = {
62
+ sans: 'var(--font-plex-sans, "IBM Plex Sans"), system-ui, sans-serif',
63
+ mono: 'var(--font-plex-mono, "IBM Plex Mono"), ui-monospace, SFMono-Regular, Menlo, monospace',
64
+ head: 'var(--font-dm-sans, "DM Sans"), system-ui, sans-serif',
65
+ };
66
+ /** The family a stack names, for targets that take one font name (React Native). */
67
+ export function fontFamily(stack) {
68
+ const name = /"([^"]+)"/.exec(stack)?.[1];
69
+ if (!name)
70
+ throw new Error(`no family in ${stack}`);
71
+ return name;
72
+ }
73
+ /** `--radius` in px, and the motion durations in ms, as numbers. */
74
+ export function staticNumbers() {
75
+ const n = (value, unit) => {
76
+ if (!value?.endsWith(unit))
77
+ throw new Error(`expected ${unit}: ${value}`);
78
+ return Number(value.slice(0, -unit.length));
79
+ };
80
+ return {
81
+ radius: n(STATIC_TOKENS.radius, "px"),
82
+ motion: {
83
+ fast: n(STATIC_TOKENS["motion-fast"], "ms"),
84
+ base: n(STATIC_TOKENS["motion-base"], "ms"),
85
+ slow: n(STATIC_TOKENS["motion-slow"], "ms"),
86
+ },
87
+ };
88
+ }
89
+ /** Parses `#rrggbb` or `rgba(r, g, b, a)`; anything else is a token this module does not know. */
90
+ export function parseColor(value) {
91
+ const hex = /^#([0-9a-f]{6})$/i.exec(value);
92
+ if (hex) {
93
+ const n = parseInt(hex[1] ?? "", 16);
94
+ return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255, a: 1 };
95
+ }
96
+ const fn = /^rgba\((\d+), (\d+), (\d+), ([0-9.]+)\)$/.exec(value);
97
+ if (fn)
98
+ return { r: Number(fn[1]), g: Number(fn[2]), b: Number(fn[3]), a: Number(fn[4]) };
99
+ throw new Error(`unsupported colour ${value}`);
100
+ }
101
+ const rgbaString = ({ r, g, b, a }) => `rgba(${r}, ${g}, ${b}, ${a})`;
102
+ /**
103
+ * A translucent token laid on its surface, for targets with no alpha
104
+ * (a terminal, Tk): `sidebar-*` tokens on the sidebar, the rest on the
105
+ * background. Opaque tokens come back as they are.
106
+ */
107
+ export function flatten(palette, token) {
108
+ const top = parseColor(palette[token]);
109
+ if (top.a === 1)
110
+ return top;
111
+ const base = parseColor(palette[token.startsWith("sidebar-") ? "sidebar" : "background"]);
112
+ if (base.a !== 1)
113
+ throw new Error(`${token} sits on a translucent surface`);
114
+ const mix = (t, b) => Math.round(t * top.a + b * (1 - top.a));
115
+ return { r: mix(top.r, base.r), g: mix(top.g, base.g), b: mix(top.b, base.b), a: 1 };
116
+ }
117
+ const PRIMARY_TINT = /^color-mix\(in oklab, var\(--primary\) (\d+)%, transparent\)$/;
118
+ /** A surface value: a colour as it is, or the preset's primary tint at its alpha. */
119
+ function surface(value, primary) {
120
+ const tint = PRIMARY_TINT.exec(value);
121
+ if (tint)
122
+ return rgbaString({ ...parseColor(primary), a: Number(tint[1]) / 100 });
123
+ parseColor(value);
124
+ return value;
125
+ }
126
+ /** The concrete palette of one ground with one family accent, with no product brand. */
127
+ export function resolvePalette(ground, accent = DEFAULT_ACCENT) {
128
+ const { mode } = GROUNDS[ground];
129
+ const s = SURFACES[ground];
130
+ const m = MODE_TOKENS[mode];
131
+ const spec = ACCENTS[accent];
132
+ const primary = mode === "dark" ? spec.dark : spec.light;
133
+ const onPrimary = mode === "dark" ? spec.onDark : spec.onLight;
134
+ const chart = { ...m.chart, ...spec.chart?.[mode] };
135
+ const status = Object.fromEntries(STATUS_NAMES.flatMap((name) => {
136
+ const t = m.status[name];
137
+ return [
138
+ [name, t.base],
139
+ [`${name}-fg`, t.fg],
140
+ [`${name}-bg`, t.bg],
141
+ [`${name}-border`, t.border],
142
+ ];
143
+ }));
144
+ const palette = {
145
+ background: s.background,
146
+ foreground: m.foreground,
147
+ card: s.card,
148
+ "card-foreground": m.foreground,
149
+ popover: s.popover,
150
+ "popover-foreground": m.foreground,
151
+ primary,
152
+ "primary-foreground": onPrimary,
153
+ secondary: s.secondary,
154
+ "secondary-foreground": m.foreground,
155
+ muted: s.muted,
156
+ "muted-foreground": s.mutedForeground,
157
+ accent: s.accent,
158
+ "accent-foreground": m.foreground,
159
+ destructive: m.destructive,
160
+ border: s.border,
161
+ input: s.input,
162
+ ring: primary,
163
+ "chart-1": primary,
164
+ ...chart,
165
+ // With no product brand, a brand sidebar falls back to the ground's card.
166
+ sidebar: s.sidebar === "brand" ? s.card : s.sidebar,
167
+ "sidebar-foreground": m.sidebarForeground,
168
+ "sidebar-primary": primary,
169
+ "sidebar-primary-foreground": onPrimary,
170
+ "sidebar-accent": s.sidebarAccent,
171
+ "sidebar-accent-foreground": m.foreground,
172
+ "sidebar-border": s.sidebarBorder,
173
+ "sidebar-ring": primary,
174
+ "sidebar-muted-foreground": s.mutedForeground,
175
+ "surface-0": s.background,
176
+ "surface-1": s.card,
177
+ "surface-2": s.popover,
178
+ ...status,
179
+ };
180
+ for (const token of COLOR_TOKENS)
181
+ palette[token] = surface(palette[token], primary);
182
+ return palette;
183
+ }
184
+ /** Every ground with every family accent. */
185
+ export function allPalettes() {
186
+ return Object.fromEntries(GROUND_KEYS.map((ground) => [
187
+ ground,
188
+ Object.fromEntries(ACCENT_KEYS.map((accent) => [accent, resolvePalette(ground, accent)])),
189
+ ]));
190
+ }
191
+ /**
192
+ * A target's values split for compact output: per ground, the entries every
193
+ * accent agrees on, and per ground and accent, the entries that move with
194
+ * the accent.
195
+ */
196
+ export function splitByAccent(values) {
197
+ const ground = {};
198
+ const accent = {};
199
+ for (const g of GROUND_KEYS) {
200
+ const each = ACCENT_KEYS.map((a) => [a, values(g, a)]);
201
+ const first = each[0][1];
202
+ const keys = Object.keys(first);
203
+ const moves = new Set(keys.filter((k) => each.some(([, v]) => JSON.stringify(v[k]) !== JSON.stringify(first[k]))));
204
+ ground[g] = Object.fromEntries(keys.filter((k) => !moves.has(k)).map((k) => [k, first[k]]));
205
+ accent[g] = Object.fromEntries(each.map(([a, v]) => [a, Object.fromEntries([...moves].map((k) => [k, v[k]]))]));
206
+ }
207
+ return { ground, accent };
208
+ }
@@ -0,0 +1,9 @@
1
+ export declare function renderJupyterLabCss(): string;
2
+ export declare function renderVsCodeCss(): string;
3
+ export declare function renderPlainCss(): string;
4
+ export declare function renderPython(): string;
5
+ export declare function renderNativeJs(): string;
6
+ export declare function renderNativeDts(): string;
7
+ export declare function renderReadmes(): Record<string, string>;
8
+ /** Every generated file under src/tokens/, by file name. */
9
+ export declare function renderTokenFiles(): Record<string, string>;