@calliopelabs/ui 0.13.0 → 0.16.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 (115) hide show
  1. package/README.md +192 -4
  2. package/dist/agent/AgentChat.d.ts +63 -0
  3. package/dist/agent/AgentChat.js +117 -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/WorkflowRenderers.d.ts +61 -0
  11. package/dist/agent/WorkflowRenderers.js +102 -0
  12. package/dist/agent/controller.d.ts +167 -0
  13. package/dist/agent/controller.js +496 -0
  14. package/dist/agent/copilotkit/CopilotKitAgentProvider.d.ts +41 -0
  15. package/dist/agent/copilotkit/CopilotKitAgentProvider.js +145 -0
  16. package/dist/agent/copilotkit/index.d.ts +6 -0
  17. package/dist/agent/copilotkit/index.js +6 -0
  18. package/dist/agent/engine.d.ts +41 -0
  19. package/dist/agent/engine.js +93 -0
  20. package/dist/agent/index.d.ts +17 -0
  21. package/dist/agent/index.js +17 -0
  22. package/dist/agent/mock.d.ts +56 -0
  23. package/dist/agent/mock.js +143 -0
  24. package/dist/agent/renderers.d.ts +49 -0
  25. package/dist/agent/renderers.js +27 -0
  26. package/dist/agent/surface.d.ts +30 -0
  27. package/dist/agent/surface.js +90 -0
  28. package/dist/agent/tool.d.ts +98 -0
  29. package/dist/agent/tool.js +81 -0
  30. package/dist/appearance/brands.d.ts +31 -12
  31. package/dist/appearance/brands.js +32 -51
  32. package/dist/chat/AgentProgress.d.ts +19 -0
  33. package/dist/chat/AgentProgress.js +34 -0
  34. package/dist/chat/Composer.d.ts +31 -0
  35. package/dist/chat/Composer.js +44 -0
  36. package/dist/chat/MessageBubble.d.ts +23 -0
  37. package/dist/chat/MessageBubble.js +26 -0
  38. package/dist/chat/MessageList.d.ts +43 -0
  39. package/dist/chat/MessageList.js +71 -0
  40. package/dist/chat/ModelPicker.d.ts +17 -0
  41. package/dist/chat/ModelPicker.js +13 -0
  42. package/dist/chat/ToolCallCard.d.ts +27 -0
  43. package/dist/chat/ToolCallCard.js +52 -0
  44. package/dist/chat/model.d.ts +123 -0
  45. package/dist/chat/model.js +337 -0
  46. package/dist/chat/patch.d.ts +12 -0
  47. package/dist/chat/patch.js +149 -0
  48. package/dist/chat/useAgUiRun.d.ts +52 -0
  49. package/dist/chat/useAgUiRun.js +137 -0
  50. package/dist/css/appearance.js +1 -1
  51. package/dist/css/calliope.css +5 -0
  52. package/dist/css/tokens.css +5 -0
  53. package/dist/index.d.ts +15 -1
  54. package/dist/index.js +16 -1
  55. package/dist/primitives/Badge.d.ts +14 -0
  56. package/dist/primitives/Badge.js +25 -0
  57. package/dist/primitives/Button.d.ts +1 -1
  58. package/dist/primitives/ContextMenu.d.ts +18 -0
  59. package/dist/primitives/ContextMenu.js +34 -0
  60. package/dist/primitives/ScrollArea.d.ts +16 -0
  61. package/dist/primitives/ScrollArea.js +11 -0
  62. package/dist/primitives/Toast.d.ts +37 -0
  63. package/dist/primitives/Toast.js +43 -0
  64. package/dist/primitives/Tooltip.d.ts +16 -0
  65. package/dist/primitives/Tooltip.js +26 -0
  66. package/dist/provider/messages.d.ts +135 -0
  67. package/dist/provider/messages.js +131 -0
  68. package/dist/theme/css-layer.js +2 -1
  69. package/dist/theme/mappings/palette.d.ts +52 -0
  70. package/dist/theme/mappings/palette.js +208 -0
  71. package/dist/theme/mappings/render.d.ts +9 -0
  72. package/dist/theme/mappings/render.js +520 -0
  73. package/dist/theme/mappings/targets.d.ts +43 -0
  74. package/dist/theme/mappings/targets.js +222 -0
  75. package/dist/theme/tokens.d.ts +10 -9
  76. package/dist/theme/tokens.generated.d.ts +356 -0
  77. package/dist/theme/tokens.generated.js +360 -0
  78. package/dist/theme/tokens.js +20 -264
  79. package/dist/tokens/README.jupyterlab.md +125 -0
  80. package/dist/tokens/README.md +15 -0
  81. package/dist/tokens/README.native.md +29 -0
  82. package/dist/tokens/README.plain.md +84 -0
  83. package/dist/tokens/README.python.md +33 -0
  84. package/dist/tokens/README.vscode.md +114 -0
  85. package/dist/tokens/jupyterlab.css +1710 -0
  86. package/dist/tokens/plain.css +930 -0
  87. package/dist/tokens/tokens.native.d.ts +85 -0
  88. package/dist/tokens/tokens.native.js +773 -0
  89. package/dist/tokens/tokens.py +1043 -0
  90. package/dist/tokens/vscode.css +1138 -0
  91. package/dist/workbench/FileBrowser.d.ts +62 -0
  92. package/dist/workbench/FileBrowser.js +117 -0
  93. package/dist/workbench/FileGrid.d.ts +13 -0
  94. package/dist/workbench/FileGrid.js +114 -0
  95. package/dist/workbench/FileIcon.d.ts +10 -0
  96. package/dist/workbench/FileIcon.js +24 -0
  97. package/dist/workbench/FileList.d.ts +35 -0
  98. package/dist/workbench/FileList.js +160 -0
  99. package/dist/workbench/FilePreview.d.ts +66 -0
  100. package/dist/workbench/FilePreview.js +108 -0
  101. package/dist/workbench/FileTree.d.ts +37 -0
  102. package/dist/workbench/FileTree.js +218 -0
  103. package/dist/workbench/UploadDropzone.d.ts +52 -0
  104. package/dist/workbench/UploadDropzone.js +115 -0
  105. package/dist/workbench/dnd.d.ts +17 -0
  106. package/dist/workbench/dnd.js +77 -0
  107. package/dist/workbench/index.d.ts +16 -0
  108. package/dist/workbench/index.js +16 -0
  109. package/dist/workbench/model.d.ts +72 -0
  110. package/dist/workbench/model.js +214 -0
  111. package/dist/workbench/selection.d.ts +16 -0
  112. package/dist/workbench/selection.js +46 -0
  113. package/dist/workbench/typeahead.d.ts +15 -0
  114. package/dist/workbench/typeahead.js +22 -0
  115. package/package.json +39 -2
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Frontend tools: what the agent may call in the browser, typed once. The
3
+ * parameters are a Standard Schema (zod 3.25+ and 4, valibot, arktype), which
4
+ * types the handler's arguments and validates them, or a plain JSON Schema
5
+ * with the argument type given as a type parameter. Pure: no React, no
6
+ * network, so a Server Component can import a tool's definition.
7
+ */
8
+ import type * as React from "react";
9
+ import type { ChatToolCall } from "../chat/model.js";
10
+ /** The Standard Schema v1 interface (standardschema.dev), inlined so no schema library is a dependency. */
11
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
12
+ readonly "~standard": {
13
+ readonly version: 1;
14
+ readonly vendor: string;
15
+ readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
16
+ readonly types?: {
17
+ readonly input: Input;
18
+ readonly output: Output;
19
+ } | undefined;
20
+ /** Standard JSON Schema v1, when the library implements it. */
21
+ readonly jsonSchema?: {
22
+ readonly input: (options: {
23
+ target: string;
24
+ }) => JsonSchema;
25
+ };
26
+ };
27
+ }
28
+ type StandardResult<Output> = {
29
+ readonly value: Output;
30
+ readonly issues?: undefined;
31
+ } | {
32
+ readonly issues: readonly {
33
+ readonly message: string;
34
+ }[];
35
+ };
36
+ /** A JSON Schema object, as AG-UI's `Tool.parameters` carries it. */
37
+ export type JsonSchema = Record<string, unknown>;
38
+ /** What a tool's handler is told besides its arguments. */
39
+ export interface AgentToolContext {
40
+ /** Aborts when the person stops the run or the agent unmounts. */
41
+ signal: AbortSignal;
42
+ toolCallId: string;
43
+ }
44
+ /** What a tool's renderer draws from. */
45
+ export interface AgentToolRenderProps<Args = unknown> {
46
+ call: ChatToolCall;
47
+ /** The arguments so far: undefined while they stream and do not parse yet. */
48
+ args: Partial<Args> | undefined;
49
+ /**
50
+ * Set while the call waits on the person (a tool with no handler): answers
51
+ * it, and the answer goes back to the agent as the tool's result.
52
+ */
53
+ respond?: (result: unknown) => void;
54
+ }
55
+ export interface AgentTool<Args = unknown> {
56
+ /** The name the agent calls it by. */
57
+ name: string;
58
+ /** What it does, for the agent. */
59
+ description?: string;
60
+ parameters?: StandardSchemaV1<unknown, Args> | JsonSchema;
61
+ /**
62
+ * Runs the call and returns its result (sent to the agent as JSON text). A
63
+ * tool with no handler and a `render` is a human-in-the-loop tool: the run
64
+ * waits until the rendered component calls `respond`.
65
+ */
66
+ handler?: (args: Args, context: AgentToolContext) => unknown;
67
+ /** Draws the call in the conversation instead of the default tool card. */
68
+ render?: (props: AgentToolRenderProps<Args>) => React.ReactNode;
69
+ /** Whether the agent runs again after the result. Defaults to true. */
70
+ followUp?: boolean;
71
+ /** False hides the tool from the agent without unregistering it. */
72
+ available?: boolean;
73
+ }
74
+ /** Types a tool's handler and renderer from its parameters; returns the tool as given. */
75
+ export declare function defineTool<Args = Record<string, unknown>>(tool: AgentTool<Args>): AgentTool<Args>;
76
+ export declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
77
+ /**
78
+ * A tool's parameters as JSON Schema for the agent: Standard JSON Schema when
79
+ * the library implements it, zod 4's `toJSONSchema()`, or the JSON Schema
80
+ * given. Throws for a schema it cannot convert (zod 3 without Standard JSON
81
+ * Schema), naming the tool.
82
+ */
83
+ export declare function toolJsonSchema(tool: Pick<AgentTool, "name" | "parameters">): JsonSchema;
84
+ /**
85
+ * The arguments as the handler receives them: validated (and transformed)
86
+ * through a Standard Schema, or as parsed for a JSON Schema. Rejects with
87
+ * the schema's issues.
88
+ */
89
+ export declare function validateToolArgs<Args>(tool: AgentTool<Args>, args: unknown): Promise<Args>;
90
+ /**
91
+ * A tool's parameters as a Standard Schema, for an engine that only takes
92
+ * one (CopilotKit): a JSON Schema becomes a schema that passes values through
93
+ * and reports itself as Standard JSON Schema.
94
+ */
95
+ export declare function toolStandardSchema(tool: AgentTool): StandardSchemaV1;
96
+ /** A tool result as the text AG-UI's tool message carries. */
97
+ export declare function toolResultText(result: unknown): string;
98
+ export {};
@@ -0,0 +1,81 @@
1
+ /** Types a tool's handler and renderer from its parameters; returns the tool as given. */
2
+ export function defineTool(tool) {
3
+ return tool;
4
+ }
5
+ export function isStandardSchema(value) {
6
+ return (typeof value === "object" &&
7
+ value !== null &&
8
+ "~standard" in value &&
9
+ typeof value["~standard"]?.validate === "function");
10
+ }
11
+ const EMPTY_OBJECT = { type: "object", properties: {} };
12
+ /**
13
+ * A tool's parameters as JSON Schema for the agent: Standard JSON Schema when
14
+ * the library implements it, zod 4's `toJSONSchema()`, or the JSON Schema
15
+ * given. Throws for a schema it cannot convert (zod 3 without Standard JSON
16
+ * Schema), naming the tool.
17
+ */
18
+ export function toolJsonSchema(tool) {
19
+ const { parameters } = tool;
20
+ if (parameters === undefined)
21
+ return EMPTY_OBJECT;
22
+ if (!isStandardSchema(parameters))
23
+ return parameters;
24
+ const standard = parameters["~standard"];
25
+ if (typeof standard.jsonSchema?.input === "function") {
26
+ return clean(standard.jsonSchema.input({ target: "draft-07" }));
27
+ }
28
+ const withMethod = parameters;
29
+ if (typeof withMethod.toJSONSchema === "function")
30
+ return clean(withMethod.toJSONSchema());
31
+ throw new Error(`Tool ${tool.name}: its ${standard.vendor} schema has no JSON Schema form. Use a library with Standard JSON Schema (zod 4, valibot 1, arktype 2) or pass a JSON Schema.`);
32
+ }
33
+ function clean(schema) {
34
+ const { $schema: _, ...rest } = schema;
35
+ return rest;
36
+ }
37
+ /**
38
+ * The arguments as the handler receives them: validated (and transformed)
39
+ * through a Standard Schema, or as parsed for a JSON Schema. Rejects with
40
+ * the schema's issues.
41
+ */
42
+ export async function validateToolArgs(tool, args) {
43
+ if (!isStandardSchema(tool.parameters))
44
+ return args;
45
+ const result = await tool.parameters["~standard"].validate(args);
46
+ if (result.issues) {
47
+ throw new Error(`Invalid arguments for ${tool.name}: ${result.issues.map((i) => i.message).join("; ")}`);
48
+ }
49
+ return result.value;
50
+ }
51
+ /**
52
+ * A tool's parameters as a Standard Schema, for an engine that only takes
53
+ * one (CopilotKit): a JSON Schema becomes a schema that passes values through
54
+ * and reports itself as Standard JSON Schema.
55
+ */
56
+ export function toolStandardSchema(tool) {
57
+ if (isStandardSchema(tool.parameters))
58
+ return tool.parameters;
59
+ const schema = toolJsonSchema(tool);
60
+ return {
61
+ "~standard": {
62
+ version: 1,
63
+ vendor: "calliope-json-schema",
64
+ validate: (value) => ({ value }),
65
+ jsonSchema: { input: () => schema },
66
+ },
67
+ };
68
+ }
69
+ /** A tool result as the text AG-UI's tool message carries. */
70
+ export function toolResultText(result) {
71
+ if (result === undefined)
72
+ return "";
73
+ if (typeof result === "string")
74
+ return result;
75
+ try {
76
+ return JSON.stringify(result);
77
+ }
78
+ catch {
79
+ return String(result);
80
+ }
81
+ }
@@ -1,6 +1,8 @@
1
1
  /**
2
- * The two consoles' brands, kept here so both apps and the catalog read the
3
- * same values. An app adds its navigation:
2
+ * The family's brands, kept here so every app and the catalog read the same
3
+ * values. Their colours (sidebar and the product's own accent) live in
4
+ * `tokens/brand/*.json`; this file adds names, grounds, defaults and themes.
5
+ * An app adds its navigation:
4
6
  *
5
7
  * export const astrolift = defineProduct({ ...astroliftBrand, nav: NAV });
6
8
  */
@@ -78,12 +80,12 @@ export declare const zentinelleBrand: {
78
80
  readonly id: "zentinelle";
79
81
  readonly name: "Zentinelle AI";
80
82
  readonly accent: {
81
- readonly key: "teal";
82
- readonly label: "Teal";
83
83
  readonly dark: "#37efed";
84
84
  readonly onDark: "#0b0b19";
85
85
  readonly light: "#0d7470";
86
86
  readonly onLight: "#f3fbf7";
87
+ readonly key: "teal";
88
+ readonly label: "Teal";
87
89
  };
88
90
  readonly sidebar: "#0b0b19";
89
91
  readonly grounds: readonly ["black", "charcoal", "graphite", "paper", "mist"];
@@ -102,12 +104,12 @@ export declare const workbenchBrand: {
102
104
  readonly id: "workbench";
103
105
  readonly name: "Calliope AI Workbench";
104
106
  readonly accent: {
105
- readonly key: "workbench-blue";
106
- readonly label: "Workbench blue";
107
107
  readonly dark: "#8fb8e8";
108
108
  readonly onDark: "#0b1120";
109
109
  readonly light: "#2f5d94";
110
110
  readonly onLight: "#f3fbf7";
111
+ readonly key: "workbench-blue";
112
+ readonly label: "Workbench blue";
111
113
  };
112
114
  readonly sidebar: "#0e1730";
113
115
  readonly grounds: readonly ["navy", "black", "charcoal", "graphite", "paper", "mist"];
@@ -124,12 +126,12 @@ export declare const chatStudioBrand: {
124
126
  readonly id: "chat-studio";
125
127
  readonly name: "Calliope AI Chat Studio";
126
128
  readonly accent: {
127
- readonly key: "studio-blue";
128
- readonly label: "Studio blue";
129
129
  readonly dark: "#8ab4f8";
130
130
  readonly onDark: "#0c0c0c";
131
131
  readonly light: "#1d4ed8";
132
132
  readonly onLight: "#ffffff";
133
+ readonly key: "studio-blue";
134
+ readonly label: "Studio blue";
133
135
  };
134
136
  readonly sidebar: "#080808";
135
137
  readonly grounds: readonly ["black", "charcoal", "graphite", "paper", "mist"];
@@ -147,12 +149,12 @@ export declare const loadrBrand: {
147
149
  readonly id: "loadr";
148
150
  readonly name: "Calliope AI Loadr";
149
151
  readonly accent: {
150
- readonly key: "loadr-blue";
151
- readonly label: "Loadr blue";
152
152
  readonly dark: "#89b4fa";
153
153
  readonly onDark: "#11111b";
154
154
  readonly light: "#1e66f5";
155
155
  readonly onLight: "#ffffff";
156
+ readonly key: "loadr-blue";
157
+ readonly label: "Loadr blue";
156
158
  };
157
159
  readonly sidebar: "#181825";
158
160
  readonly grounds: readonly ["black", "charcoal", "graphite", "paper", "mist"];
@@ -161,6 +163,23 @@ export declare const loadrBrand: {
161
163
  readonly light: "paper";
162
164
  };
163
165
  };
166
+ /**
167
+ * Calliope AI Deep Data Agent: the family amber on a warm umber sidebar, with
168
+ * charcoal and mist as its defaults. A separate product from Chat Studio, and
169
+ * kept visibly apart from it (Studio blue on black and paper), from Astrolift
170
+ * (green), Zentinelle (teal) and Loadr (blue).
171
+ */
172
+ export declare const deepDataAgentBrand: {
173
+ readonly id: "deep-data-agent";
174
+ readonly name: "Deep Data Agent";
175
+ readonly accent: "amber";
176
+ readonly sidebar: "#1a150c";
177
+ readonly grounds: readonly ["charcoal", "black", "graphite", "paper", "mist"];
178
+ readonly defaultGround: {
179
+ readonly dark: "charcoal";
180
+ readonly light: "mist";
181
+ };
182
+ };
164
183
  /**
165
184
  * Cykick: cyan on near-black with square corners, from
166
185
  * `ui/theme/native-themes.json` (primary, background, radius 0). Cykick's
@@ -171,12 +190,12 @@ export declare const cykickBrand: {
171
190
  readonly id: "cykick";
172
191
  readonly name: "Cykick";
173
192
  readonly accent: {
174
- readonly key: "cykick-cyan";
175
- readonly label: "Cykick cyan";
176
193
  readonly dark: "#22d3ee";
177
194
  readonly onDark: "#07090e";
178
195
  readonly light: "#0e7490";
179
196
  readonly onLight: "#ffffff";
197
+ readonly key: "cykick-cyan";
198
+ readonly label: "Cykick cyan";
180
199
  };
181
200
  readonly sidebar: "#07090e";
182
201
  readonly grounds: readonly ["black", "charcoal", "graphite", "paper", "mist"];
@@ -1,6 +1,9 @@
1
+ import { TOKENS } from "../theme/tokens.generated.js";
1
2
  /**
2
- * The two consoles' brands, kept here so both apps and the catalog read the
3
- * same values. An app adds its navigation:
3
+ * The family's brands, kept here so every app and the catalog read the same
4
+ * values. Their colours (sidebar and the product's own accent) live in
5
+ * `tokens/brand/*.json`; this file adds names, grounds, defaults and themes.
6
+ * An app adds its navigation:
4
7
  *
5
8
  * export const astrolift = defineProduct({ ...astroliftBrand, nav: NAV });
6
9
  */
@@ -13,7 +16,7 @@ export const astroliftBrand = {
13
16
  id: "astrolift",
14
17
  name: "Astrolift AI",
15
18
  accent: "green",
16
- sidebar: "#04231a",
19
+ sidebar: TOKENS.brand.astrolift.sidebar,
17
20
  grounds: ["black", "charcoal", "graphite", "emerald", "paper", "mist"],
18
21
  defaultGround: { dark: "graphite", light: "mist" },
19
22
  themes: [
@@ -47,15 +50,8 @@ export const astroliftBrand = {
47
50
  export const zentinelleBrand = {
48
51
  id: "zentinelle",
49
52
  name: "Zentinelle AI",
50
- accent: {
51
- key: "teal",
52
- label: "Teal",
53
- dark: "#37efed",
54
- onDark: "#0b0b19",
55
- light: "#0d7470",
56
- onLight: "#f3fbf7",
57
- },
58
- sidebar: "#0b0b19",
53
+ accent: { key: "teal", label: "Teal", ...TOKENS.brand.zentinelle.accent },
54
+ sidebar: TOKENS.brand.zentinelle.sidebar,
59
55
  grounds: ["black", "charcoal", "graphite", "paper", "mist"],
60
56
  defaultGround: { dark: "black", light: "mist" },
61
57
  };
@@ -63,7 +59,8 @@ export const zentinelleBrand = {
63
59
  * The brands below are copied from what each product ships today, so nothing
64
60
  * changes when an app moves onto the library. Where a product has no value
65
61
  * the model needs (usually a light-ground accent), the nearest one that
66
- * passes the contrast tests is used and marked "derived" for the designer.
62
+ * passes the contrast tests is used and its token's description says
63
+ * "Derived" for the designer.
67
64
  */
68
65
  /**
69
66
  * Calliope AI Workbench (JunoHub, the installer's default theme, the desktop
@@ -74,16 +71,8 @@ export const zentinelleBrand = {
74
71
  export const workbenchBrand = {
75
72
  id: "workbench",
76
73
  name: "Calliope AI Workbench",
77
- accent: {
78
- key: "workbench-blue",
79
- label: "Workbench blue",
80
- dark: "#8fb8e8",
81
- onDark: "#0b1120",
82
- // derived: the hub has no light ground
83
- light: "#2f5d94",
84
- onLight: "#f3fbf7",
85
- },
86
- sidebar: "#0e1730",
74
+ accent: { key: "workbench-blue", label: "Workbench blue", ...TOKENS.brand.workbench.accent },
75
+ sidebar: TOKENS.brand.workbench.sidebar,
87
76
  grounds: ["navy", "black", "charcoal", "graphite", "paper", "mist"],
88
77
  defaultGround: { dark: "navy", light: "mist" },
89
78
  };
@@ -94,15 +83,8 @@ export const workbenchBrand = {
94
83
  export const chatStudioBrand = {
95
84
  id: "chat-studio",
96
85
  name: "Calliope AI Chat Studio",
97
- accent: {
98
- key: "studio-blue",
99
- label: "Studio blue",
100
- dark: "#8ab4f8",
101
- onDark: "#0c0c0c",
102
- light: "#1d4ed8",
103
- onLight: "#ffffff",
104
- },
105
- sidebar: "#080808",
86
+ accent: { key: "studio-blue", label: "Studio blue", ...TOKENS.brand["chat-studio"].accent },
87
+ sidebar: TOKENS.brand["chat-studio"].sidebar,
106
88
  grounds: ["black", "charcoal", "graphite", "paper", "mist"],
107
89
  defaultGround: { dark: "black", light: "paper" },
108
90
  };
@@ -114,18 +96,25 @@ export const chatStudioBrand = {
114
96
  export const loadrBrand = {
115
97
  id: "loadr",
116
98
  name: "Calliope AI Loadr",
117
- accent: {
118
- key: "loadr-blue",
119
- label: "Loadr blue",
120
- dark: "#89b4fa",
121
- onDark: "#11111b",
122
- light: "#1e66f5",
123
- onLight: "#ffffff",
124
- },
125
- sidebar: "#181825",
99
+ accent: { key: "loadr-blue", label: "Loadr blue", ...TOKENS.brand.loadr.accent },
100
+ sidebar: TOKENS.brand.loadr.sidebar,
126
101
  grounds: ["black", "charcoal", "graphite", "paper", "mist"],
127
102
  defaultGround: { dark: "charcoal", light: "paper" },
128
103
  };
104
+ /**
105
+ * Calliope AI Deep Data Agent: the family amber on a warm umber sidebar, with
106
+ * charcoal and mist as its defaults. A separate product from Chat Studio, and
107
+ * kept visibly apart from it (Studio blue on black and paper), from Astrolift
108
+ * (green), Zentinelle (teal) and Loadr (blue).
109
+ */
110
+ export const deepDataAgentBrand = {
111
+ id: "deep-data-agent",
112
+ name: "Deep Data Agent",
113
+ accent: "amber",
114
+ sidebar: TOKENS.brand["deep-data-agent"].sidebar,
115
+ grounds: ["charcoal", "black", "graphite", "paper", "mist"],
116
+ defaultGround: { dark: "charcoal", light: "mist" },
117
+ };
129
118
  /**
130
119
  * Cykick: cyan on near-black with square corners, from
131
120
  * `ui/theme/native-themes.json` (primary, background, radius 0). Cykick's
@@ -135,16 +124,8 @@ export const loadrBrand = {
135
124
  export const cykickBrand = {
136
125
  id: "cykick",
137
126
  name: "Cykick",
138
- accent: {
139
- key: "cykick-cyan",
140
- label: "Cykick cyan",
141
- dark: "#22d3ee",
142
- onDark: "#07090e",
143
- // derived: no light accent in native-themes.json
144
- light: "#0e7490",
145
- onLight: "#ffffff",
146
- },
147
- sidebar: "#07090e",
127
+ accent: { key: "cykick-cyan", label: "Cykick cyan", ...TOKENS.brand.cykick.accent },
128
+ sidebar: TOKENS.brand.cykick.sidebar,
148
129
  grounds: ["black", "charcoal", "graphite", "paper", "mist"],
149
130
  defaultGround: { dark: "black", light: "mist" },
150
131
  defaultCorners: 0,
@@ -0,0 +1,19 @@
1
+ import type { StatusTone } from "../status/tones.js";
2
+ import type { RunStage } from "../workflow/run.js";
3
+ import type { ChatRunStatus, ChatStep, ChatView } from "./model.js";
4
+ export interface AgentProgressProps {
5
+ /** The run: a `ChatView`, or the part of one this reads. */
6
+ view: Pick<ChatView, "status" | "steps" | "error">;
7
+ /** Names the section. Defaults to "Agent progress". */
8
+ label?: string;
9
+ className?: string;
10
+ }
11
+ export declare const CHAT_RUN_TONE: Record<ChatRunStatus, StatusTone>;
12
+ /** A run's steps as the run timeline's stages. */
13
+ export declare function chatStepStages(steps: readonly ChatStep[]): RunStage[];
14
+ /**
15
+ * Where the agent's run stands: its status, and its steps on the library's
16
+ * RunTimeline, so a chat run reads like any other run. The error, when the
17
+ * run failed, sits beside the status.
18
+ */
19
+ export declare function AgentProgress({ view, label, className }: AgentProgressProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../lib/cn.js";
4
+ import { useUI } from "../provider/UIProvider.js";
5
+ import { StatusBadge } from "../status/StatusBadge.js";
6
+ import { RunTimeline } from "../workflow/RunTimeline.js";
7
+ export const CHAT_RUN_TONE = {
8
+ idle: "muted",
9
+ running: "info",
10
+ finished: "ok",
11
+ interrupted: "warn",
12
+ cancelled: "muted",
13
+ error: "error",
14
+ };
15
+ const STAGE = {
16
+ running: "running",
17
+ finished: "passed",
18
+ failed: "failed",
19
+ cancelled: "cancelled",
20
+ };
21
+ /** A run's steps as the run timeline's stages. */
22
+ export function chatStepStages(steps) {
23
+ return steps.map((step, i) => ({ id: String(i), label: step.name, status: STAGE[step.status] }));
24
+ }
25
+ /**
26
+ * Where the agent's run stands: its status, and its steps on the library's
27
+ * RunTimeline, so a chat run reads like any other run. The error, when the
28
+ * run failed, sits beside the status.
29
+ */
30
+ export function AgentProgress({ view, label, className }) {
31
+ const { messages } = useUI();
32
+ const t = messages.chat;
33
+ return (_jsxs("section", { "aria-label": label ?? t.progress, "data-slot": "agent-progress", className: cn("flex min-w-0 flex-col gap-3", className), children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx(StatusBadge, { tone: CHAT_RUN_TONE[view.status], pulse: view.status === "running", children: t.runStatus(view.status) }), view.status === "error" && view.error && (_jsx("p", { className: "min-w-0 text-sm [overflow-wrap:anywhere] text-danger-fg", children: view.error.message }))] }), view.steps.length > 0 && _jsx(RunTimeline, { stages: chatStepStages(view.steps), label: t.steps })] }));
34
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from "react";
2
+ export interface ComposerProps {
3
+ /** The draft, when the host holds it. Leave it out and the composer keeps its own. */
4
+ value?: string;
5
+ onValueChange?: (value: string) => void;
6
+ /**
7
+ * Sends the trimmed draft. A composer that keeps its own draft clears it;
8
+ * a host that holds it clears it when it wants to.
9
+ */
10
+ onSubmit: (text: string) => void;
11
+ /** A run is in flight: send is held, and stop shows when `onStop` is given. */
12
+ running?: boolean;
13
+ onStop?: () => void;
14
+ disabled?: boolean;
15
+ placeholder?: string;
16
+ /** Names the field. Defaults to "Message". */
17
+ label?: string;
18
+ /** Above the field: the files the host has attached, as chips. */
19
+ attachments?: React.ReactNode;
20
+ /** Beside send: an attach button, a ModelPicker. */
21
+ actions?: React.ReactNode;
22
+ autoFocus?: boolean;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * Where the person writes. Multi-line, growing with the text; Enter sends,
27
+ * Shift+Enter breaks the line, and nothing sends while an input method is
28
+ * composing. While a run is in flight, send is held and stop is offered.
29
+ * Attachments and actions are slots: the host owns files and models.
30
+ */
31
+ export declare function Composer({ value, onValueChange, onSubmit, running, onStop, disabled, placeholder, label, attachments, actions, autoFocus, className, }: ComposerProps): React.JSX.Element;
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ArrowUpIcon, SquareIcon } from "lucide-react";
4
+ import * as React from "react";
5
+ import { cn } from "../lib/cn.js";
6
+ import { Button } from "../primitives/Button.js";
7
+ import { useUI } from "../provider/UIProvider.js";
8
+ /**
9
+ * Where the person writes. Multi-line, growing with the text; Enter sends,
10
+ * Shift+Enter breaks the line, and nothing sends while an input method is
11
+ * composing. While a run is in flight, send is held and stop is offered.
12
+ * Attachments and actions are slots: the host owns files and models.
13
+ */
14
+ export function Composer({ value, onValueChange, onSubmit, running = false, onStop, disabled = false, placeholder, label, attachments, actions, autoFocus, className, }) {
15
+ const { messages } = useUI();
16
+ const t = messages.chat;
17
+ const hintId = React.useId();
18
+ const [own, setOwn] = React.useState("");
19
+ const draft = value ?? own;
20
+ const canSend = !disabled && !running && draft.trim() !== "";
21
+ const change = (next) => {
22
+ if (value === undefined)
23
+ setOwn(next);
24
+ onValueChange?.(next);
25
+ };
26
+ const submit = () => {
27
+ if (!canSend)
28
+ return;
29
+ onSubmit(draft.trim());
30
+ if (value === undefined)
31
+ setOwn("");
32
+ };
33
+ return (_jsxs("form", { "data-slot": "composer", onSubmit: (event) => {
34
+ event.preventDefault();
35
+ submit();
36
+ }, className: cn("flex min-w-0 flex-col rounded-lg border border-input bg-background focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 dark:bg-input/30", disabled && "opacity-50", className), children: [attachments && (_jsx("div", { role: "group", "aria-label": t.attachments, className: "flex min-w-0 flex-wrap gap-2 px-3 pt-2", children: attachments })), _jsx("textarea", { "aria-label": label ?? t.composer, "aria-describedby": hintId, value: draft, disabled: disabled, placeholder: placeholder ?? t.placeholder, rows: 1,
37
+ // eslint-disable-next-line jsx-a11y/no-autofocus -- the host asks for it, on a chat screen
38
+ autoFocus: autoFocus, onChange: (event) => change(event.target.value), onKeyDown: (event) => {
39
+ if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing)
40
+ return;
41
+ event.preventDefault();
42
+ submit();
43
+ }, className: "field-sizing-content max-h-48 min-h-10 w-full min-w-0 resize-none bg-transparent px-3 py-2 text-base outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed md:text-sm" }), _jsx("p", { id: hintId, className: "sr-only", children: t.composerHint }), _jsxs("div", { className: "flex min-w-0 items-center gap-2 px-2 pb-2", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: actions }), running && onStop ? (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onStop, children: [_jsx(SquareIcon, { "aria-hidden": true }), t.stop] })) : (_jsxs(Button, { type: "submit", size: "sm", disabled: !canSend, children: [_jsx(ArrowUpIcon, { "aria-hidden": true }), t.send] }))] })] }));
44
+ }
@@ -0,0 +1,23 @@
1
+ import type * as React from "react";
2
+ import type { ChatMessage } from "./model.js";
3
+ /**
4
+ * Renders a message's text: the host's markdown renderer, say. It receives
5
+ * the text so far (it grows while the message streams) and must treat it as
6
+ * untrusted. Without one, the text shows as plain text.
7
+ */
8
+ export type ChatContentRenderer = (content: string, message: ChatMessage) => React.ReactNode;
9
+ export interface MessageBubbleProps {
10
+ message: ChatMessage;
11
+ renderContent?: ChatContentRenderer;
12
+ /** Beside the speaker: a time, a copy button. */
13
+ meta?: React.ReactNode;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * One message, by role: the person's on the right in a muted bubble, the
18
+ * assistant's full width, system and developer notes dashed and quiet, a tool
19
+ * message in mono. While it streams, an empty one says it is thinking and a
20
+ * growing one shows a caret. Text is plain unless the host passes
21
+ * `renderContent`; plain text is never parsed as HTML.
22
+ */
23
+ export declare function MessageBubble({ message, renderContent, meta, className }: MessageBubbleProps): React.JSX.Element;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { cn } from "../lib/cn.js";
4
+ import { useUI } from "../provider/UIProvider.js";
5
+ import { StatusDot } from "../status/StatusDot.js";
6
+ const ROLE_CLASS = {
7
+ user: "ml-auto max-w-[85%] rounded-lg bg-muted px-3 py-2",
8
+ assistant: "",
9
+ system: "rounded-lg border border-dashed px-3 py-2 text-xs text-muted-foreground",
10
+ developer: "rounded-lg border border-dashed px-3 py-2 text-xs text-muted-foreground",
11
+ tool: "rounded-lg border bg-card px-3 py-2 font-mono text-xs",
12
+ };
13
+ /**
14
+ * One message, by role: the person's on the right in a muted bubble, the
15
+ * assistant's full width, system and developer notes dashed and quiet, a tool
16
+ * message in mono. While it streams, an empty one says it is thinking and a
17
+ * growing one shows a caret. Text is plain unless the host passes
18
+ * `renderContent`; plain text is never parsed as HTML.
19
+ */
20
+ export function MessageBubble({ message, renderContent, meta, className }) {
21
+ const { messages } = useUI();
22
+ const t = messages.chat;
23
+ const speaker = message.name ?? t.role(message.role);
24
+ const empty = message.content === "";
25
+ return (_jsxs("article", { "aria-label": t.messageFrom(speaker), "aria-busy": message.streaming || undefined, "data-slot": "message-bubble", "data-role": message.role, className: cn("flex min-w-0 flex-col gap-1", ROLE_CLASS[message.role], className), children: [(message.role !== "user" || meta) && (_jsxs("header", { className: "flex min-w-0 items-center gap-2 text-xs text-muted-foreground", children: [message.role !== "user" && _jsx("span", { className: "font-medium", children: speaker }), meta && _jsx("span", { className: "ml-auto flex items-center gap-2", children: meta })] })), message.streaming && empty ? (_jsxs("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(StatusDot, { tone: "info", pulse: true }), t.thinking] })) : (_jsxs("div", { className: "min-w-0 text-sm [overflow-wrap:break-word]", children: [renderContent ? (renderContent(message.content, message)) : (_jsx("p", { className: "whitespace-pre-wrap", children: message.content })), message.streaming && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": true, className: "ml-0.5 inline-block h-4 w-1.5 translate-y-0.5 bg-foreground/60 motion-safe:animate-pulse" }), _jsx("span", { className: "sr-only", children: t.writing })] }))] }))] }));
26
+ }
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import { type ChatContentRenderer } from "./MessageBubble.js";
3
+ import type { ChatTimelineItem, ChatToolCall, ChatView } from "./model.js";
4
+ import { type ToolCallCardProps } from "./ToolCallCard.js";
5
+ export interface MessageListProps {
6
+ /** The conversation: a `ChatView`, or the part of one the list reads. */
7
+ view: Pick<ChatView, "messages" | "toolCalls" | "timeline" | "status" | "error">;
8
+ renderContent?: ChatContentRenderer;
9
+ /**
10
+ * Draws a tool call the host has its own component for; return undefined
11
+ * for the default `ToolCallCard`.
12
+ */
13
+ renderToolCall?: (call: ChatToolCall) => React.ReactNode;
14
+ /** The tool's name for people, per call. */
15
+ toolTitle?: (call: ChatToolCall) => string | undefined;
16
+ onApprove?: ToolCallCardProps["onApprove"];
17
+ onDeny?: ToolCallCardProps["onDeny"];
18
+ /**
19
+ * Draws something after a timeline item: generative UI an agent sent
20
+ * between messages (a progress card, an artifact). Called for each item.
21
+ */
22
+ renderAfter?: (item: ChatTimelineItem) => React.ReactNode;
23
+ /** Drawn after the whole conversation, before a failed run's error: what the run waits on. */
24
+ footer?: React.ReactNode;
25
+ /** Offered beside a failed run's error. */
26
+ onRetry?: () => void;
27
+ /** The first page of history is loading. */
28
+ loading?: boolean;
29
+ /** Replaces the default empty state. */
30
+ empty?: React.ReactNode;
31
+ /** Names the list. Defaults to "Conversation". */
32
+ label?: string;
33
+ className?: string;
34
+ }
35
+ /**
36
+ * The conversation in arrival order: message bubbles with tool call cards
37
+ * between them, a failed run's error at the end. It follows the stream while
38
+ * the person is at the bottom and stops following when they scroll up, with
39
+ * a button back to the latest. Each bubble re-renders only when its message
40
+ * changes, so a long conversation streams without virtualization; a host
41
+ * with thousands of turns pages its history.
42
+ */
43
+ export declare function MessageList({ view, renderContent, renderToolCall, renderAfter, footer, toolTitle, onApprove, onDeny, onRetry, loading, empty, label, className, }: MessageListProps): React.JSX.Element;