@ciridae/ui-agent 0.3.0 → 0.3.5
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.
- package/README.md +7 -1
- package/dist/components/agentic/agent-card.d.ts +28 -0
- package/dist/components/agentic/agent-card.js +27 -0
- package/dist/components/agentic/agent-composer.d.ts +42 -0
- package/dist/components/agentic/agent-composer.js +89 -0
- package/dist/components/agentic/agent-context.d.ts +28 -0
- package/dist/components/agentic/agent-context.js +102 -0
- package/dist/components/agentic/agent-queue.js +1 -1
- package/dist/components/agentic/artifact.js +4 -4
- package/dist/components/agentic/assistant-mark-icon.d.ts +9 -0
- package/dist/components/agentic/assistant-mark-icon.js +12 -0
- package/dist/components/agentic/attachments-actions.d.ts +10 -0
- package/dist/components/agentic/attachments-actions.js +142 -0
- package/dist/components/agentic/attachments-context.d.ts +25 -0
- package/dist/components/agentic/attachments-context.js +196 -0
- package/dist/components/agentic/attachments-model.d.ts +23 -0
- package/dist/components/agentic/attachments-model.js +226 -0
- package/dist/components/agentic/attachments-preview.d.ts +7 -0
- package/dist/components/agentic/attachments-preview.js +25 -0
- package/dist/components/agentic/attachments-types.d.ts +87 -0
- package/dist/components/agentic/attachments-types.js +1 -0
- package/dist/components/agentic/attachments-ui.d.ts +14 -0
- package/dist/components/agentic/attachments-ui.js +90 -0
- package/dist/components/agentic/attachments.d.ts +5 -124
- package/dist/components/agentic/attachments.js +5 -591
- package/dist/components/agentic/audio-player.d.ts +16 -0
- package/dist/components/agentic/audio-player.js +21 -0
- package/dist/components/agentic/breathing-grid-icon.css +45 -0
- package/dist/components/agentic/breathing-grid-icon.css.d.ts +2 -0
- package/dist/components/agentic/breathing-grid-icon.d.ts +6 -0
- package/dist/components/agentic/breathing-grid-icon.js +7 -0
- package/dist/components/agentic/canvas.d.ts +10 -0
- package/dist/components/agentic/canvas.js +22 -0
- package/dist/components/agentic/code-block.js +4 -4
- package/dist/components/agentic/commit.d.ts +57 -0
- package/dist/components/agentic/commit.js +85 -0
- package/dist/components/agentic/connection.d.ts +3 -0
- package/dist/components/agentic/connection.js +8 -0
- package/dist/components/agentic/controls.d.ts +6 -0
- package/dist/components/agentic/controls.js +7 -0
- package/dist/components/agentic/edge.d.ts +8 -0
- package/dist/components/agentic/edge.js +65 -0
- package/dist/components/agentic/effort-selector.d.ts +43 -0
- package/dist/components/agentic/effort-selector.js +47 -0
- package/dist/components/agentic/environment-variables.d.ts +32 -0
- package/dist/components/agentic/environment-variables.js +66 -0
- package/dist/components/agentic/jsx-preview.d.ts +19 -0
- package/dist/components/agentic/jsx-preview.js +34 -0
- package/dist/components/agentic/message.d.ts +1 -1
- package/dist/components/agentic/message.js +56 -16
- package/dist/components/agentic/mic-selector.d.ts +58 -0
- package/dist/components/agentic/mic-selector.js +173 -0
- package/dist/components/agentic/node.d.ts +26 -0
- package/dist/components/agentic/node.js +29 -0
- package/dist/components/agentic/open-in-chat.d.ts +9 -0
- package/dist/components/agentic/open-in-chat.js +8 -0
- package/dist/components/agentic/package-info.d.ts +30 -0
- package/dist/components/agentic/package-info.js +44 -0
- package/dist/components/agentic/panel.d.ts +6 -0
- package/dist/components/agentic/panel.js +7 -0
- package/dist/components/agentic/persona.d.ts +16 -0
- package/dist/components/agentic/persona.js +116 -0
- package/dist/components/agentic/prompt-input.js +7 -6
- package/dist/components/agentic/sources.js +9 -9
- package/dist/components/agentic/speech-input.d.ts +24 -0
- package/dist/components/agentic/speech-input.js +30 -0
- package/dist/components/agentic/stack-trace.d.ts +27 -0
- package/dist/components/agentic/stack-trace.js +76 -0
- package/dist/components/agentic/suggestion.d.ts +12 -0
- package/dist/components/agentic/suggestion.js +15 -0
- package/dist/components/agentic/test-results.d.ts +51 -0
- package/dist/components/agentic/test-results.js +83 -0
- package/dist/components/agentic/theme.d.ts +5 -0
- package/dist/components/agentic/theme.js +31 -0
- package/dist/components/agentic/toolbar.d.ts +6 -0
- package/dist/components/agentic/toolbar.js +7 -0
- package/dist/components/agentic/transcription.d.ts +20 -0
- package/dist/components/agentic/transcription.js +32 -0
- package/dist/components/agentic/use-controllable-state.d.ts +6 -0
- package/dist/components/agentic/use-controllable-state.js +13 -0
- package/dist/components/agentic/voice-selector.d.ts +69 -0
- package/dist/components/agentic/voice-selector.js +122 -0
- package/dist/components/blocks/artifact-workbench.d.ts +9 -0
- package/dist/components/blocks/artifact-workbench.js +28 -0
- package/dist/components/blocks/tool-approval-panel.d.ts +11 -0
- package/dist/components/blocks/tool-approval-panel.js +11 -0
- package/dist/components/blocks/voice-agent-panel.d.ts +14 -0
- package/dist/components/blocks/voice-agent-panel.js +60 -0
- package/dist/components/blocks/workflow-canvas-panel.d.ts +18 -0
- package/dist/components/blocks/workflow-canvas-panel.js +106 -0
- package/dist/styles.css +5 -0
- package/package.json +176 -10
package/README.md
CHANGED
|
@@ -16,4 +16,10 @@ Install with the base UI package:
|
|
|
16
16
|
npm install @ciridae/ui @ciridae/ui-agent
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Import
|
|
19
|
+
Import Tailwind and both package stylesheets from your global CSS entry:
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
@import "tailwindcss";
|
|
23
|
+
@import "@ciridae/ui/styles.css";
|
|
24
|
+
@import "@ciridae/ui-agent/styles.css";
|
|
25
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import { Accordion, AccordionItem } from "@ciridae/ui/accordion";
|
|
3
|
+
type AgentProps = React.ComponentProps<"div">;
|
|
4
|
+
type AgentHeaderProps = React.ComponentProps<"div"> & {
|
|
5
|
+
status?: string;
|
|
6
|
+
};
|
|
7
|
+
type AgentContentProps = React.ComponentProps<"div">;
|
|
8
|
+
type AgentInstructionsProps = React.ComponentProps<"div">;
|
|
9
|
+
type AgentToolsProps = Omit<React.ComponentProps<typeof Accordion>, "collapsible" | "defaultValue" | "onValueChange" | "type" | "value"> & {
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
onValueChange?: (value: string) => void;
|
|
12
|
+
value?: string;
|
|
13
|
+
};
|
|
14
|
+
type AgentToolProps = Omit<React.ComponentProps<typeof AccordionItem>, "value"> & {
|
|
15
|
+
description?: string;
|
|
16
|
+
name: string;
|
|
17
|
+
value?: string;
|
|
18
|
+
};
|
|
19
|
+
type AgentOutputProps = React.ComponentProps<"div">;
|
|
20
|
+
declare function Agent({ className, ...props }: AgentProps): React.JSX.Element;
|
|
21
|
+
declare function AgentHeader({ children, className, status, ...props }: AgentHeaderProps): React.JSX.Element;
|
|
22
|
+
declare function AgentContent({ className, ...props }: AgentContentProps): React.JSX.Element;
|
|
23
|
+
declare function AgentInstructions({ className, ...props }: AgentInstructionsProps): React.JSX.Element;
|
|
24
|
+
declare function AgentTools(props: AgentToolsProps): React.JSX.Element;
|
|
25
|
+
declare function AgentTool({ children, description, name, value, ...props }: AgentToolProps): React.JSX.Element;
|
|
26
|
+
declare function AgentOutput({ className, ...props }: AgentOutputProps): React.JSX.Element;
|
|
27
|
+
export { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools };
|
|
28
|
+
export type { AgentContentProps, AgentHeaderProps, AgentInstructionsProps, AgentOutputProps, AgentProps, AgentToolProps, AgentToolsProps, };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BotIcon } from "lucide-react";
|
|
3
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@ciridae/ui/accordion";
|
|
4
|
+
import { Badge } from "@ciridae/ui/badge";
|
|
5
|
+
import { cn } from "@ciridae/ui/utils";
|
|
6
|
+
function Agent({ className, ...props }) {
|
|
7
|
+
return (_jsx("div", { "data-slot": "agent", className: cn("grid gap-3 rounded-lg border bg-card p-4", className), ...props }));
|
|
8
|
+
}
|
|
9
|
+
function AgentHeader({ children, className, status, ...props }) {
|
|
10
|
+
return (_jsxs("div", { "data-slot": "agent-header", className: cn("flex min-w-0 items-start justify-between gap-3", className), ...props, children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(BotIcon, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground" }), _jsx("div", { className: "min-w-0", children: children })] }), status ? _jsx(Badge, { variant: "outline", children: status }) : null] }));
|
|
11
|
+
}
|
|
12
|
+
function AgentContent({ className, ...props }) {
|
|
13
|
+
return _jsx("div", { "data-slot": "agent-content", className: cn("grid gap-3", className), ...props });
|
|
14
|
+
}
|
|
15
|
+
function AgentInstructions({ className, ...props }) {
|
|
16
|
+
return (_jsx("div", { "data-slot": "agent-instructions", className: cn("rounded-md bg-muted/40 p-3 text-sm text-muted-foreground", className), ...props }));
|
|
17
|
+
}
|
|
18
|
+
function AgentTools(props) {
|
|
19
|
+
return _jsx(Accordion, { "data-slot": "agent-tools", type: "single", collapsible: true, ...props });
|
|
20
|
+
}
|
|
21
|
+
function AgentTool({ children, description, name, value, ...props }) {
|
|
22
|
+
return (_jsxs(AccordionItem, { "data-slot": "agent-tool", value: value ?? name, ...props, children: [_jsx(AccordionTrigger, { children: name }), _jsxs(AccordionContent, { children: [description ? _jsx("p", { className: "mb-2 text-sm text-muted-foreground", children: description }) : null, children] })] }));
|
|
23
|
+
}
|
|
24
|
+
function AgentOutput({ className, ...props }) {
|
|
25
|
+
return (_jsx("div", { "data-slot": "agent-output", className: cn("rounded-md border bg-muted/30 p-3 text-sm", className), ...props }));
|
|
26
|
+
}
|
|
27
|
+
export { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type AgenticAttachment, type AttachmentError } from "@ciridae/ui-agent/attachments";
|
|
3
|
+
import { type EffortSelectorValue } from "@ciridae/ui-agent/effort-selector";
|
|
4
|
+
import { type PromptInputMessage } from "@ciridae/ui-agent/prompt-input";
|
|
5
|
+
import type { AgenticChatStatus } from "@ciridae/ui-agent/types";
|
|
6
|
+
type AgentComposerModel = {
|
|
7
|
+
description?: string;
|
|
8
|
+
group?: string;
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
shortcut?: string;
|
|
13
|
+
src?: string;
|
|
14
|
+
};
|
|
15
|
+
type AgentComposerProps = Omit<React.ComponentProps<"div">, "onSubmit"> & {
|
|
16
|
+
accept?: string;
|
|
17
|
+
attachments?: AgenticAttachment[];
|
|
18
|
+
defaultAttachments?: AgenticAttachment[];
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
effort: EffortSelectorValue;
|
|
22
|
+
focusHint?: React.ReactNode;
|
|
23
|
+
globalDrop?: boolean;
|
|
24
|
+
maxAttachmentSizeMb?: number;
|
|
25
|
+
maxAttachments?: number;
|
|
26
|
+
model: string;
|
|
27
|
+
models: AgentComposerModel[];
|
|
28
|
+
multiple?: boolean;
|
|
29
|
+
onAttachmentError?: (error: AttachmentError) => void;
|
|
30
|
+
onAttachmentsChange?: (attachments: AgenticAttachment[]) => void;
|
|
31
|
+
onEffortChange: (value: EffortSelectorValue) => void;
|
|
32
|
+
onModelChange: (modelId: string) => void;
|
|
33
|
+
onStop?: () => void;
|
|
34
|
+
onSubmit?: (message: PromptInputMessage) => void | Promise<void>;
|
|
35
|
+
onValueChange?: (value: string) => void;
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
status?: AgenticChatStatus;
|
|
38
|
+
value?: string;
|
|
39
|
+
};
|
|
40
|
+
declare function AgentComposer({ accept, attachments, className, defaultAttachments, defaultValue, disabled, effort, focusHint, globalDrop, maxAttachmentSizeMb, maxAttachments, model, models, multiple, onAttachmentError, onAttachmentsChange, onEffortChange, onModelChange, onStop, onSubmit, onValueChange, placeholder, status, value, ...props }: AgentComposerProps): React.JSX.Element;
|
|
41
|
+
export { AgentComposer };
|
|
42
|
+
export type { AgentComposerModel, AgentComposerProps };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable max-lines, max-lines-per-function, react/jsx-max-depth */
|
|
3
|
+
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { AttachmentList, useAttachments, } from "@ciridae/ui-agent/attachments";
|
|
6
|
+
import { EffortSelector } from "@ciridae/ui-agent/effort-selector";
|
|
7
|
+
import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorName, ModelSelectorShortcut, ModelSelectorTrigger, } from "@ciridae/ui-agent/model-selector";
|
|
8
|
+
import { PromptInput, PromptInputActionAddAttachments, PromptInputBody, PromptInputProvider, PromptInputSubmit, PromptInputTextarea, PromptInputTools, } from "@ciridae/ui-agent/prompt-input";
|
|
9
|
+
import { Button } from "@ciridae/ui/button";
|
|
10
|
+
import { cn } from "@ciridae/ui/utils";
|
|
11
|
+
function AgentComposer({ accept, attachments, className, defaultAttachments, defaultValue, disabled, effort, focusHint, globalDrop, maxAttachmentSizeMb = 25, maxAttachments = 4, model, models, multiple = true, onAttachmentError, onAttachmentsChange, onEffortChange, onModelChange, onStop, onSubmit, onValueChange, placeholder = "Ask anything", status = "idle", value, ...props }) {
|
|
12
|
+
const containerRef = React.useRef(null);
|
|
13
|
+
const [localValue, setLocalValue] = React.useState(defaultValue ?? "");
|
|
14
|
+
const resolvedValue = value ?? localValue;
|
|
15
|
+
const dropHandlers = useAgentComposerDropHandlers();
|
|
16
|
+
const selectedModel = getSelectedModel(models, model);
|
|
17
|
+
const hasInput = resolvedValue.length > 0;
|
|
18
|
+
const isRunning = status === "submitted" || status === "streaming";
|
|
19
|
+
useComposerFocusShortcut(containerRef);
|
|
20
|
+
return (_jsx("div", { ref: containerRef, className: cn("w-full", className), ...props, children: _jsx(PromptInputProvider, { accept: accept, attachments: attachments, defaultAttachments: defaultAttachments, maxAttachmentSizeMb: maxAttachmentSizeMb, maxAttachments: maxAttachments, multiple: multiple, onAttachmentError: onAttachmentError, onAttachmentsChange: onAttachmentsChange, onValueChange: (nextValue) => {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
setLocalValue(nextValue);
|
|
23
|
+
onValueChange?.(nextValue);
|
|
24
|
+
}, value: resolvedValue, children: _jsx(PromptInput, { disabled: disabled, globalDrop: globalDrop, maxAttachments: maxAttachments, onSubmit: onSubmit, status: status, ...dropHandlers, children: _jsxs(PromptInputBody, { className: "min-h-0!", children: [_jsx(AgentComposerPromptArea, { focusHint: focusHint, hasInput: hasInput, placeholder: placeholder }), _jsxs(PromptInputTools, { children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [_jsx(AgentComposerModelSelector, { disabled: disabled || isRunning, model: selectedModel, models: models, onModelChange: onModelChange }), _jsx(EffortSelector, { disabled: disabled || isRunning, value: effort, onValueChange: onEffortChange })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(PromptInputActionAddAttachments, { disabled: disabled || isRunning, tooltip: "Add files" }), _jsx(PromptInputSubmit, { disabled: disabled, onStop: onStop, status: status })] })] })] }) }) }) }));
|
|
25
|
+
}
|
|
26
|
+
function AgentComposerPromptArea({ focusHint, hasInput, placeholder, }) {
|
|
27
|
+
const attachments = useAttachments();
|
|
28
|
+
const hasAttachments = attachments.files.length > 0;
|
|
29
|
+
const isExpanded = hasAttachments || hasInput;
|
|
30
|
+
const showFocusHint = !hasAttachments && !hasInput;
|
|
31
|
+
return (_jsxs("div", { "data-expanded": isExpanded ? "true" : undefined, className: cn("grid min-w-0 content-start px-3", isExpanded ? "gap-2 py-2.5" : "py-1.5"), children: [hasAttachments ? (_jsx(AttachmentList, { className: cn("w-full max-w-full flex-nowrap! overflow-x-auto pb-0.5", "[&_[data-slot=attachment]]:h-[1.375rem] [&_[data-slot=attachment]]:max-w-full [&_[data-slot=attachment]]:gap-1 [&_[data-slot=attachment]]:rounded-[0.25rem] [&_[data-slot=attachment]]:px-1 [&_[data-slot=attachment]]:pr-1.5", "[&_[data-slot=attachment-info]]:gap-0 [&_[data-slot=attachment-info]>span]:text-[0.75rem] [&_[data-slot=attachment-info]>span]:leading-none", "[&_[data-slot=attachment-preview]]:size-4 [&_[data-slot=attachment-status]]:size-3.5") })) : null, _jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [_jsx(PromptInputTextarea, { placeholder: placeholder, rows: isExpanded ? 2 : 1, className: cn("field-sizing-content! !w-full !px-0 !py-0 text-base leading-6 md:text-sm", isExpanded ? "max-h-36 !min-h-12" : "max-h-6 !min-h-6") }), showFocusHint ? (_jsx("div", { "aria-hidden": "true", "data-slot": "agent-composer-focus-hint", className: "pointer-events-none flex shrink-0 items-center gap-1 px-1.5 py-0.5 text-xs text-muted-foreground/70 transition-opacity group-focus-within/prompt-input:opacity-0", children: focusHint ?? (_jsxs(_Fragment, { children: [_jsx("kbd", { className: "font-sans", children: "\u2318L" }), _jsx("span", { children: "to focus" })] })) })) : null] })] }));
|
|
32
|
+
}
|
|
33
|
+
function AgentComposerModelSelector({ disabled, model, models, onModelChange, }) {
|
|
34
|
+
return (_jsxs(ModelSelector, { children: [_jsx(ModelSelectorTrigger, { asChild: true, children: _jsxs(Button, { className: "h-7 max-w-44 justify-start gap-1.5 rounded-md px-1.5 text-xs", disabled: disabled, type: "button", variant: "ghost", children: [_jsx(ModelSelectorLogo, { provider: model.provider, src: model.src }), _jsx(ModelSelectorName, { className: "max-w-32 flex-none", children: model.label }), _jsx(ChevronsUpDownIcon, { className: "size-3.5 shrink-0 text-muted-foreground" })] }) }), _jsxs(ModelSelectorContent, { title: "Select model", children: [_jsx(ModelSelectorInput, { placeholder: "Search models..." }), _jsxs(ModelSelectorList, { children: [_jsx(ModelSelectorEmpty, { children: "No models found." }), groupModels(models).map(([group, groupModels]) => (_jsx(ModelSelectorGroup, { heading: group, children: groupModels.map((option) => (_jsxs(ModelSelectorItem, { value: `${option.label} ${option.id} ${option.provider}`, onSelect: () => onModelChange(option.id), children: [_jsx(ModelSelectorLogo, { className: "size-8 rounded-md p-1", provider: option.provider, src: option.src }), _jsxs(ModelSelectorName, { children: [_jsx("span", { className: "block truncate", children: option.label }), option.description ? (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: option.description })) : null] }), option.shortcut ? (_jsx(ModelSelectorShortcut, { children: option.shortcut })) : null, model.id === option.id ? _jsx(CheckIcon, {}) : null] }, option.id))) }, group)))] })] })] }));
|
|
35
|
+
}
|
|
36
|
+
function useComposerFocusShortcut(containerRef) {
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
const handleKeyDown = (event) => {
|
|
39
|
+
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "l")
|
|
40
|
+
return;
|
|
41
|
+
const textarea = containerRef.current?.querySelector('[data-slot="prompt-input-textarea"]');
|
|
42
|
+
if (!textarea)
|
|
43
|
+
return;
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
textarea.focus();
|
|
46
|
+
};
|
|
47
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
48
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
49
|
+
}, [containerRef]);
|
|
50
|
+
}
|
|
51
|
+
function useAgentComposerDropHandlers() {
|
|
52
|
+
const [dragDepth, setDragDepth] = React.useState(0);
|
|
53
|
+
const handleDragEnter = React.useCallback((event) => {
|
|
54
|
+
if (!event.dataTransfer.types.includes("Files"))
|
|
55
|
+
return;
|
|
56
|
+
setDragDepth((depth) => depth + 1);
|
|
57
|
+
}, []);
|
|
58
|
+
const handleDragLeave = React.useCallback((event) => {
|
|
59
|
+
if (!event.dataTransfer.types.includes("Files"))
|
|
60
|
+
return;
|
|
61
|
+
setDragDepth((depth) => Math.max(0, depth - 1));
|
|
62
|
+
}, []);
|
|
63
|
+
const handleDrop = React.useCallback(() => setDragDepth(0), []);
|
|
64
|
+
const handleDragEnd = React.useCallback(() => setDragDepth(0), []);
|
|
65
|
+
return React.useMemo(() => ({
|
|
66
|
+
"data-drag-over": dragDepth > 0 ? "true" : undefined,
|
|
67
|
+
onDragEnd: handleDragEnd,
|
|
68
|
+
onDragEnter: handleDragEnter,
|
|
69
|
+
onDragLeave: handleDragLeave,
|
|
70
|
+
onDrop: handleDrop,
|
|
71
|
+
}), [dragDepth, handleDragEnd, handleDragEnter, handleDragLeave, handleDrop]);
|
|
72
|
+
}
|
|
73
|
+
function getSelectedModel(models, modelId) {
|
|
74
|
+
return models.find((model) => model.id === modelId) ?? models[0] ?? fallbackModel;
|
|
75
|
+
}
|
|
76
|
+
function groupModels(models) {
|
|
77
|
+
const groups = new Map();
|
|
78
|
+
for (const model of models) {
|
|
79
|
+
const group = model.group ?? "Models";
|
|
80
|
+
groups.set(group, [...(groups.get(group) ?? []), model]);
|
|
81
|
+
}
|
|
82
|
+
return Array.from(groups.entries());
|
|
83
|
+
}
|
|
84
|
+
const fallbackModel = {
|
|
85
|
+
id: "model",
|
|
86
|
+
label: "Model",
|
|
87
|
+
provider: "openai",
|
|
88
|
+
};
|
|
89
|
+
export { AgentComposer };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { AgenticContextUsage } from "@ciridae/ui-agent/types";
|
|
3
|
+
import { Button } from "@ciridae/ui/button";
|
|
4
|
+
import { HoverCard, HoverCardContent } from "@ciridae/ui/hover-card";
|
|
5
|
+
type AgentContextProps = React.ComponentProps<typeof HoverCard> & AgenticContextUsage;
|
|
6
|
+
type AgentContextTriggerProps = React.ComponentProps<typeof Button>;
|
|
7
|
+
type AgentContextContentProps = React.ComponentProps<typeof HoverCardContent>;
|
|
8
|
+
type AgentContextContentHeaderProps = React.ComponentProps<"div">;
|
|
9
|
+
type AgentContextContentBodyProps = React.ComponentProps<"div">;
|
|
10
|
+
type AgentContextContentFooterProps = React.ComponentProps<"div">;
|
|
11
|
+
type AgentContextUsageRowProps = React.ComponentProps<"div"> & {
|
|
12
|
+
costText?: string;
|
|
13
|
+
label: string;
|
|
14
|
+
tokens?: number;
|
|
15
|
+
};
|
|
16
|
+
declare function AgentContext({ cacheCostText, cachedInputTokens, children, inputCostText, inputTokens, maxTokens, outputCostText, outputTokens, reasoningCostText, reasoningTokens, totalCostText, usedTokens, ...props }: AgentContextProps): React.JSX.Element;
|
|
17
|
+
declare function AgentContextTrigger({ children, ...props }: AgentContextTriggerProps): React.JSX.Element;
|
|
18
|
+
declare function AgentContextContent({ className, ...props }: AgentContextContentProps): React.JSX.Element;
|
|
19
|
+
declare function AgentContextContentHeader({ children, className, ...props }: AgentContextContentHeaderProps): React.JSX.Element;
|
|
20
|
+
declare function AgentContextContentBody({ className, ...props }: AgentContextContentBodyProps): React.JSX.Element;
|
|
21
|
+
declare function AgentContextContentFooter({ children, className, ...props }: AgentContextContentFooterProps): React.JSX.Element;
|
|
22
|
+
declare function AgentContextUsageRow({ className, costText, label, tokens, ...props }: AgentContextUsageRowProps): React.JSX.Element | null;
|
|
23
|
+
declare function AgentContextInputUsage(props: Omit<AgentContextUsageRowProps, "label" | "tokens">): React.JSX.Element;
|
|
24
|
+
declare function AgentContextOutputUsage(props: Omit<AgentContextUsageRowProps, "label" | "tokens">): React.JSX.Element;
|
|
25
|
+
declare function AgentContextReasoningUsage(props: Omit<AgentContextUsageRowProps, "label" | "tokens">): React.JSX.Element;
|
|
26
|
+
declare function AgentContextCacheUsage(props: Omit<AgentContextUsageRowProps, "label" | "tokens">): React.JSX.Element;
|
|
27
|
+
export { AgentContext, AgentContextCacheUsage, AgentContextContent, AgentContextContentBody, AgentContextContentFooter, AgentContextContentHeader, AgentContextInputUsage, AgentContextOutputUsage, AgentContextReasoningUsage, AgentContextTrigger, AgentContextUsageRow, };
|
|
28
|
+
export type { AgentContextContentBodyProps, AgentContextContentFooterProps, AgentContextContentHeaderProps, AgentContextContentProps, AgentContextProps, AgentContextTriggerProps, AgentContextUsageRowProps, };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import { Button } from "@ciridae/ui/button";
|
|
4
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ciridae/ui/hover-card";
|
|
5
|
+
import { Progress } from "@ciridae/ui/progress";
|
|
6
|
+
import { cn } from "@ciridae/ui/utils";
|
|
7
|
+
const PERCENT_MAX = 100;
|
|
8
|
+
const AgentContextContext = createContext(null);
|
|
9
|
+
function useAgentContext() {
|
|
10
|
+
const context = useContext(AgentContextContext);
|
|
11
|
+
if (!context)
|
|
12
|
+
throw new Error("AgentContext components must be used within AgentContext");
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
function AgentContext({ cacheCostText, cachedInputTokens, children, inputCostText, inputTokens, maxTokens, outputCostText, outputTokens, reasoningCostText, reasoningTokens, totalCostText, usedTokens, ...props }) {
|
|
16
|
+
const { closeDelay = 0, openDelay = 0, ...hoverCardProps } = props;
|
|
17
|
+
const contextValue = useMemo(() => ({
|
|
18
|
+
cacheCostText,
|
|
19
|
+
cachedInputTokens,
|
|
20
|
+
inputCostText,
|
|
21
|
+
inputTokens,
|
|
22
|
+
maxTokens,
|
|
23
|
+
outputCostText,
|
|
24
|
+
outputTokens,
|
|
25
|
+
reasoningCostText,
|
|
26
|
+
reasoningTokens,
|
|
27
|
+
totalCostText,
|
|
28
|
+
usedTokens,
|
|
29
|
+
}), [
|
|
30
|
+
cacheCostText,
|
|
31
|
+
cachedInputTokens,
|
|
32
|
+
inputCostText,
|
|
33
|
+
inputTokens,
|
|
34
|
+
maxTokens,
|
|
35
|
+
outputCostText,
|
|
36
|
+
outputTokens,
|
|
37
|
+
reasoningCostText,
|
|
38
|
+
reasoningTokens,
|
|
39
|
+
totalCostText,
|
|
40
|
+
usedTokens,
|
|
41
|
+
]);
|
|
42
|
+
return (_jsx(AgentContextContext.Provider, { value: contextValue, children: _jsx(HoverCard, { closeDelay: closeDelay, openDelay: openDelay, ...hoverCardProps, children: children }) }));
|
|
43
|
+
}
|
|
44
|
+
function AgentContextTrigger({ children, ...props }) {
|
|
45
|
+
const { maxTokens, usedTokens } = useAgentContext();
|
|
46
|
+
const usedPercent = getUsagePercent(usedTokens, maxTokens);
|
|
47
|
+
return (_jsx(HoverCardTrigger, { asChild: true, children: children ? (_jsx(Button, { "data-slot": "agent-context-trigger", type: "button", variant: "ghost", ...props, asChild: true, children: children })) : (_jsxs(Button, { "data-slot": "agent-context-trigger", type: "button", variant: "ghost", ...props, children: [_jsx("span", { className: "font-medium text-muted-foreground", children: formatPercent(usedPercent) }), _jsx(AgentContextIcon, { usedPercent: usedPercent })] })) }));
|
|
48
|
+
}
|
|
49
|
+
function AgentContextIcon({ usedPercent }) {
|
|
50
|
+
const radius = 10;
|
|
51
|
+
const circumference = 2 * Math.PI * radius;
|
|
52
|
+
const dashOffset = circumference * (1 - usedPercent);
|
|
53
|
+
return (_jsxs("svg", { "aria-label": "Context usage", className: "size-5", role: "img", viewBox: "0 0 24 24", children: [_jsx("circle", { cx: "12", cy: "12", fill: "none", opacity: "0.25", r: radius, stroke: "currentColor", strokeWidth: "2" }), _jsx("circle", { cx: "12", cy: "12", fill: "none", opacity: "0.75", r: radius, stroke: "currentColor", strokeDasharray: `${circumference} ${circumference}`, strokeDashoffset: dashOffset, strokeLinecap: "round", strokeWidth: "2", style: { transform: "rotate(-90deg)", transformOrigin: "center" } })] }));
|
|
54
|
+
}
|
|
55
|
+
function AgentContextContent({ className, ...props }) {
|
|
56
|
+
return (_jsx(HoverCardContent, { "data-slot": "agent-context-content", className: cn("w-72 divide-y overflow-hidden p-0", className), ...props }));
|
|
57
|
+
}
|
|
58
|
+
function AgentContextContentHeader({ children, className, ...props }) {
|
|
59
|
+
const { maxTokens, usedTokens } = useAgentContext();
|
|
60
|
+
const usedPercent = getUsagePercent(usedTokens, maxTokens);
|
|
61
|
+
return (_jsx("div", { "data-slot": "agent-context-content-header", className: cn("grid gap-2 p-3", className), ...props, children: children ?? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [_jsx("span", { children: formatPercent(usedPercent) }), _jsxs("span", { className: "font-mono text-muted-foreground", children: [formatCompact(usedTokens), " / ", formatCompact(maxTokens)] })] }), _jsx(Progress, { value: usedPercent * PERCENT_MAX })] })) }));
|
|
62
|
+
}
|
|
63
|
+
function AgentContextContentBody({ className, ...props }) {
|
|
64
|
+
return (_jsx("div", { "data-slot": "agent-context-content-body", className: cn("grid gap-2 p-3", className), ...props }));
|
|
65
|
+
}
|
|
66
|
+
function AgentContextContentFooter({ children, className, ...props }) {
|
|
67
|
+
const { totalCostText } = useAgentContext();
|
|
68
|
+
return (_jsx("div", { "data-slot": "agent-context-content-footer", className: cn("flex items-center justify-between gap-3 bg-secondary p-3 text-xs", className), ...props, children: children ?? (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground", children: "Total cost" }), _jsx("span", { children: totalCostText ?? "Provided by host" })] })) }));
|
|
69
|
+
}
|
|
70
|
+
function AgentContextUsageRow({ className, costText, label, tokens, ...props }) {
|
|
71
|
+
if (tokens === undefined)
|
|
72
|
+
return null;
|
|
73
|
+
return (_jsxs("div", { "data-slot": "agent-context-usage-row", className: cn("flex items-center justify-between gap-3 text-xs", className), ...props, children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsxs("span", { className: "font-mono", children: [formatCompact(tokens), costText ? _jsx("span", { className: "ml-2 font-sans text-muted-foreground", children: costText }) : null] })] }));
|
|
74
|
+
}
|
|
75
|
+
function AgentContextInputUsage(props) {
|
|
76
|
+
const { inputCostText, inputTokens } = useAgentContext();
|
|
77
|
+
return (_jsx(AgentContextUsageRow, { costText: inputCostText, label: "Input", tokens: inputTokens, ...props }));
|
|
78
|
+
}
|
|
79
|
+
function AgentContextOutputUsage(props) {
|
|
80
|
+
const { outputCostText, outputTokens } = useAgentContext();
|
|
81
|
+
return (_jsx(AgentContextUsageRow, { costText: outputCostText, label: "Output", tokens: outputTokens, ...props }));
|
|
82
|
+
}
|
|
83
|
+
function AgentContextReasoningUsage(props) {
|
|
84
|
+
const { reasoningCostText, reasoningTokens } = useAgentContext();
|
|
85
|
+
return (_jsx(AgentContextUsageRow, { costText: reasoningCostText, label: "Reasoning", tokens: reasoningTokens, ...props }));
|
|
86
|
+
}
|
|
87
|
+
function AgentContextCacheUsage(props) {
|
|
88
|
+
const { cacheCostText, cachedInputTokens } = useAgentContext();
|
|
89
|
+
return (_jsx(AgentContextUsageRow, { costText: cacheCostText, label: "Cache", tokens: cachedInputTokens, ...props }));
|
|
90
|
+
}
|
|
91
|
+
function getUsagePercent(usedTokens, maxTokens) {
|
|
92
|
+
if (maxTokens <= 0)
|
|
93
|
+
return 0;
|
|
94
|
+
return Math.min(1, Math.max(0, usedTokens / maxTokens));
|
|
95
|
+
}
|
|
96
|
+
function formatPercent(value) {
|
|
97
|
+
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 1, style: "percent" }).format(value);
|
|
98
|
+
}
|
|
99
|
+
function formatCompact(value) {
|
|
100
|
+
return new Intl.NumberFormat("en-US", { notation: "compact" }).format(value);
|
|
101
|
+
}
|
|
102
|
+
export { AgentContext, AgentContextCacheUsage, AgentContextContent, AgentContextContentBody, AgentContextContentFooter, AgentContextContentHeader, AgentContextInputUsage, AgentContextOutputUsage, AgentContextReasoningUsage, AgentContextTrigger, AgentContextUsageRow, };
|
|
@@ -39,7 +39,7 @@ function AgentQueueItemActions({ className, ...props }) {
|
|
|
39
39
|
return (_jsx("div", { "data-slot": "agent-queue-item-actions", className: cn("flex gap-1", className), ...props }));
|
|
40
40
|
}
|
|
41
41
|
function AgentQueueItemAction({ className, type = "button", ...props }) {
|
|
42
|
-
return (_jsx(Button, { className: cn("size-7 opacity-0 group-
|
|
42
|
+
return (_jsx(Button, { className: cn("size-7 opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 focus-visible:opacity-100", className), size: "icon-sm", type: type, variant: "ghost", ...props }));
|
|
43
43
|
}
|
|
44
44
|
function AgentQueueItemAttachment({ className, ...props }) {
|
|
45
45
|
return (_jsx("div", { "data-slot": "agent-queue-item-attachment", className: cn("ml-6 flex flex-wrap gap-2", className), ...props }));
|
|
@@ -25,11 +25,11 @@ const artifactHeaderClassName = css({
|
|
|
25
25
|
borderBottomStyle: "solid",
|
|
26
26
|
borderBottomWidth: "default",
|
|
27
27
|
display: "flex",
|
|
28
|
-
gap: "
|
|
28
|
+
gap: "3",
|
|
29
29
|
justifyContent: "space-between",
|
|
30
30
|
minWidth: 0,
|
|
31
|
-
paddingBlock: "
|
|
32
|
-
paddingInline: "
|
|
31
|
+
paddingBlock: "3",
|
|
32
|
+
paddingInline: "4",
|
|
33
33
|
});
|
|
34
34
|
const artifactTitleClassName = css({
|
|
35
35
|
fontSize: "sm",
|
|
@@ -52,7 +52,7 @@ const artifactContentClassName = css({
|
|
|
52
52
|
flex: 1,
|
|
53
53
|
minHeight: 0,
|
|
54
54
|
overflow: "auto",
|
|
55
|
-
padding: "
|
|
55
|
+
padding: "4",
|
|
56
56
|
});
|
|
57
57
|
function Artifact({ className, ...props }) {
|
|
58
58
|
return _jsx("div", { "data-slot": "artifact", className: cn(artifactClassName, className), ...props });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AriaAttributes, ComponentProps } from "react";
|
|
2
|
+
type AssistantMarkIconProps = Omit<ComponentProps<"svg">, "children" | "dangerouslySetInnerHTML" | "role" | keyof AriaAttributes>;
|
|
3
|
+
/**
|
|
4
|
+
* The path data is frozen from the Ciridae constellation mark. Regenerate it
|
|
5
|
+
* from the design source rather than hand-editing individual points.
|
|
6
|
+
*/
|
|
7
|
+
declare function AssistantMarkIcon({ className, ...props }: AssistantMarkIconProps): import("react").JSX.Element;
|
|
8
|
+
export { AssistantMarkIcon };
|
|
9
|
+
export type { AssistantMarkIconProps };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { cn } from "@ciridae/ui/utils";
|
|
4
|
+
/**
|
|
5
|
+
* The path data is frozen from the Ciridae constellation mark. Regenerate it
|
|
6
|
+
* from the design source rather than hand-editing individual points.
|
|
7
|
+
*/
|
|
8
|
+
function AssistantMarkIcon({ className, ...props }) {
|
|
9
|
+
const gradientId = `assistant-mark-${useId().replace(/:/g, "")}`;
|
|
10
|
+
return (_jsxs("svg", { ...props, "aria-hidden": "true", className: cn("size-4 shrink-0", className), "data-slot": "assistant-mark-icon", viewBox: "0 0 24 24", children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradientId, x1: "0", x2: "1", y1: "0", y2: "1", children: [_jsx("stop", { offset: "0", stopColor: "var(--assistant-mark-foreground, var(--muted-foreground))" }), _jsx("stop", { offset: "1", stopColor: "var(--assistant-mark-foreground-accent, var(--foreground))" })] }) }), _jsx("rect", { fill: "var(--assistant-mark-surface, var(--background))", height: "22.5", rx: "5.25", stroke: "var(--assistant-mark-surface-border, var(--border))", strokeWidth: "0.7", width: "22.5", x: "0.75", y: "0.75" }), _jsxs("g", { fill: `url(#${gradientId})`, children: [_jsx("path", { d: "M10.56 5.10 L 10.73 4.88 L 10.88 4.66 L 11.03 4.44 L 11.17 4.22 L 11.29 4.01 L 11.41 3.79 L 11.52 3.57 L 11.61 3.35 L 11.70 3.13 L 11.78 2.91 L 11.84 2.69 L 11.90 2.47 L 11.94 2.26 L 11.97 2.04 L 11.99 1.82 L 12.00 1.60 L 12.00 1.60 L 12.01 1.82 L 12.03 2.04 L 12.06 2.26 L 12.10 2.47 L 12.16 2.69 L 12.22 2.91 L 12.30 3.13 L 12.39 3.35 L 12.48 3.57 L 12.59 3.79 L 12.71 4.01 L 12.83 4.22 L 12.97 4.44 L 13.12 4.66 L 13.27 4.88 L 13.44 5.10 Z" }), _jsx("path", { d: "M12.50 4.16 L 12.74 4.33 L 12.99 4.48 L 13.23 4.63 L 13.47 4.77 L 13.72 4.89 L 13.96 5.01 L 14.21 5.12 L 14.45 5.21 L 14.69 5.30 L 14.94 5.38 L 15.18 5.44 L 15.42 5.50 L 15.67 5.54 L 15.91 5.57 L 16.16 5.59 L 16.40 5.60 L 16.40 5.60 L 16.16 5.61 L 15.91 5.63 L 15.67 5.66 L 15.42 5.70 L 15.18 5.76 L 14.94 5.82 L 14.69 5.90 L 14.45 5.99 L 14.21 6.08 L 13.96 6.19 L 13.72 6.31 L 13.47 6.43 L 13.23 6.57 L 12.99 6.72 L 12.74 6.87 L 12.50 7.04 Z" }), _jsx("path", { d: "M13.44 6.10 L 13.27 6.33 L 13.12 6.56 L 12.97 6.79 L 12.83 7.03 L 12.71 7.26 L 12.59 7.49 L 12.48 7.72 L 12.39 7.95 L 12.30 8.18 L 12.22 8.41 L 12.16 8.64 L 12.10 8.88 L 12.06 9.11 L 12.03 9.34 L 12.01 9.57 L 12.00 9.80 L 12.00 9.80 L 11.99 9.57 L 11.97 9.34 L 11.94 9.11 L 11.90 8.88 L 11.84 8.64 L 11.78 8.41 L 11.70 8.18 L 11.61 7.95 L 11.52 7.72 L 11.41 7.49 L 11.29 7.26 L 11.17 7.03 L 11.03 6.79 L 10.88 6.56 L 10.73 6.33 L 10.56 6.10 Z" }), _jsx("path", { d: "M11.50 7.04 L 11.26 6.87 L 11.01 6.72 L 10.77 6.57 L 10.53 6.43 L 10.28 6.31 L 10.04 6.19 L 9.79 6.08 L 9.55 5.99 L 9.31 5.90 L 9.06 5.82 L 8.82 5.76 L 8.57 5.70 L 8.33 5.66 L 8.09 5.63 L 7.84 5.61 L 7.60 5.60 L 7.60 5.60 L 7.84 5.59 L 8.09 5.57 L 8.33 5.54 L 8.57 5.50 L 8.82 5.44 L 9.06 5.38 L 9.31 5.30 L 9.55 5.21 L 9.79 5.12 L 10.04 5.01 L 10.28 4.89 L 10.53 4.77 L 10.77 4.63 L 11.01 4.48 L 11.26 4.33 L 11.50 4.16 Z" }), _jsx("path", { d: "M4.64 11.20 L 4.87 11.07 L 5.09 10.94 L 5.31 10.80 L 5.52 10.66 L 5.72 10.51 L 5.91 10.36 L 6.09 10.20 L 6.27 10.04 L 6.44 9.88 L 6.60 9.71 L 6.75 9.53 L 6.90 9.36 L 7.04 9.17 L 7.16 8.99 L 7.29 8.80 L 7.40 8.60 L 7.40 8.60 L 7.29 8.80 L 7.20 9.00 L 7.11 9.21 L 7.02 9.43 L 6.95 9.64 L 6.88 9.86 L 6.82 10.09 L 6.77 10.32 L 6.73 10.56 L 6.69 10.79 L 6.67 11.04 L 6.65 11.29 L 6.64 11.54 L 6.63 11.80 L 6.64 12.06 L 6.65 12.32 Z" }), _jsx("path", { d: "M4.47 13.04 L 4.29 12.81 L 4.11 12.58 L 3.93 12.37 L 3.74 12.16 L 3.55 11.96 L 3.36 11.77 L 3.16 11.58 L 2.95 11.41 L 2.75 11.24 L 2.54 11.08 L 2.32 10.93 L 2.11 10.79 L 1.89 10.65 L 1.66 10.53 L 1.43 10.41 L 1.20 10.30 L 1.20 10.30 L 1.44 10.40 L 1.68 10.50 L 1.92 10.58 L 2.17 10.66 L 2.42 10.73 L 2.67 10.79 L 2.93 10.84 L 3.19 10.89 L 3.46 10.92 L 3.73 10.95 L 4.00 10.97 L 4.27 10.98 L 4.56 10.98 L 4.84 10.98 L 5.13 10.97 L 5.42 10.95 Z" }), _jsx("path", { d: "M5.86 13.37 L 5.58 13.45 L 5.31 13.53 L 5.05 13.62 L 4.79 13.72 L 4.54 13.83 L 4.30 13.94 L 4.06 14.06 L 3.83 14.18 L 3.60 14.31 L 3.38 14.45 L 3.17 14.59 L 2.96 14.74 L 2.76 14.89 L 2.57 15.06 L 2.38 15.23 L 2.20 15.40 L 2.20 15.40 L 2.37 15.22 L 2.54 15.03 L 2.71 14.84 L 2.86 14.64 L 3.01 14.43 L 3.15 14.22 L 3.29 14.00 L 3.42 13.77 L 3.54 13.54 L 3.66 13.30 L 3.77 13.06 L 3.88 12.81 L 3.98 12.55 L 4.07 12.29 L 4.15 12.02 L 4.23 11.74 Z" }), _jsx("path", { d: "M17.35 12.32 L 17.36 12.06 L 17.37 11.80 L 17.36 11.54 L 17.35 11.29 L 17.33 11.04 L 17.31 10.79 L 17.27 10.56 L 17.23 10.32 L 17.18 10.09 L 17.12 9.86 L 17.05 9.64 L 16.98 9.43 L 16.89 9.21 L 16.80 9.00 L 16.71 8.80 L 16.60 8.60 L 16.60 8.60 L 16.71 8.80 L 16.84 8.99 L 16.96 9.17 L 17.10 9.36 L 17.25 9.53 L 17.40 9.71 L 17.56 9.88 L 17.73 10.04 L 17.91 10.20 L 18.09 10.36 L 18.28 10.51 L 18.48 10.66 L 18.69 10.80 L 18.91 10.94 L 19.13 11.07 L 19.36 11.20 Z" }), _jsx("path", { d: "M18.58 10.95 L 18.87 10.97 L 19.16 10.98 L 19.44 10.98 L 19.73 10.98 L 20.00 10.97 L 20.27 10.95 L 20.54 10.92 L 20.81 10.89 L 21.07 10.84 L 21.33 10.79 L 21.58 10.73 L 21.83 10.66 L 22.08 10.58 L 22.32 10.50 L 22.56 10.40 L 22.80 10.30 L 22.80 10.30 L 22.57 10.41 L 22.34 10.53 L 22.11 10.65 L 21.89 10.79 L 21.68 10.93 L 21.46 11.08 L 21.25 11.24 L 21.05 11.41 L 20.84 11.58 L 20.64 11.77 L 20.45 11.96 L 20.26 12.16 L 20.07 12.37 L 19.89 12.58 L 19.71 12.81 L 19.53 13.04 Z" }), _jsx("path", { d: "M19.77 11.74 L 19.85 12.02 L 19.93 12.29 L 20.02 12.55 L 20.12 12.81 L 20.23 13.06 L 20.34 13.30 L 20.46 13.54 L 20.58 13.77 L 20.71 14.00 L 20.85 14.22 L 20.99 14.43 L 21.14 14.64 L 21.29 14.84 L 21.46 15.03 L 21.63 15.22 L 21.80 15.40 L 21.80 15.40 L 21.62 15.23 L 21.43 15.06 L 21.24 14.89 L 21.04 14.74 L 20.83 14.59 L 20.62 14.45 L 20.40 14.31 L 20.17 14.18 L 19.94 14.06 L 19.70 13.94 L 19.46 13.83 L 19.21 13.72 L 18.95 13.62 L 18.69 13.53 L 18.42 13.45 L 18.14 13.37 Z" }), _jsx("path", { d: "M13.15 15.90 L 13.01 16.14 L 12.88 16.39 L 12.76 16.63 L 12.65 16.88 L 12.54 17.12 L 12.45 17.36 L 12.36 17.61 L 12.29 17.85 L 12.22 18.09 L 12.16 18.34 L 12.11 18.58 L 12.07 18.82 L 12.04 19.07 L 12.02 19.31 L 12.00 19.56 L 12.00 19.80 L 12.00 19.80 L 12.00 19.56 L 11.98 19.31 L 11.96 19.07 L 11.93 18.82 L 11.89 18.58 L 11.84 18.34 L 11.78 18.09 L 11.71 17.85 L 11.64 17.61 L 11.55 17.36 L 11.46 17.12 L 11.35 16.88 L 11.24 16.63 L 11.12 16.39 L 10.99 16.14 L 10.85 15.90 Z" }), _jsx("path", { d: "M6.35 11.38 L 6.49 11.60 L 6.62 11.81 L 6.77 12.02 L 6.91 12.21 L 7.06 12.40 L 7.21 12.58 L 7.37 12.75 L 7.53 12.91 L 7.70 13.07 L 7.87 13.22 L 8.04 13.36 L 8.22 13.49 L 8.40 13.61 L 8.58 13.72 L 8.77 13.83 L 8.96 13.93 L 8.96 13.93 L 8.77 13.84 L 8.57 13.76 L 8.36 13.68 L 8.15 13.62 L 7.94 13.56 L 7.73 13.51 L 7.51 13.47 L 7.28 13.43 L 7.05 13.41 L 6.82 13.39 L 6.59 13.38 L 6.35 13.38 L 6.10 13.38 L 5.86 13.40 L 5.60 13.42 L 5.35 13.45 Z" }), _jsx("path", { d: "M11.05 16.22 L 10.91 16.00 L 10.78 15.79 L 10.63 15.58 L 10.49 15.39 L 10.34 15.20 L 10.19 15.02 L 10.03 14.85 L 9.87 14.69 L 9.70 14.53 L 9.53 14.38 L 9.36 14.24 L 9.18 14.11 L 9.00 13.99 L 8.82 13.88 L 8.63 13.77 L 8.44 13.67 L 8.44 13.67 L 8.63 13.76 L 8.83 13.84 L 9.04 13.92 L 9.25 13.98 L 9.46 14.04 L 9.67 14.09 L 9.89 14.13 L 10.12 14.17 L 10.35 14.19 L 10.58 14.21 L 10.81 14.22 L 11.05 14.22 L 11.30 14.22 L 11.54 14.20 L 11.80 14.18 L 12.05 14.15 Z" }), _jsx("path", { d: "M18.65 13.45 L 18.40 13.42 L 18.14 13.40 L 17.90 13.38 L 17.65 13.38 L 17.41 13.38 L 17.18 13.39 L 16.95 13.41 L 16.72 13.43 L 16.49 13.47 L 16.27 13.51 L 16.06 13.56 L 15.85 13.62 L 15.64 13.68 L 15.43 13.76 L 15.23 13.84 L 15.04 13.93 L 15.04 13.93 L 15.23 13.83 L 15.42 13.72 L 15.60 13.61 L 15.78 13.49 L 15.96 13.36 L 16.13 13.22 L 16.30 13.07 L 16.47 12.91 L 16.63 12.75 L 16.79 12.58 L 16.94 12.40 L 17.09 12.21 L 17.23 12.02 L 17.38 11.81 L 17.51 11.60 L 17.65 11.38 Z" }), _jsx("path", { d: "M11.95 14.15 L 12.20 14.18 L 12.46 14.20 L 12.70 14.22 L 12.95 14.22 L 13.19 14.22 L 13.42 14.21 L 13.65 14.19 L 13.88 14.17 L 14.11 14.13 L 14.33 14.09 L 14.54 14.04 L 14.75 13.98 L 14.96 13.92 L 15.17 13.84 L 15.37 13.76 L 15.56 13.67 L 15.56 13.67 L 15.37 13.77 L 15.18 13.88 L 15.00 13.99 L 14.82 14.11 L 14.64 14.24 L 14.47 14.38 L 14.30 14.53 L 14.13 14.69 L 13.97 14.85 L 13.81 15.02 L 13.66 15.20 L 13.51 15.39 L 13.37 15.58 L 13.22 15.79 L 13.09 16.00 L 12.95 16.22 Z" }), _jsx("circle", { cx: "5.40", cy: "12.20", r: "0.98" }), _jsx("circle", { cx: "18.60", cy: "12.20", r: "0.98" }), _jsx("circle", { cx: "12.00", cy: "15.40", r: "0.98" }), _jsx("circle", { cx: "12", cy: "5.6", r: "0.72" })] })] }));
|
|
11
|
+
}
|
|
12
|
+
export { AssistantMarkIcon };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { AttachmentActionAddFilesProps, AttachmentActionAddScreenshotProps, AttachmentRemoveProps } from "@ciridae/ui-agent/attachments-types";
|
|
3
|
+
declare function AttachmentRemove({ attachmentId, children, className, label, onClick, size, type, variant: buttonVariant, ...props }: AttachmentRemoveProps): React.JSX.Element | null;
|
|
4
|
+
declare function AttachmentActionAddFiles({ children, className, label, onClick, renderAs, size, tooltip, type, variant, ...props }: AttachmentActionAddFilesProps): React.JSX.Element;
|
|
5
|
+
declare function AttachmentActionAddScreenshot({ children, className, disabled, label, onClick, renderAs, size, status, tooltip, type, variant, ...props }: AttachmentActionAddScreenshotProps): React.JSX.Element;
|
|
6
|
+
declare function useAttachmentDropTarget({ global, ref, }: {
|
|
7
|
+
global?: boolean;
|
|
8
|
+
ref: React.RefObject<HTMLElement | null>;
|
|
9
|
+
}): void;
|
|
10
|
+
export { AttachmentActionAddFiles, AttachmentActionAddScreenshot, AttachmentRemove, useAttachmentDropTarget, };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
3
|
+
import { CameraIcon, PaperclipIcon, XIcon } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { AttachmentItemContext, useAttachments, useOptionalAttachments, } from "@ciridae/ui-agent/attachments-context";
|
|
6
|
+
import { getAttachmentRemoveSize, getScreenshotLabel } from "@ciridae/ui-agent/attachments-model";
|
|
7
|
+
import { Button } from "@ciridae/ui/button";
|
|
8
|
+
import { DropdownMenuItem } from "@ciridae/ui/dropdown-menu";
|
|
9
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@ciridae/ui/tooltip";
|
|
10
|
+
import { cn } from "@ciridae/ui/utils";
|
|
11
|
+
const attachmentRemoveClassNames = {
|
|
12
|
+
grid: [
|
|
13
|
+
"absolute top-2 right-2 size-6 rounded-full bg-background/90 p-0 opacity-0 shadow-sm transition-opacity",
|
|
14
|
+
"group-focus-within/attachment:opacity-100 group-hover/attachment:opacity-100",
|
|
15
|
+
"hover:bg-background [&>svg]:size-3",
|
|
16
|
+
],
|
|
17
|
+
inline: [
|
|
18
|
+
"absolute top-1/2 right-0.5 size-4 -translate-y-1/2 rounded p-0 opacity-0 transition-opacity",
|
|
19
|
+
"group-focus-within/attachment:opacity-100 group-hover/attachment:opacity-100",
|
|
20
|
+
"[&>svg]:size-2.5",
|
|
21
|
+
],
|
|
22
|
+
list: "size-8 shrink-0 rounded p-0 [&>svg]:size-4",
|
|
23
|
+
};
|
|
24
|
+
function getResolvedAttachmentId(attachmentId, item) {
|
|
25
|
+
return attachmentId ?? item?.data.id;
|
|
26
|
+
}
|
|
27
|
+
function getResolvedAttachmentVariant(item) {
|
|
28
|
+
return item?.variant ?? "inline";
|
|
29
|
+
}
|
|
30
|
+
function hasAttachmentRemoveAction(item, resolvedAttachmentId, onClick) {
|
|
31
|
+
return Boolean(item?.onRemove || resolvedAttachmentId || onClick);
|
|
32
|
+
}
|
|
33
|
+
function handleAttachmentRemove({ controller, event, item, onClick, resolvedAttachmentId, }) {
|
|
34
|
+
event.stopPropagation();
|
|
35
|
+
onClick?.(event);
|
|
36
|
+
if (event.defaultPrevented)
|
|
37
|
+
return;
|
|
38
|
+
if (item?.onRemove) {
|
|
39
|
+
item.onRemove();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (resolvedAttachmentId)
|
|
43
|
+
controller?.remove(resolvedAttachmentId);
|
|
44
|
+
}
|
|
45
|
+
function getResolvedScreenshotStatus(status, contextStatus) {
|
|
46
|
+
return status ?? contextStatus ?? "idle";
|
|
47
|
+
}
|
|
48
|
+
function getResolvedScreenshotLabel(label, status) {
|
|
49
|
+
return label ?? getScreenshotLabel(status);
|
|
50
|
+
}
|
|
51
|
+
function isScreenshotActionDisabled(disabled, status) {
|
|
52
|
+
return disabled ?? status === "unavailable";
|
|
53
|
+
}
|
|
54
|
+
function AttachmentRemove({ attachmentId, children, className, label = "Remove", onClick, size, type = "button", variant: buttonVariant = "ghost", ...props }) {
|
|
55
|
+
const controller = useOptionalAttachments();
|
|
56
|
+
const item = React.useContext(AttachmentItemContext);
|
|
57
|
+
const resolvedAttachmentId = getResolvedAttachmentId(attachmentId, item);
|
|
58
|
+
const attachmentVariant = getResolvedAttachmentVariant(item);
|
|
59
|
+
const handleClick = React.useCallback((event) => {
|
|
60
|
+
handleAttachmentRemove({ controller, event, item, onClick, resolvedAttachmentId });
|
|
61
|
+
}, [controller, item, onClick, resolvedAttachmentId]);
|
|
62
|
+
if (!hasAttachmentRemoveAction(item, resolvedAttachmentId, onClick))
|
|
63
|
+
return null;
|
|
64
|
+
return (_jsxs(Button, { "aria-label": label, "data-slot": "attachment-remove", onClick: handleClick, size: size ?? getAttachmentRemoveSize(attachmentVariant), type: type, variant: buttonVariant, className: cn(attachmentRemoveClassNames[attachmentVariant], className), ...props, children: [children ?? _jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: label })] }));
|
|
65
|
+
}
|
|
66
|
+
function AttachmentActionAddFiles({ children, className, label = "Add attachments", onClick, renderAs = "button", size = "icon-sm", tooltip = "Add attachments", type = "button", variant = "ghost", ...props }) {
|
|
67
|
+
const attachments = useOptionalAttachments();
|
|
68
|
+
const handleAction = React.useCallback(() => {
|
|
69
|
+
attachments?.openFileDialog();
|
|
70
|
+
}, [attachments]);
|
|
71
|
+
if (renderAs === "menu-item") {
|
|
72
|
+
return (_jsxs(DropdownMenuItem, { "data-slot": "attachment-action-add-files", onSelect: (event) => {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
handleAction();
|
|
75
|
+
}, className: cn("gap-2", className), children: [children ?? _jsx(PaperclipIcon, {}), label] }));
|
|
76
|
+
}
|
|
77
|
+
const button = (_jsx(Button, { "aria-label": label, "data-slot": "attachment-action-add-files", onClick: (event) => {
|
|
78
|
+
onClick?.(event);
|
|
79
|
+
if (event.defaultPrevented)
|
|
80
|
+
return;
|
|
81
|
+
handleAction();
|
|
82
|
+
}, size: size, type: type, variant: variant, className: cn("shrink-0 text-muted-foreground", className), ...props, children: children ?? _jsx(PaperclipIcon, {}) }));
|
|
83
|
+
return withOptionalTooltip(button, tooltip);
|
|
84
|
+
}
|
|
85
|
+
function AttachmentActionAddScreenshot({ children, className, disabled, label, onClick, renderAs = "button", size = "icon-sm", status, tooltip, type = "button", variant = "ghost", ...props }) {
|
|
86
|
+
const attachments = useOptionalAttachments();
|
|
87
|
+
const resolvedStatus = getResolvedScreenshotStatus(status, attachments?.screenshotStatus);
|
|
88
|
+
const resolvedLabel = getResolvedScreenshotLabel(label, resolvedStatus);
|
|
89
|
+
const isDisabled = isScreenshotActionDisabled(disabled, resolvedStatus);
|
|
90
|
+
const content = children ?? _jsx(CameraIcon, {});
|
|
91
|
+
const handleAction = React.useCallback(() => {
|
|
92
|
+
void attachments?.takeScreenshot();
|
|
93
|
+
}, [attachments]);
|
|
94
|
+
if (renderAs === "menu-item") {
|
|
95
|
+
return (_jsxs(DropdownMenuItem, { "data-slot": "attachment-action-add-screenshot", "data-status": resolvedStatus, disabled: isDisabled, onSelect: (event) => {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
handleAction();
|
|
98
|
+
}, className: cn("gap-2", className), children: [content, resolvedLabel] }));
|
|
99
|
+
}
|
|
100
|
+
const button = (_jsx(Button, { "aria-label": resolvedLabel, "data-slot": "attachment-action-add-screenshot", "data-status": resolvedStatus, disabled: isDisabled, loading: resolvedStatus === "capturing", loadingLabel: "Capturing screenshot", onClick: (event) => {
|
|
101
|
+
onClick?.(event);
|
|
102
|
+
if (event.defaultPrevented)
|
|
103
|
+
return;
|
|
104
|
+
handleAction();
|
|
105
|
+
}, size: size, type: type, variant: variant, className: cn("shrink-0 text-muted-foreground", className), ...props, children: content }));
|
|
106
|
+
return withOptionalTooltip(button, tooltip ?? resolvedLabel);
|
|
107
|
+
}
|
|
108
|
+
function useAttachmentDropTarget({ global, ref, }) {
|
|
109
|
+
const { add } = useAttachments();
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
const target = global ? document : ref.current;
|
|
112
|
+
if (!target)
|
|
113
|
+
return;
|
|
114
|
+
const handleDragOver = (event) => {
|
|
115
|
+
if (!(event instanceof DragEvent))
|
|
116
|
+
return;
|
|
117
|
+
if (event.dataTransfer?.types.includes("Files"))
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
};
|
|
120
|
+
const handleDrop = (event) => {
|
|
121
|
+
if (!(event instanceof DragEvent))
|
|
122
|
+
return;
|
|
123
|
+
if (!event.dataTransfer?.types.includes("Files"))
|
|
124
|
+
return;
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
if (event.dataTransfer.files.length > 0)
|
|
127
|
+
add(event.dataTransfer.files);
|
|
128
|
+
};
|
|
129
|
+
target.addEventListener("dragover", handleDragOver);
|
|
130
|
+
target.addEventListener("drop", handleDrop);
|
|
131
|
+
return () => {
|
|
132
|
+
target.removeEventListener("dragover", handleDragOver);
|
|
133
|
+
target.removeEventListener("drop", handleDrop);
|
|
134
|
+
};
|
|
135
|
+
}, [add, global, ref]);
|
|
136
|
+
}
|
|
137
|
+
function withOptionalTooltip(button, tooltip) {
|
|
138
|
+
if (!tooltip)
|
|
139
|
+
return button;
|
|
140
|
+
return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: button }), _jsx(TooltipContent, { children: tooltip })] }) }));
|
|
141
|
+
}
|
|
142
|
+
export { AttachmentActionAddFiles, AttachmentActionAddScreenshot, AttachmentRemove, useAttachmentDropTarget, };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { AttachmentControllerContextValue, AttachmentData, AttachmentMediaCategory, AttachmentProviderProps, AttachmentVariant } from "@ciridae/ui-agent/attachments-types";
|
|
3
|
+
declare const AttachmentControllerContext: React.Context<AttachmentControllerContextValue | null>;
|
|
4
|
+
declare const AttachmentsContext: React.Context<{
|
|
5
|
+
variant: AttachmentVariant;
|
|
6
|
+
} | null>;
|
|
7
|
+
declare const AttachmentItemContext: React.Context<{
|
|
8
|
+
data: AttachmentData;
|
|
9
|
+
mediaCategory: AttachmentMediaCategory;
|
|
10
|
+
onRemove?: () => void;
|
|
11
|
+
variant: AttachmentVariant;
|
|
12
|
+
} | null>;
|
|
13
|
+
declare function AttachmentProvider({ accept, attachments, children, defaultAttachments, maxAttachmentSizeMb, maxAttachments, multiple, onAttachmentError, onAttachmentsChange, onCaptureScreenshot, }: AttachmentProviderProps): React.JSX.Element;
|
|
14
|
+
declare function useAttachments(): AttachmentControllerContextValue;
|
|
15
|
+
declare function useOptionalAttachments(): AttachmentControllerContextValue | null;
|
|
16
|
+
declare function useAttachmentsContext(): {
|
|
17
|
+
variant: AttachmentVariant;
|
|
18
|
+
};
|
|
19
|
+
declare function useAttachmentContext(): {
|
|
20
|
+
data: AttachmentData;
|
|
21
|
+
mediaCategory: AttachmentMediaCategory;
|
|
22
|
+
onRemove?: () => void;
|
|
23
|
+
variant: AttachmentVariant;
|
|
24
|
+
};
|
|
25
|
+
export { AttachmentControllerContext, AttachmentItemContext, AttachmentProvider, AttachmentsContext, useAttachmentContext, useAttachments, useAttachmentsContext, useOptionalAttachments, };
|