@calliopelabs/ui 0.14.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 (64) hide show
  1. package/README.md +142 -1
  2. package/dist/agent/AgentChat.d.ts +8 -1
  3. package/dist/agent/AgentChat.js +43 -2
  4. package/dist/agent/WorkflowRenderers.d.ts +61 -0
  5. package/dist/agent/WorkflowRenderers.js +102 -0
  6. package/dist/agent/controller.d.ts +19 -0
  7. package/dist/agent/controller.js +33 -0
  8. package/dist/agent/copilotkit/CopilotKitAgentProvider.js +2 -2
  9. package/dist/agent/index.d.ts +4 -2
  10. package/dist/agent/index.js +3 -1
  11. package/dist/agent/mock.d.ts +26 -4
  12. package/dist/agent/mock.js +68 -37
  13. package/dist/agent/renderers.d.ts +49 -0
  14. package/dist/agent/renderers.js +27 -0
  15. package/dist/appearance/brands.d.ts +17 -0
  16. package/dist/appearance/brands.js +14 -0
  17. package/dist/chat/MessageBubble.js +1 -1
  18. package/dist/chat/MessageList.d.ts +9 -2
  19. package/dist/chat/MessageList.js +11 -8
  20. package/dist/css/appearance.js +1 -1
  21. package/dist/css/calliope.css +5 -0
  22. package/dist/css/tokens.css +5 -0
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.js +6 -1
  25. package/dist/primitives/Badge.d.ts +14 -0
  26. package/dist/primitives/Badge.js +25 -0
  27. package/dist/primitives/ContextMenu.d.ts +18 -0
  28. package/dist/primitives/ContextMenu.js +34 -0
  29. package/dist/primitives/ScrollArea.d.ts +16 -0
  30. package/dist/primitives/ScrollArea.js +11 -0
  31. package/dist/primitives/Toast.d.ts +37 -0
  32. package/dist/primitives/Toast.js +43 -0
  33. package/dist/primitives/Tooltip.d.ts +16 -0
  34. package/dist/primitives/Tooltip.js +26 -0
  35. package/dist/provider/messages.d.ts +69 -0
  36. package/dist/provider/messages.js +60 -0
  37. package/dist/theme/css-layer.js +2 -1
  38. package/dist/theme/tokens.generated.d.ts +3 -0
  39. package/dist/theme/tokens.generated.js +3 -0
  40. package/dist/workbench/FileBrowser.d.ts +62 -0
  41. package/dist/workbench/FileBrowser.js +117 -0
  42. package/dist/workbench/FileGrid.d.ts +13 -0
  43. package/dist/workbench/FileGrid.js +114 -0
  44. package/dist/workbench/FileIcon.d.ts +10 -0
  45. package/dist/workbench/FileIcon.js +24 -0
  46. package/dist/workbench/FileList.d.ts +35 -0
  47. package/dist/workbench/FileList.js +160 -0
  48. package/dist/workbench/FilePreview.d.ts +66 -0
  49. package/dist/workbench/FilePreview.js +108 -0
  50. package/dist/workbench/FileTree.d.ts +37 -0
  51. package/dist/workbench/FileTree.js +218 -0
  52. package/dist/workbench/UploadDropzone.d.ts +52 -0
  53. package/dist/workbench/UploadDropzone.js +115 -0
  54. package/dist/workbench/dnd.d.ts +17 -0
  55. package/dist/workbench/dnd.js +77 -0
  56. package/dist/workbench/index.d.ts +16 -0
  57. package/dist/workbench/index.js +16 -0
  58. package/dist/workbench/model.d.ts +72 -0
  59. package/dist/workbench/model.js +214 -0
  60. package/dist/workbench/selection.d.ts +16 -0
  61. package/dist/workbench/selection.js +46 -0
  62. package/dist/workbench/typeahead.d.ts +15 -0
  63. package/dist/workbench/typeahead.js +22 -0
  64. package/package.json +5 -1
@@ -1,8 +1,9 @@
1
1
  /**
2
- * A mock AG-UI agent for Storybook and tests: a real `@ag-ui/client`
3
- * `HttpAgent` whose fetch is answered from a script instead of the network,
4
- * as a server-sent event stream. The client's own parser, verifier and
5
- * message bookkeeping run exactly as they do against a live endpoint.
2
+ * Agents from functions: `createFunctionAgent` adapts any function that
3
+ * produces AG-UI events (a non-AG-UI backend, wrapped), and `createMockAgent`
4
+ * plays recorded turns for Storybook and tests. Both are a real
5
+ * `@ag-ui/client` `HttpAgent` whose fetch is answered in the page as a
6
+ * server-sent event stream, never over the network.
6
7
  */
7
8
  import { HttpAgent } from "@ag-ui/client";
8
9
  import { type Message, type RunAgentInput } from "@ag-ui/core";
@@ -23,6 +24,27 @@ export interface MockAgentOptions {
23
24
  /** Every request's input, for a test to read. */
24
25
  onRequest?: (input: RunAgentInput) => void;
25
26
  }
27
+ /**
28
+ * The events of one run, as a function produces them: a list, a promise of
29
+ * one, or an async iterable that yields them as they come. The run's start
30
+ * and finish are added unless the function emits them.
31
+ */
32
+ export type AgentRunFunction = (input: RunAgentInput, signal: AbortSignal) => Iterable<AgUiEvent> | AsyncIterable<AgUiEvent> | Promise<Iterable<AgUiEvent>>;
33
+ export interface FunctionAgentOptions {
34
+ threadId?: string;
35
+ initialMessages?: Message[];
36
+ initialState?: unknown;
37
+ /** Pause before each event, in ms (a recording played back at reading speed). */
38
+ delay?: number;
39
+ }
40
+ /**
41
+ * An AG-UI agent from a function: the adapter for a backend that does not
42
+ * speak AG-UI yet (a request and response endpoint, a custom stream), and
43
+ * the base of `createMockAgent`. It is a real `HttpAgent` whose transport is
44
+ * the function, so the client's parser, verifier and message bookkeeping run
45
+ * as they do against a live endpoint, and `stop` aborts `signal`.
46
+ */
47
+ export declare function createFunctionAgent(run: AgentRunFunction, { threadId, initialMessages, initialState, delay }?: FunctionAgentOptions): HttpAgent;
26
48
  /** An agent that answers from `turns`, streaming each event as SSE. */
27
49
  export declare function createMockAgent({ turns, delay, threadId, initialMessages, initialState, onRequest, }: MockAgentOptions): HttpAgent;
28
50
  /** An assistant's streamed reply, in `chunks`, as the events that make it. */
@@ -1,8 +1,9 @@
1
1
  /**
2
- * A mock AG-UI agent for Storybook and tests: a real `@ag-ui/client`
3
- * `HttpAgent` whose fetch is answered from a script instead of the network,
4
- * as a server-sent event stream. The client's own parser, verifier and
5
- * message bookkeeping run exactly as they do against a live endpoint.
2
+ * Agents from functions: `createFunctionAgent` adapts any function that
3
+ * produces AG-UI events (a non-AG-UI backend, wrapped), and `createMockAgent`
4
+ * plays recorded turns for Storybook and tests. Both are a real
5
+ * `@ag-ui/client` `HttpAgent` whose fetch is answered in the page as a
6
+ * server-sent event stream, never over the network.
6
7
  */
7
8
  import { HttpAgent } from "@ag-ui/client";
8
9
  import { EventType } from "@ag-ui/core";
@@ -16,56 +17,66 @@ const sleep = (ms, signal) => new Promise((resolve, reject) => {
16
17
  reject(new DOMException("Aborted", "AbortError"));
17
18
  }, { once: true });
18
19
  });
19
- /** Wraps a turn's events in its run's start and, unless they end it, its finish. */
20
- function framed(input, events) {
21
- const ends = events.some((e) => e.type === EventType.RUN_FINISHED || e.type === EventType.RUN_ERROR);
22
- return [
23
- { type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId },
24
- ...events,
25
- ...(ends
26
- ? []
27
- : [
28
- {
29
- type: EventType.RUN_FINISHED,
30
- threadId: input.threadId,
31
- runId: input.runId,
32
- },
33
- ]),
34
- ];
20
+ async function* eventsOf(output) {
21
+ if (Symbol.asyncIterator in output)
22
+ yield* output;
23
+ else
24
+ yield* output;
35
25
  }
36
- /** An agent that answers from `turns`, streaming each event as SSE. */
37
- export function createMockAgent({ turns, delay = 30, threadId, initialMessages, initialState, onRequest, }) {
38
- let turn = 0;
26
+ /**
27
+ * An AG-UI agent from a function: the adapter for a backend that does not
28
+ * speak AG-UI yet (a request and response endpoint, a custom stream), and
29
+ * the base of `createMockAgent`. It is a real `HttpAgent` whose transport is
30
+ * the function, so the client's parser, verifier and message bookkeeping run
31
+ * as they do against a live endpoint, and `stop` aborts `signal`.
32
+ */
33
+ export function createFunctionAgent(run, { threadId, initialMessages, initialState, delay = 0 } = {}) {
39
34
  const fetch = async (_url, init) => {
40
35
  const input = JSON.parse(String(init.body));
41
- onRequest?.(input);
42
- const index = turn++;
43
- const script = turns[Math.min(index, turns.length - 1)] ?? [];
44
- const events = framed(input, typeof script === "function" ? script(input, index) : script);
45
- const signal = init.signal;
36
+ const signal = init.signal ?? new AbortController().signal;
46
37
  const body = new ReadableStream({
47
38
  async start(controller) {
48
39
  try {
49
- for (const event of events) {
40
+ let ended = false;
41
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId })}\n\n`));
42
+ const output = run(input, signal);
43
+ const source = output instanceof Promise ? await output : output;
44
+ for await (const event of eventsOf(source)) {
50
45
  if (delay > 0)
51
46
  await sleep(delay, signal);
52
- else
53
- await Promise.resolve();
54
- if (signal?.aborted)
47
+ if (signal.aborted)
55
48
  throw new DOMException("Aborted", "AbortError");
49
+ if (event.type === EventType.RUN_STARTED)
50
+ continue;
51
+ if (event.type === EventType.RUN_FINISHED || event.type === EventType.RUN_ERROR)
52
+ ended = true;
56
53
  controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
57
54
  }
55
+ if (!ended) {
56
+ const finished = {
57
+ type: EventType.RUN_FINISHED,
58
+ threadId: input.threadId,
59
+ runId: input.runId,
60
+ };
61
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(finished)}\n\n`));
62
+ }
58
63
  controller.close();
59
64
  }
60
65
  catch (error) {
61
- controller.error(error);
66
+ if (signal.aborted)
67
+ controller.error(error);
68
+ else {
69
+ const failed = {
70
+ type: EventType.RUN_ERROR,
71
+ message: error instanceof Error ? error.message : String(error),
72
+ };
73
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(failed)}\n\n`));
74
+ controller.close();
75
+ }
62
76
  }
63
77
  },
64
78
  });
65
- return new Response(body, {
66
- status: 200,
67
- headers: { "content-type": "text/event-stream" },
68
- });
79
+ return new Response(body, { status: 200, headers: { "content-type": "text/event-stream" } });
69
80
  };
70
81
  return new HttpAgent({
71
82
  url: "https://agent.invalid/agui",
@@ -75,6 +86,26 @@ export function createMockAgent({ turns, delay = 30, threadId, initialMessages,
75
86
  ...(initialState !== undefined ? { initialState: initialState } : {}),
76
87
  });
77
88
  }
89
+ /** An agent that answers from `turns`, streaming each event as SSE. */
90
+ export function createMockAgent({ turns, delay = 30, threadId, initialMessages, initialState, onRequest, }) {
91
+ let turn = 0;
92
+ return createFunctionAgent(async function* (input) {
93
+ onRequest?.(input);
94
+ const index = turn++;
95
+ const script = turns[Math.min(index, turns.length - 1)] ?? [];
96
+ const events = typeof script === "function" ? script(input, index) : script;
97
+ // Yield once first, so even a delay of 0 streams instead of arriving in one piece.
98
+ for (const event of events) {
99
+ await Promise.resolve();
100
+ yield event;
101
+ }
102
+ }, {
103
+ delay,
104
+ ...(threadId ? { threadId } : {}),
105
+ ...(initialMessages ? { initialMessages } : {}),
106
+ ...(initialState !== undefined ? { initialState } : {}),
107
+ });
108
+ }
78
109
  /** An assistant's streamed reply, in `chunks`, as the events that make it. */
79
110
  export function textEvents(messageId, ...chunks) {
80
111
  return [
@@ -0,0 +1,49 @@
1
+ import type * as React from "react";
2
+ import { type AgUiInterrupt, type ChatToolCall } from "../chat/model.js";
3
+ import type { CalliopeAgentApi } from "./AgentProvider.js";
4
+ import type { AgentToolRenderProps } from "./tool.js";
5
+ /** What a tool renderer draws from: the call, its arguments and result, and the agent. */
6
+ export interface ToolRendererProps<Args = unknown> extends AgentToolRenderProps<Args> {
7
+ /** The result parsed as JSON, or as text when it is not JSON. */
8
+ result: unknown;
9
+ agent: CalliopeAgentApi;
10
+ }
11
+ /** What a custom-event renderer draws from: the latest value of its event, and the agent. */
12
+ export interface CustomRendererProps<Value = unknown> {
13
+ name: string;
14
+ value: Value;
15
+ /** Every value the event has had under this key, oldest first. */
16
+ history: readonly Value[];
17
+ agent: CalliopeAgentApi;
18
+ }
19
+ /** What an interrupt renderer draws from: the open interrupt and how to answer it. */
20
+ export interface InterruptRendererProps {
21
+ interrupt: AgUiInterrupt;
22
+ /** Answers it; the run resumes once every open interrupt has an answer. */
23
+ resolve: (payload?: unknown) => void;
24
+ cancel: () => void;
25
+ agent: CalliopeAgentApi;
26
+ }
27
+ export interface StateRendererProps<State = unknown> {
28
+ state: State;
29
+ setState: (state: State) => void;
30
+ agent: CalliopeAgentApi;
31
+ }
32
+ /**
33
+ * Generative UI: host components for what the agent emits. A tool call by
34
+ * name (the agent's backend tools as well as the browser's), an AG-UI
35
+ * `CUSTOM` event by name (drawn where it arrived; an event carrying an `id`
36
+ * in its value replaces its earlier values in place, so progress updates
37
+ * one card), an interrupt by reason, and the shared state.
38
+ */
39
+ export interface AgentRenderers {
40
+ tools?: Readonly<Record<string, React.ComponentType<ToolRendererProps<any>>>>;
41
+ custom?: Readonly<Record<string, React.ComponentType<CustomRendererProps<any>>>>;
42
+ interrupts?: Readonly<Record<string, React.ComponentType<InterruptRendererProps>>>;
43
+ /** Draws the shared state above the conversation. */
44
+ state?: React.ComponentType<StateRendererProps<any>>;
45
+ }
46
+ /** Several registries as one; a later one wins a name. */
47
+ export declare function mergeRenderers(...registries: (AgentRenderers | undefined)[]): AgentRenderers;
48
+ /** A tool's result as a renderer reads it. */
49
+ export declare function parseResult(call: ChatToolCall): unknown;
@@ -0,0 +1,27 @@
1
+ import { parseToolCallArgs } from "../chat/model.js";
2
+ /* eslint-enable @typescript-eslint/no-explicit-any */
3
+ /** Several registries as one; a later one wins a name. */
4
+ export function mergeRenderers(...registries) {
5
+ const merged = {
6
+ tools: {},
7
+ custom: {},
8
+ interrupts: {},
9
+ };
10
+ for (const r of registries) {
11
+ if (!r)
12
+ continue;
13
+ merged.tools = { ...merged.tools, ...r.tools };
14
+ merged.custom = { ...merged.custom, ...r.custom };
15
+ merged.interrupts = { ...merged.interrupts, ...r.interrupts };
16
+ if (r.state)
17
+ merged.state = r.state;
18
+ }
19
+ return merged;
20
+ }
21
+ /** A tool's result as a renderer reads it. */
22
+ export function parseResult(call) {
23
+ if (call.result === undefined)
24
+ return undefined;
25
+ const parsed = parseToolCallArgs(call.result);
26
+ return parsed === undefined ? call.result : parsed;
27
+ }
@@ -163,6 +163,23 @@ export declare const loadrBrand: {
163
163
  readonly light: "paper";
164
164
  };
165
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
+ };
166
183
  /**
167
184
  * Cykick: cyan on near-black with square corners, from
168
185
  * `ui/theme/native-themes.json` (primary, background, radius 0). Cykick's
@@ -101,6 +101,20 @@ export const loadrBrand = {
101
101
  grounds: ["black", "charcoal", "graphite", "paper", "mist"],
102
102
  defaultGround: { dark: "charcoal", light: "paper" },
103
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
+ };
104
118
  /**
105
119
  * Cykick: cyan on near-black with square corners, from
106
120
  * `ui/theme/native-themes.json` (primary, background, radius 0). Cykick's
@@ -22,5 +22,5 @@ export function MessageBubble({ message, renderContent, meta, className }) {
22
22
  const t = messages.chat;
23
23
  const speaker = message.name ?? t.role(message.role);
24
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:anywhere]", 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 })] }))] }))] }));
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
26
  }
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type ChatContentRenderer } from "./MessageBubble.js";
3
- import type { ChatToolCall, ChatView } from "./model.js";
3
+ import type { ChatTimelineItem, ChatToolCall, ChatView } from "./model.js";
4
4
  import { type ToolCallCardProps } from "./ToolCallCard.js";
5
5
  export interface MessageListProps {
6
6
  /** The conversation: a `ChatView`, or the part of one the list reads. */
@@ -15,6 +15,13 @@ export interface MessageListProps {
15
15
  toolTitle?: (call: ChatToolCall) => string | undefined;
16
16
  onApprove?: ToolCallCardProps["onApprove"];
17
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;
18
25
  /** Offered beside a failed run's error. */
19
26
  onRetry?: () => void;
20
27
  /** The first page of history is loading. */
@@ -33,4 +40,4 @@ export interface MessageListProps {
33
40
  * changes, so a long conversation streams without virtualization; a host
34
41
  * with thousands of turns pages its history.
35
42
  */
36
- export declare function MessageList({ view, renderContent, renderToolCall, toolTitle, onApprove, onDeny, onRetry, loading, empty, label, className, }: MessageListProps): React.JSX.Element;
43
+ export declare function MessageList({ view, renderContent, renderToolCall, renderAfter, footer, toolTitle, onApprove, onDeny, onRetry, loading, empty, label, className, }: MessageListProps): React.JSX.Element;
@@ -21,7 +21,7 @@ const PINNED = 48;
21
21
  * changes, so a long conversation streams without virtualization; a host
22
22
  * with thousands of turns pages its history.
23
23
  */
24
- export function MessageList({ view, renderContent, renderToolCall, toolTitle, onApprove, onDeny, onRetry, loading = false, empty, label, className, }) {
24
+ export function MessageList({ view, renderContent, renderToolCall, renderAfter, footer, toolTitle, onApprove, onDeny, onRetry, loading = false, empty, label, className, }) {
25
25
  const { messages } = useUI();
26
26
  const t = messages.chat;
27
27
  const scroller = React.useRef(null);
@@ -48,16 +48,19 @@ export function MessageList({ view, renderContent, renderToolCall, toolTitle, on
48
48
  body = empty ?? _jsx(EmptyState, { title: t.empty, description: t.emptyDescription });
49
49
  else
50
50
  body = (_jsxs(_Fragment, { children: [view.timeline.map((item) => {
51
+ const after = renderAfter?.(item);
52
+ let node = null;
51
53
  if (item.kind === "message") {
52
54
  const message = byId.messages.get(item.id);
53
- return message ? (_jsx(MemoBubble, { message: message, renderContent: renderContent }, `m:${item.id}`)) : null;
55
+ node = message ? _jsx(MemoBubble, { message: message, renderContent: renderContent }) : null;
54
56
  }
55
- const call = byId.calls.get(item.id);
56
- if (!call)
57
- return null;
58
- const custom = renderToolCall?.(call);
59
- return custom !== undefined ? (_jsx(React.Fragment, { children: custom }, `t:${item.id}`)) : (_jsx(MemoToolCall, { call: call, title: toolTitle?.(call), onApprove: onApprove, onDeny: onDeny }, `t:${item.id}`));
60
- }), view.status === "error" && view.error && (_jsxs("div", { role: "alert", className: "flex min-w-0 flex-col gap-2 rounded-lg border border-danger-border bg-danger-bg px-3 py-2 text-sm text-danger-fg sm:flex-row sm:items-center", children: [_jsxs("p", { className: "min-w-0 flex-1 [overflow-wrap:anywhere]", children: [_jsx("span", { className: "font-medium", children: t.runFailed }), ": ", view.error.message] }), onRetry && (_jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: t.retry }))] }))] }));
57
+ else {
58
+ const call = byId.calls.get(item.id);
59
+ const custom = call ? renderToolCall?.(call) : undefined;
60
+ node = !call ? null : custom !== undefined ? (custom) : (_jsx(MemoToolCall, { call: call, title: toolTitle?.(call), onApprove: onApprove, onDeny: onDeny }));
61
+ }
62
+ return (_jsxs(React.Fragment, { children: [node, after] }, `${item.kind === "message" ? "m" : "t"}:${item.id}`));
63
+ }), footer, view.status === "error" && view.error && (_jsxs("div", { role: "alert", className: "flex min-w-0 flex-col gap-2 rounded-lg border border-danger-border bg-danger-bg px-3 py-2 text-sm text-danger-fg sm:flex-row sm:items-center", children: [_jsxs("p", { className: "min-w-0 flex-1 [overflow-wrap:anywhere]", children: [_jsx("span", { className: "font-medium", children: t.runFailed }), ": ", view.error.message] }), onRetry && (_jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: t.retry }))] }))] }));
61
64
  return (_jsxs("div", { "data-slot": "message-list", className: cn("relative flex min-h-0 flex-col", className), children: [_jsx("div", { ref: scroller, role: "log", "aria-label": label ?? t.conversation, "aria-busy": view.status === "running" || loading || undefined,
62
65
  // A scrolling region is reached by keyboard (WCAG 2.1.1).
63
66
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
@@ -64,7 +64,7 @@
64
64
  d.style.setProperty("--radius",
65
65
  (c.corners.indexOf(p.corners) > -1 ? p.corners : c.defaults.corners) + "px");
66
66
  };
67
- var products = {"astrolift":{"product":"astrolift","key":"astrolift.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"emerald":{"mode":"dark","background":"#050b08"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"graphite","light":"mist","accent":"green","density":"compact","corners":2}},"zentinelle":{"product":"zentinelle","key":"zentinelle.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["teal","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"mist","accent":"teal","density":"compact","corners":2}},"workbench":{"product":"workbench","key":"workbench.appearance","grounds":{"navy":{"mode":"dark","background":"#0b1120"},"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["workbench-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"navy","light":"mist","accent":"workbench-blue","density":"compact","corners":2}},"chat-studio":{"product":"chat-studio","key":"chat-studio.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["studio-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"paper","accent":"studio-blue","density":"compact","corners":2}},"loadr":{"product":"loadr","key":"loadr.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["loadr-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"charcoal","light":"paper","accent":"loadr-blue","density":"compact","corners":2}},"cykick":{"product":"cykick","key":"cykick.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["cykick-cyan","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"mist","accent":"cykick-cyan","density":"compact","corners":0}}};
67
+ var products = {"astrolift":{"product":"astrolift","key":"astrolift.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"emerald":{"mode":"dark","background":"#050b08"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"graphite","light":"mist","accent":"green","density":"compact","corners":2}},"zentinelle":{"product":"zentinelle","key":"zentinelle.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["teal","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"mist","accent":"teal","density":"compact","corners":2}},"workbench":{"product":"workbench","key":"workbench.appearance","grounds":{"navy":{"mode":"dark","background":"#0b1120"},"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["workbench-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"navy","light":"mist","accent":"workbench-blue","density":"compact","corners":2}},"chat-studio":{"product":"chat-studio","key":"chat-studio.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["studio-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"paper","accent":"studio-blue","density":"compact","corners":2}},"loadr":{"product":"loadr","key":"loadr.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["loadr-blue","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"charcoal","light":"paper","accent":"loadr-blue","density":"compact","corners":2}},"cykick":{"product":"cykick","key":"cykick.appearance","grounds":{"black":{"mode":"dark","background":"#000000"},"charcoal":{"mode":"dark","background":"#0e0f10"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["cykick-cyan","green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"black","light":"mist","accent":"cykick-cyan","density":"compact","corners":0}},"deep-data-agent":{"product":"deep-data-agent","key":"deep-data-agent.appearance","grounds":{"charcoal":{"mode":"dark","background":"#0e0f10"},"black":{"mode":"dark","background":"#000000"},"graphite":{"mode":"dark","background":"#111214"},"paper":{"mode":"light","background":"#faf6ee"},"mist":{"mode":"light","background":"#f2f7f9"}},"accents":["green","copper","ice","periwinkle","amber"],"corners":[0,2,4,10],"minContrast":3,"defaults":{"dark":"charcoal","light":"mist","accent":"amber","density":"compact","corners":2}}};
68
68
  var d = document.documentElement;
69
69
  var hex = /^#[0-9a-f]{6}$/;
70
70
  var has = function (o, k) {
@@ -490,6 +490,11 @@
490
490
  --brand-on-primary: #ffffff;
491
491
  }
492
492
 
493
+ /* Product: Deep Data Agent */
494
+ [data-product="deep-data-agent"] {
495
+ --brand-sidebar: #1a150c;
496
+ }
497
+
493
498
  /*
494
499
  * @calliopelabs/ui CSS layer: components.
495
500
  *
@@ -489,3 +489,8 @@
489
489
  --brand-primary: #0e7490;
490
490
  --brand-on-primary: #ffffff;
491
491
  }
492
+
493
+ /* Product: Deep Data Agent */
494
+ [data-product="deep-data-agent"] {
495
+ --brand-sidebar: #1a150c;
496
+ }
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { cn } from "./lib/cn.js";
8
8
  export { ACCENTS, ACCENT_KEYS, DENSITIES, DENSITY_KEYS, GROUNDS, GROUND_KEYS, MODE_TOKENS, SURFACES, type AccentSpec, type Density, type FamilyAccent, type Ground, type Mode, type StatusName, } from "./theme/tokens.js";
9
9
  export { MIN_NON_TEXT_CONTRAST, MIN_TEXT_CONTRAST, contrastRatio, luminance, } from "./theme/contrast.js";
10
10
  export { CORNERS, defineProduct, type BrandAccent, type Corners, type Product, type ProductAccent, type ProductConfig, type ProductTheme, } from "./appearance/product.js";
11
- export { astroliftBrand, chatStudioBrand, cykickBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "./appearance/brands.js";
11
+ export { astroliftBrand, chatStudioBrand, cykickBrand, deepDataAgentBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "./appearance/brands.js";
12
12
  export { accentContrast, appearanceFromServer, applyAppearance, clearAppearance, inkFor, isCustomAccent, modeFor, normalize, normalizePartial, parseCustomAccent, readAppearance, resolveAppearance, writeAppearance, type Appearance, type CustomAccent, type OrgAppearancePolicy, type ResolvedAppearance, } from "./appearance/appearance.js";
13
13
  export { appearanceScript, bootConfig, productStyles, type BootConfig } from "./appearance/boot.js";
14
14
  export { BRAND_ACCENT_KEY, brandStyleVars, brandStyles, htmlAttributes, resolveRuntimeBrand, withBrand, type ResolvedRuntimeBrand, type RuntimeBrand, } from "./appearance/runtime-brand.js";
@@ -62,6 +62,11 @@ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMe
62
62
  export { Popover, PopoverAnchor, PopoverContent, PopoverHeader, PopoverTrigger, } from "./primitives/Popover.js";
63
63
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/Tabs.js";
64
64
  export { Skeleton } from "./primitives/Skeleton.js";
65
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/Tooltip.js";
66
+ export { ContextMenu, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuTrigger, } from "./primitives/ContextMenu.js";
67
+ export { ScrollArea, type ScrollAreaProps } from "./primitives/ScrollArea.js";
68
+ export { Badge, badgeVariants, type BadgeProps } from "./primitives/Badge.js";
69
+ export { ToastProvider, useToast, type ToastApi, type ToastOptions, type ToastProviderProps, } from "./primitives/Toast.js";
65
70
  export type { WorkflowAction, WorkflowAvailability, WorkflowModule, WorkflowPermissions, WorkflowScope, WorkflowStatus, WorkflowSummary, } from "./workflow/model.js";
66
71
  export { WorkflowCard, WorkflowCardSkeleton, type WorkflowCardActions, type WorkflowCardProps, } from "./workflow/WorkflowCard.js";
67
72
  export { WorkflowQuestionCard, type WorkflowQuestionCardProps, } from "./workflow/WorkflowQuestionCard.js";
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { ACCENTS, ACCENT_KEYS, DENSITIES, DENSITY_KEYS, GROUNDS, GROUND_KEYS, MO
11
11
  export { MIN_NON_TEXT_CONTRAST, MIN_TEXT_CONTRAST, contrastRatio, luminance, } from "./theme/contrast.js";
12
12
  // Product and appearance
13
13
  export { CORNERS, defineProduct, } from "./appearance/product.js";
14
- export { astroliftBrand, chatStudioBrand, cykickBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "./appearance/brands.js";
14
+ export { astroliftBrand, chatStudioBrand, cykickBrand, deepDataAgentBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "./appearance/brands.js";
15
15
  export { accentContrast, appearanceFromServer, applyAppearance, clearAppearance, inkFor, isCustomAccent, modeFor, normalize, normalizePartial, parseCustomAccent, readAppearance, resolveAppearance, writeAppearance, } from "./appearance/appearance.js";
16
16
  export { appearanceScript, bootConfig, productStyles } from "./appearance/boot.js";
17
17
  export { BRAND_ACCENT_KEY, brandStyleVars, brandStyles, htmlAttributes, resolveRuntimeBrand, withBrand, } from "./appearance/runtime-brand.js";
@@ -73,6 +73,11 @@ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMe
73
73
  export { Popover, PopoverAnchor, PopoverContent, PopoverHeader, PopoverTrigger, } from "./primitives/Popover.js";
74
74
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/Tabs.js";
75
75
  export { Skeleton } from "./primitives/Skeleton.js";
76
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/Tooltip.js";
77
+ export { ContextMenu, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuTrigger, } from "./primitives/ContextMenu.js";
78
+ export { ScrollArea } from "./primitives/ScrollArea.js";
79
+ export { Badge, badgeVariants } from "./primitives/Badge.js";
80
+ export { ToastProvider, useToast, } from "./primitives/Toast.js";
76
81
  export { WorkflowCard, WorkflowCardSkeleton, } from "./workflow/WorkflowCard.js";
77
82
  export { WorkflowQuestionCard, } from "./workflow/WorkflowQuestionCard.js";
78
83
  export { ANY_FILTER, DEFAULT_WORKFLOW_FILTERS, filterWorkflows, isFiltered, } from "./workflow/gallery.js";
@@ -0,0 +1,14 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ import type * as React from "react";
3
+ export declare const badgeVariants: (props?: ({
4
+ variant?: "neutral" | "outline" | "primary" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ export interface BadgeProps extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {
7
+ }
8
+ /**
9
+ * A small count or label. Not a status: a state with a meaning (running,
10
+ * failed) is a StatusBadge, whose colours never follow the accent. A badge
11
+ * that is only a number needs words for a screen reader, so give it an
12
+ * `aria-label` ("3 unread") or put the noun beside it.
13
+ */
14
+ export declare function Badge({ className, variant, ...props }: BadgeProps): React.JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
+ import { cn } from "../lib/cn.js";
4
+ export const badgeVariants = cva("inline-flex h-5 w-fit min-w-5 shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border px-1.5 text-xs font-medium whitespace-nowrap tabular-nums [&>svg]:pointer-events-none [&>svg]:size-3", {
5
+ variants: {
6
+ variant: {
7
+ /** Quiet: a count beside a label, a kind ("folder", "beta"). */
8
+ neutral: "border-transparent bg-secondary text-secondary-foreground",
9
+ /** The accent: a new count that wants attention. */
10
+ primary: "border-transparent bg-primary text-primary-foreground",
11
+ /** A hairline only, for dense rows. */
12
+ outline: "border-border bg-transparent text-muted-foreground",
13
+ },
14
+ },
15
+ defaultVariants: { variant: "neutral" },
16
+ });
17
+ /**
18
+ * A small count or label. Not a status: a state with a meaning (running,
19
+ * failed) is a StatusBadge, whose colours never follow the accent. A badge
20
+ * that is only a number needs words for a screen reader, so give it an
21
+ * `aria-label` ("3 unread") or put the noun beside it.
22
+ */
23
+ export function Badge({ className, variant, ...props }) {
24
+ return (_jsx("span", { "data-slot": "badge", "data-variant": variant ?? "neutral", className: cn(badgeVariants({ variant }), className), ...props }));
25
+ }
@@ -0,0 +1,18 @@
1
+ import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
2
+ import type * as React from "react";
3
+ /**
4
+ * The right-click (and Shift+F10, and the context-menu key) menu. Every
5
+ * action in it must also be reachable another way: a toolbar button, a row
6
+ * menu or a shortcut, since touch and many keyboard users never open it.
7
+ */
8
+ export declare function ContextMenu(props: React.ComponentProps<typeof ContextMenuPrimitive.Root>): React.JSX.Element;
9
+ export declare function ContextMenuTrigger(props: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>): React.JSX.Element;
10
+ export declare function ContextMenuContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Content>): React.JSX.Element;
11
+ export declare function ContextMenuGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.Group>): React.JSX.Element;
12
+ export declare function ContextMenuItem({ className, variant, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
13
+ variant?: "default" | "destructive";
14
+ }): React.JSX.Element;
15
+ export declare function ContextMenuLabel({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Label>): React.JSX.Element;
16
+ export declare function ContextMenuSeparator({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>): React.JSX.Element;
17
+ /** The item's keyboard shortcut, right-aligned. Decorative: the shortcut itself is the app's. */
18
+ export declare function ContextMenuShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
4
+ import { cn } from "../lib/cn.js";
5
+ /**
6
+ * The right-click (and Shift+F10, and the context-menu key) menu. Every
7
+ * action in it must also be reachable another way: a toolbar button, a row
8
+ * menu or a shortcut, since touch and many keyboard users never open it.
9
+ */
10
+ export function ContextMenu(props) {
11
+ return _jsx(ContextMenuPrimitive.Root, { "data-slot": "context-menu", ...props });
12
+ }
13
+ export function ContextMenuTrigger(props) {
14
+ return _jsx(ContextMenuPrimitive.Trigger, { "data-slot": "context-menu-trigger", ...props });
15
+ }
16
+ export function ContextMenuContent({ className, ...props }) {
17
+ return (_jsx(ContextMenuPrimitive.Portal, { children: _jsx(ContextMenuPrimitive.Content, { "data-slot": "context-menu-content", className: cn("z-50 max-h-(--radix-context-menu-content-available-height) min-w-40 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 data-[state=open]:animate-cui-fade-in", className), ...props }) }));
18
+ }
19
+ export function ContextMenuGroup(props) {
20
+ return _jsx(ContextMenuPrimitive.Group, { "data-slot": "context-menu-group", ...props });
21
+ }
22
+ export function ContextMenuItem({ className, variant = "default", ...props }) {
23
+ return (_jsx(ContextMenuPrimitive.Item, { "data-slot": "context-menu-item", "data-variant": variant, className: cn("relative flex cursor-default items-center gap-1.5 rounded-lg px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-[variant=destructive]:text-danger-fg data-[variant=destructive]:focus:bg-danger/10 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props }));
24
+ }
25
+ export function ContextMenuLabel({ className, ...props }) {
26
+ return (_jsx(ContextMenuPrimitive.Label, { "data-slot": "context-menu-label", className: cn("px-1.5 py-1 text-xs font-medium text-muted-foreground", className), ...props }));
27
+ }
28
+ export function ContextMenuSeparator({ className, ...props }) {
29
+ return (_jsx(ContextMenuPrimitive.Separator, { "data-slot": "context-menu-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
30
+ }
31
+ /** The item's keyboard shortcut, right-aligned. Decorative: the shortcut itself is the app's. */
32
+ export function ContextMenuShortcut({ className, ...props }) {
33
+ return (_jsx("span", { "data-slot": "context-menu-shortcut", "aria-hidden": true, className: cn("ml-auto pl-4 font-mono text-xs text-muted-foreground", className), ...props }));
34
+ }
@@ -0,0 +1,16 @@
1
+ import { ScrollArea as ScrollAreaPrimitive } from "radix-ui";
2
+ import type * as React from "react";
3
+ export interface ScrollAreaProps extends React.ComponentProps<typeof ScrollAreaPrimitive.Root> {
4
+ /**
5
+ * Names the scrolling region. Given, the viewport is a focusable region so
6
+ * a keyboard can scroll it; leave it out when the content has its own
7
+ * focusable items (a list of links, a tree).
8
+ */
9
+ label?: string;
10
+ /** Which bars to draw. Defaults to vertical. */
11
+ orientation?: "vertical" | "horizontal" | "both";
12
+ viewportClassName?: string;
13
+ }
14
+ /** A scrolling box with thin, token-coloured bars that look the same on every platform. */
15
+ export declare function ScrollArea({ className, viewportClassName, children, label, orientation, ...props }: ScrollAreaProps): React.JSX.Element;
16
+ export declare function ScrollBar({ className, orientation, ...props }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>): React.JSX.Element;