@asharca/ui 0.1.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.
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/Brand.d.ts +6 -0
- package/dist/Brand.js +5 -0
- package/dist/ChatShell.d.ts +21 -0
- package/dist/ChatShell.js +29 -0
- package/dist/ChatThread.d.ts +96 -0
- package/dist/ChatThread.js +306 -0
- package/dist/ContentPage.d.ts +6 -0
- package/dist/ContentPage.js +4 -0
- package/dist/Controls.d.ts +68 -0
- package/dist/Controls.js +99 -0
- package/dist/ConversationSidebar.d.ts +43 -0
- package/dist/ConversationSidebar.js +67 -0
- package/dist/Dialog.d.ts +9 -0
- package/dist/Dialog.js +35 -0
- package/dist/Feedback.d.ts +22 -0
- package/dist/Feedback.js +48 -0
- package/dist/Forms.d.ts +38 -0
- package/dist/Forms.js +99 -0
- package/dist/Layout.d.ts +73 -0
- package/dist/Layout.js +53 -0
- package/dist/MermaidAssistantText.d.ts +1 -0
- package/dist/MermaidAssistantText.js +15 -0
- package/dist/Navigation.d.ts +39 -0
- package/dist/Navigation.js +60 -0
- package/dist/Overlays.d.ts +24 -0
- package/dist/Overlays.js +62 -0
- package/dist/RotatingHeadline.d.ts +4 -0
- package/dist/RotatingHeadline.js +17 -0
- package/dist/SafeStreamdown.d.ts +5 -0
- package/dist/SafeStreamdown.js +57 -0
- package/dist/Sidebar.d.ts +9 -0
- package/dist/Sidebar.js +24 -0
- package/dist/WorkspaceTabBar.d.ts +33 -0
- package/dist/WorkspaceTabBar.js +56 -0
- package/dist/chat-markdown.d.ts +1 -0
- package/dist/chat-markdown.js +4 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +17 -0
- package/package.json +107 -0
- package/src/styles.css +693 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { createContext, lazy, Suspense, useCallback, useContext, useMemo, useRef, useState, } from 'react';
|
|
15
|
+
import { ActionBarPrimitive, AssistantRuntimeProvider, AttachmentPrimitive, ChainOfThoughtPrimitive, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useAuiState, useComposerRuntime, useMessagePartText, } from '@assistant-ui/react';
|
|
16
|
+
import { StreamdownTextPrimitive } from '@assistant-ui/react-streamdown';
|
|
17
|
+
import { code } from '@streamdown/code';
|
|
18
|
+
import { Popover } from 'radix-ui';
|
|
19
|
+
import remarkBreaks from 'remark-breaks';
|
|
20
|
+
import { defaultRemarkPlugins } from 'streamdown';
|
|
21
|
+
import { Bot, Box, Brain, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, CircleAlert, CirclePause, Copy, Globe2, Loader2, Maximize2, Minimize2, Paperclip, Pencil, Plug, Plus, RefreshCw, Send, Split, UserRound, Wrench, X, } from 'lucide-react';
|
|
22
|
+
import { Button, IconButton } from "./Controls.js";
|
|
23
|
+
import { hasMermaidFence } from "./chat-markdown.js";
|
|
24
|
+
const MermaidAssistantText = lazy(() => import("./MermaidAssistantText.js"));
|
|
25
|
+
export const chatThreadDefaultLabels = {
|
|
26
|
+
addAttachment: 'Add attachment',
|
|
27
|
+
allowTool: 'Allow',
|
|
28
|
+
attachment: 'Attachment',
|
|
29
|
+
attachmentsUnavailable: 'Attachments are not available.',
|
|
30
|
+
cancel: 'Cancel',
|
|
31
|
+
composerTools: 'Composer tools',
|
|
32
|
+
conversationBranch: 'Conversation branch',
|
|
33
|
+
copy: 'Copy',
|
|
34
|
+
edit: 'Edit',
|
|
35
|
+
expandComposer: 'Expand composer',
|
|
36
|
+
generatingReply: 'Generating reply',
|
|
37
|
+
messagePlaceholder: 'Type a message',
|
|
38
|
+
next: 'Next',
|
|
39
|
+
openComposerTools: 'Open tools',
|
|
40
|
+
preparingReply: 'Preparing',
|
|
41
|
+
previous: 'Previous',
|
|
42
|
+
processFailed: 'Process failed',
|
|
43
|
+
processed: 'Processed',
|
|
44
|
+
processing: 'Processing',
|
|
45
|
+
regenerate: 'Regenerate',
|
|
46
|
+
rejectTool: 'Reject',
|
|
47
|
+
removeAttachment: (name) => `Remove ${name}`,
|
|
48
|
+
restoreComposer: 'Restore composer',
|
|
49
|
+
save: 'Save',
|
|
50
|
+
scrollToLatestMessage: 'Scroll to latest message',
|
|
51
|
+
send: 'Send',
|
|
52
|
+
startBranch: 'Start a new branch',
|
|
53
|
+
startConversation: 'Start a conversation',
|
|
54
|
+
stop: 'Stop',
|
|
55
|
+
thinking: 'Thinking',
|
|
56
|
+
thought: 'Thought',
|
|
57
|
+
toolApprovalDescription: 'This tool needs your approval before it can run.',
|
|
58
|
+
toolAwaitingApproval: 'Awaiting approval',
|
|
59
|
+
toolCompleted: 'Completed',
|
|
60
|
+
toolFailed: 'Failed',
|
|
61
|
+
toolInput: 'Input',
|
|
62
|
+
toolKindMcp: 'MCP',
|
|
63
|
+
toolKindSandbox: 'Sandbox',
|
|
64
|
+
toolKindSkill: 'Skill',
|
|
65
|
+
toolKindSubagent: 'Sub-agent',
|
|
66
|
+
toolKindTool: 'Tool',
|
|
67
|
+
toolKindWeb: 'Web',
|
|
68
|
+
toolOutput: 'Output',
|
|
69
|
+
toolRunning: 'Running',
|
|
70
|
+
user: 'You',
|
|
71
|
+
usingTool: (toolName) => `Using ${toolName}`,
|
|
72
|
+
};
|
|
73
|
+
const LabelsContext = createContext(chatThreadDefaultLabels);
|
|
74
|
+
const UserTextTransformContext = createContext((text) => text);
|
|
75
|
+
const markdownPlugins = { code };
|
|
76
|
+
const markdownRemarkPlugins = [...Object.values(defaultRemarkPlugins), remarkBreaks];
|
|
77
|
+
function cx(...classes) {
|
|
78
|
+
return classes.filter(Boolean).join(' ');
|
|
79
|
+
}
|
|
80
|
+
function useLabels() {
|
|
81
|
+
return useContext(LabelsContext);
|
|
82
|
+
}
|
|
83
|
+
function formatToolResult(result) {
|
|
84
|
+
if (typeof result === 'string')
|
|
85
|
+
return result;
|
|
86
|
+
if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) {
|
|
87
|
+
const text = result.content.flatMap((part) => (part && typeof part === 'object' && 'text' in part && typeof part.text === 'string'
|
|
88
|
+
? [part.text]
|
|
89
|
+
: [])).join('\n\n');
|
|
90
|
+
if (text)
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return JSON.stringify(result, null, 2);
|
|
95
|
+
}
|
|
96
|
+
catch (_a) {
|
|
97
|
+
return String(result);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function toolKind(toolName) {
|
|
101
|
+
if (/(?:^|__)(?:brave|web|firecrawl|fetch|search|crawl|scrape|extract|browser)/i.test(toolName))
|
|
102
|
+
return 'web';
|
|
103
|
+
if (/skill/i.test(toolName))
|
|
104
|
+
return 'skill';
|
|
105
|
+
if (/sandbox|terminal|shell|process|filesystem/i.test(toolName))
|
|
106
|
+
return 'sandbox';
|
|
107
|
+
if (/sub.?agent|delegate/i.test(toolName))
|
|
108
|
+
return 'subagent';
|
|
109
|
+
if (toolName.includes('__'))
|
|
110
|
+
return 'mcp';
|
|
111
|
+
return 'tool';
|
|
112
|
+
}
|
|
113
|
+
function toolKindLabel(kind, labels) {
|
|
114
|
+
return {
|
|
115
|
+
web: labels.toolKindWeb,
|
|
116
|
+
skill: labels.toolKindSkill,
|
|
117
|
+
sandbox: labels.toolKindSandbox,
|
|
118
|
+
mcp: labels.toolKindMcp,
|
|
119
|
+
subagent: labels.toolKindSubagent,
|
|
120
|
+
tool: labels.toolKindTool,
|
|
121
|
+
}[kind];
|
|
122
|
+
}
|
|
123
|
+
function displayToolName(toolName) {
|
|
124
|
+
const sandboxAlias = /^(?:mcp__)?tp_\d+_[A-Za-z0-9_-]+__(.+)$/.exec(toolName);
|
|
125
|
+
if (sandboxAlias)
|
|
126
|
+
return sandboxAlias[1];
|
|
127
|
+
return toolName;
|
|
128
|
+
}
|
|
129
|
+
function UserText({ text }) {
|
|
130
|
+
const transform = useContext(UserTextTransformContext);
|
|
131
|
+
return _jsx("span", { className: "block whitespace-pre-wrap [&:not(:last-child)]:mb-2", children: transform(text) });
|
|
132
|
+
}
|
|
133
|
+
function AssistantText() {
|
|
134
|
+
const { text } = useMessagePartText();
|
|
135
|
+
if (hasMermaidFence(text)) {
|
|
136
|
+
return (_jsx(Suspense, { fallback: _jsx(PlainAssistantText, {}), children: _jsx(MermaidAssistantText, {}) }));
|
|
137
|
+
}
|
|
138
|
+
return _jsx(PlainAssistantText, {});
|
|
139
|
+
}
|
|
140
|
+
function PlainAssistantText() {
|
|
141
|
+
return (_jsx(StreamdownTextPrimitive, { plugins: markdownPlugins, remarkPlugins: markdownRemarkPlugins, linkSafety: { enabled: true }, security: {
|
|
142
|
+
allowedProtocols: ['http', 'https', 'mailto'],
|
|
143
|
+
allowDataImages: false,
|
|
144
|
+
}, className: "space-y-2 [&_li]:my-0.5 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:my-1 [&_pre]:my-2 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-5" }));
|
|
145
|
+
}
|
|
146
|
+
function ReasoningPart({ text, status }) {
|
|
147
|
+
const labels = useLabels();
|
|
148
|
+
if (!text.trim())
|
|
149
|
+
return null;
|
|
150
|
+
const running = status.type === 'running';
|
|
151
|
+
return (_jsxs("details", { open: running, className: "group/reasoning rounded-md", children: [_jsxs("summary", { className: "flex min-h-7 cursor-pointer list-none items-center gap-2 rounded-md px-1 text-muted-foreground marker:content-none hover:bg-muted/50", children: [running
|
|
152
|
+
? _jsx(Loader2, { className: "size-3.5 shrink-0 animate-spin" })
|
|
153
|
+
: _jsx(CheckCircle2, { className: "size-3.5 shrink-0" }), _jsx(Brain, { className: "size-3.5 shrink-0" }), _jsx("span", { children: running ? labels.thinking : labels.thought }), text ? _jsx(ChevronRight, { className: "ml-auto size-3.5 transition-transform group-open/reasoning:rotate-90" }) : null] }), text ? (_jsx("pre", { className: "ml-5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/30 p-2 text-[11px] leading-relaxed text-muted-foreground", children: text })) : null] }));
|
|
154
|
+
}
|
|
155
|
+
function FilePart({ data, filename }) {
|
|
156
|
+
const labels = useLabels();
|
|
157
|
+
return (_jsxs("a", { href: data, download: filename, className: "my-1 inline-flex max-w-full items-center gap-2 rounded-md border border-current/20 px-2 py-1 text-xs underline-offset-2 hover:underline", children: [_jsx(Paperclip, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: filename || labels.attachment })] }));
|
|
158
|
+
}
|
|
159
|
+
function ToolPart({ toolName, status, result, isError, args, argsText, approval, respondToApproval, }) {
|
|
160
|
+
const labels = useLabels();
|
|
161
|
+
const displayName = displayToolName(toolName);
|
|
162
|
+
const kind = toolKind(toolName);
|
|
163
|
+
const Icon = kind === 'skill'
|
|
164
|
+
? Brain
|
|
165
|
+
: kind === 'web'
|
|
166
|
+
? Globe2
|
|
167
|
+
: kind === 'sandbox'
|
|
168
|
+
? Box
|
|
169
|
+
: kind === 'mcp'
|
|
170
|
+
? Plug
|
|
171
|
+
: kind === 'subagent'
|
|
172
|
+
? Bot
|
|
173
|
+
: Wrench;
|
|
174
|
+
const waitingForApproval = approval && approval.approved === undefined && !approval.resolution;
|
|
175
|
+
const running = status.type === 'running';
|
|
176
|
+
const stateLabel = waitingForApproval
|
|
177
|
+
? labels.toolAwaitingApproval
|
|
178
|
+
: running
|
|
179
|
+
? labels.toolRunning
|
|
180
|
+
: isError
|
|
181
|
+
? labels.toolFailed
|
|
182
|
+
: labels.toolCompleted;
|
|
183
|
+
const StateIcon = waitingForApproval || isError ? CircleAlert : running ? Loader2 : CheckCircle2;
|
|
184
|
+
return (_jsxs("details", { open: running || Boolean(isError) || Boolean(waitingForApproval), className: cx('group my-1 overflow-hidden rounded-lg text-xs', (isError || waitingForApproval) && 'bg-amber-500/5'), children: [_jsxs("summary", { className: "flex min-h-7 cursor-pointer list-none items-center gap-1.5 rounded-lg px-1 py-0.5 marker:content-none hover:bg-muted/50", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground transition-transform group-open:rotate-90" }), _jsx(Icon, { className: "size-3.5 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "min-w-0 flex-1 truncate text-[13px] text-muted-foreground", children: [_jsx("span", { className: "font-medium text-foreground", children: displayName }), _jsx("span", { className: "ml-1.5 text-[11px]", children: toolKindLabel(kind, labels) })] }), _jsxs("span", { className: cx('inline-flex shrink-0 items-center gap-1 px-1.5 text-[10px] font-medium', isError ? 'text-red-700 dark:text-red-300'
|
|
185
|
+
: waitingForApproval ? 'text-amber-700 dark:text-amber-300'
|
|
186
|
+
: running ? 'text-brand'
|
|
187
|
+
: 'text-muted-foreground'), children: [_jsx(StateIcon, { className: cx('size-3', running && 'animate-spin') }), stateLabel] })] }), _jsxs("div", { className: "ml-5 space-y-3 border-l border-border/70 px-3 py-2", children: [_jsxs("div", { children: [_jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: labels.toolInput }), _jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/30 p-2 text-[11px] leading-relaxed text-foreground", children: argsText.trim() || formatToolResult(args) })] }), waitingForApproval ? (_jsxs("div", { className: "rounded-md border border-amber-500/25 bg-amber-500/10 p-2.5", children: [_jsx("p", { className: "text-xs font-medium text-amber-800 dark:text-amber-200", children: labels.toolApprovalDescription }), _jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: () => respondToApproval({ approved: true }), children: labels.allowTool }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => respondToApproval({ approved: false }), children: labels.rejectTool })] })] })) : null, result !== undefined ? (_jsxs("div", { children: [_jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: labels.toolOutput }), _jsx("pre", { className: cx('max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md border p-2 text-[11px] leading-relaxed', isError ? 'border-red-500/20 bg-red-500/5 text-red-800 dark:text-red-200' : 'border-border bg-muted/30 text-foreground'), children: formatToolResult(result) })] })) : null] })] }));
|
|
188
|
+
}
|
|
189
|
+
function AssistantProcess() {
|
|
190
|
+
const labels = useLabels();
|
|
191
|
+
const running = useAuiState((state) => state.chainOfThought.status.type === 'running');
|
|
192
|
+
const hasContent = useAuiState((state) => state.chainOfThought.parts.some((part) => (part.type === 'tool-call' || (part.type === 'reasoning' && Boolean(part.text.trim())))));
|
|
193
|
+
const failed = useAuiState((state) => state.chainOfThought.parts.some((part) => (part.type === 'tool-call' && part.isError)));
|
|
194
|
+
if (!hasContent)
|
|
195
|
+
return null;
|
|
196
|
+
const timeline = (_jsx("div", { className: "ml-5 py-1", children: _jsx(ChainOfThoughtPrimitive.Parts, { components: { Reasoning: ReasoningPart, tools: { Fallback: ToolPart } } }) }));
|
|
197
|
+
if (running) {
|
|
198
|
+
return (_jsx(ChainOfThoughtPrimitive.Root, { asChild: true, children: _jsx("div", { "data-ui": "assistant-process", className: "my-1.5 text-xs", children: timeline }) }));
|
|
199
|
+
}
|
|
200
|
+
return (_jsx(ChainOfThoughtPrimitive.Root, { asChild: true, children: _jsxs("details", { open: failed, "data-ui": "assistant-process", className: "group/process my-1.5 text-xs", children: [_jsxs("summary", { className: "flex min-h-8 cursor-pointer list-none items-center gap-2 rounded-md px-1 text-muted-foreground marker:content-none hover:bg-muted/50", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 transition-transform group-open/process:rotate-90" }), failed
|
|
201
|
+
? _jsx(CircleAlert, { className: "size-3.5 shrink-0 text-red-600" })
|
|
202
|
+
: _jsx(CheckCircle2, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium text-foreground", children: failed ? labels.processFailed : labels.processed })] }), timeline] }) }));
|
|
203
|
+
}
|
|
204
|
+
function PendingIndicator() {
|
|
205
|
+
const { generatingReply } = useLabels();
|
|
206
|
+
return (_jsx("div", { role: "status", "aria-label": generatingReply, "data-ui": "conversation-pending", className: "flex items-center py-0.5 text-[13px] text-muted-foreground", children: _jsxs("span", { "aria-hidden": "true", className: "flex items-center gap-1", children: [_jsx("span", { "data-ui": "conversation-pending-dot", className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-300ms]" }), _jsx("span", { "data-ui": "conversation-pending-dot", className: "size-1 animate-bounce rounded-full bg-current [animation-delay:-150ms]" }), _jsx("span", { "data-ui": "conversation-pending-dot", className: "size-1 animate-bounce rounded-full bg-current" })] }) }));
|
|
207
|
+
}
|
|
208
|
+
function AssistantPendingPart() {
|
|
209
|
+
const running = useAuiState((state) => { var _a; return ((_a = state.message.status) === null || _a === void 0 ? void 0 : _a.type) === 'running'; });
|
|
210
|
+
if (!running)
|
|
211
|
+
return null;
|
|
212
|
+
return _jsx(PendingIndicator, {});
|
|
213
|
+
}
|
|
214
|
+
function attachmentUrl(attachment) {
|
|
215
|
+
var _a;
|
|
216
|
+
const part = (_a = attachment.content) === null || _a === void 0 ? void 0 : _a.find((item) => item.type === 'file' || item.type === 'image');
|
|
217
|
+
if ((part === null || part === void 0 ? void 0 : part.type) === 'file')
|
|
218
|
+
return part.data;
|
|
219
|
+
if ((part === null || part === void 0 ? void 0 : part.type) === 'image')
|
|
220
|
+
return part.image;
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
function SentAttachment({ attachment }) {
|
|
224
|
+
const url = attachmentUrl(attachment);
|
|
225
|
+
return (_jsxs(AttachmentPrimitive.Root, { className: "my-1 inline-flex h-8 max-w-full items-center gap-2 rounded-md border border-current/20 px-2 text-xs", children: [_jsx(Paperclip, { className: "size-3.5 shrink-0" }), url ? (_jsx("a", { href: url, download: attachment.name, className: "min-w-0 truncate underline-offset-2 hover:underline", children: _jsx(AttachmentPrimitive.Name, {}) })) : (_jsx("span", { className: "min-w-0 truncate", children: _jsx(AttachmentPrimitive.Name, {}) }))] }));
|
|
226
|
+
}
|
|
227
|
+
function ComposerAttachment({ attachment }) {
|
|
228
|
+
const labels = useLabels();
|
|
229
|
+
return (_jsxs(AttachmentPrimitive.Root, { className: "mx-0.5 my-0.5 inline-flex h-6 max-w-[calc(100%_-_0.25rem)] items-center gap-1 overflow-hidden rounded-md border border-border bg-muted/50 px-1.5 text-xs font-medium text-foreground", children: [_jsx(AttachmentPrimitive.unstable_Thumb, { className: "flex size-[18px] shrink-0 items-center justify-center rounded-[5px] bg-background text-[9px] font-semibold uppercase text-muted-foreground" }), _jsx("span", { className: "max-w-48 truncate", children: _jsx(AttachmentPrimitive.Name, {}) }), attachment.status.type === 'running' ? (_jsxs("span", { className: "text-muted-foreground", children: [Math.round(attachment.status.progress * 100), "%"] })) : null, _jsx(AttachmentPrimitive.Remove, { "aria-label": labels.removeAttachment(attachment.name), title: labels.removeAttachment(attachment.name), className: "flex size-4 shrink-0 items-center justify-center rounded-[5px] text-muted-foreground hover:bg-muted hover:text-foreground", children: _jsx(X, { className: "size-3" }) })] }));
|
|
230
|
+
}
|
|
231
|
+
function AttachmentPicker({ disabled, enabled }) {
|
|
232
|
+
const labels = useLabels();
|
|
233
|
+
const composer = useComposerRuntime();
|
|
234
|
+
const openPicker = useCallback(() => {
|
|
235
|
+
const input = document.createElement('input');
|
|
236
|
+
input.type = 'file';
|
|
237
|
+
input.multiple = true;
|
|
238
|
+
input.hidden = true;
|
|
239
|
+
const accept = composer.getState().attachmentAccept;
|
|
240
|
+
if (accept && accept !== '*')
|
|
241
|
+
input.accept = accept;
|
|
242
|
+
const removeInput = () => input.remove();
|
|
243
|
+
input.onchange = () => {
|
|
244
|
+
var _a;
|
|
245
|
+
for (const file of Array.from((_a = input.files) !== null && _a !== void 0 ? _a : [])) {
|
|
246
|
+
void composer.addAttachment(file).catch(() => undefined);
|
|
247
|
+
}
|
|
248
|
+
removeInput();
|
|
249
|
+
};
|
|
250
|
+
input.oncancel = removeInput;
|
|
251
|
+
document.body.appendChild(input);
|
|
252
|
+
input.click();
|
|
253
|
+
}, [composer]);
|
|
254
|
+
return (_jsxs(Popover.Root, { children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(IconButton, { icon: _jsx(Plus, { className: "size-[18px]" }), label: labels.openComposerTools, size: "sm", variant: "ghost", disabled: disabled, className: "size-[30px] min-h-[30px] shrink-0 rounded-full" }) }), _jsx(Popover.Portal, { children: _jsx(Popover.Content, { side: "top", align: "start", sideOffset: 8, collisionPadding: 12, "aria-label": labels.composerTools, className: "z-50 w-64 rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-xl", children: _jsx(Popover.Close, { asChild: true, children: _jsxs("button", { type: "button", disabled: !enabled, onClick: openPicker, className: "flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-left text-sm transition-colors hover:bg-muted disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx(Paperclip, { className: "size-4 shrink-0" }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block", children: labels.addAttachment }), !enabled ? _jsx("span", { className: "mt-0.5 block text-[11px] text-muted-foreground", children: labels.attachmentsUnavailable }) : null] })] }) }) }) })] }));
|
|
255
|
+
}
|
|
256
|
+
function BranchNavigator({ branch, disabled, onSelect, }) {
|
|
257
|
+
const labels = useLabels();
|
|
258
|
+
if (!branch || !onSelect)
|
|
259
|
+
return null;
|
|
260
|
+
return (_jsxs("div", { role: "group", "aria-label": labels.conversationBranch, className: "inline-flex h-8 items-center gap-0.5 text-[11px] tabular-nums text-muted-foreground", children: [_jsx(IconButton, { icon: _jsx(ChevronLeft, { className: "size-3" }), label: labels.previous, size: "sm", variant: "ghost", disabled: disabled, onClick: () => void onSelect(branch.previousMessageId), className: "rounded-md" }), _jsxs("span", { className: "min-w-8 text-center font-mono", children: [branch.position, "/", branch.total] }), _jsx(IconButton, { icon: _jsx(ChevronRight, { className: "size-3" }), label: labels.next, size: "sm", variant: "ghost", disabled: disabled, onClick: () => void onSelect(branch.nextMessageId), className: "rounded-md" })] }));
|
|
261
|
+
}
|
|
262
|
+
function UserMessage({ allowEdit, branch, busy, components, messageId, onBranchSelect, }) {
|
|
263
|
+
var _a;
|
|
264
|
+
const labels = useLabels();
|
|
265
|
+
const SentAttachmentComponent = (_a = components === null || components === void 0 ? void 0 : components.SentAttachment) !== null && _a !== void 0 ? _a : SentAttachment;
|
|
266
|
+
return (_jsx(MessagePrimitive.Root, { asChild: true, children: _jsxs("article", { id: `chat-message-${messageId}`, className: "flex flex-col items-end rounded-[10px] pt-2.5", children: [_jsxs(ComposerPrimitive.If, { editing: false, children: [_jsxs("div", { className: "flex max-w-full items-start justify-end gap-2.5", children: [_jsxs("div", { className: "min-w-0 max-w-[calc(100%_-_2.5rem)] break-words rounded-[10px] bg-muted px-4 py-2.5 text-sm leading-[1.65] text-foreground", children: [_jsx(MessagePrimitive.Parts, { components: { Text: UserText } }), _jsx(MessagePrimitive.Attachments, { children: ({ attachment }) => _jsx(SentAttachmentComponent, { attachment: attachment }) })] }), _jsx("div", { "aria-label": labels.user, className: "flex size-[30px] shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(UserRound, { className: "size-4" }) })] }), _jsxs("div", { className: "mr-10 flex min-h-[26px] items-center justify-end gap-1", children: [_jsx(BranchNavigator, { branch: branch, disabled: busy, onSelect: onBranchSelect }), _jsxs(ActionBarPrimitive.Root, { autohide: "always", className: "flex h-[26px] items-center justify-end gap-0.5", children: [allowEdit ? (_jsx(ActionBarPrimitive.Edit, { "aria-label": labels.edit, title: labels.edit, className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(Pencil, { className: "size-[14px]" }) })) : null, _jsx(ActionBarPrimitive.Copy, { "aria-label": labels.copy, title: labels.copy, className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(Copy, { className: "size-[15px]" }) })] })] })] }), _jsx(ComposerPrimitive.If, { editing: true, children: _jsxs(ComposerPrimitive.Root, { className: "mr-10 w-[min(36rem,calc(100%_-_2.5rem))] rounded-[10px] bg-muted p-2", children: [_jsx(ComposerPrimitive.Input, { autoFocus: true, rows: 2, submitMode: "enter", className: "max-h-48 min-h-14 w-full resize-none bg-transparent px-2 py-1 text-sm leading-6 outline-none" }), _jsxs("div", { className: "mt-1 flex justify-end gap-1", children: [_jsx(ComposerPrimitive.Cancel, { "aria-label": labels.cancel, title: labels.cancel, className: "flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-background hover:text-foreground", children: _jsx(X, { className: "size-4" }) }), _jsx(ComposerPrimitive.Send, { "aria-label": labels.save, title: labels.save, className: "flex size-7 items-center justify-center rounded-md bg-foreground text-background disabled:opacity-40", children: _jsx(Check, { className: "size-4" }) })] })] }) })] }) }));
|
|
267
|
+
}
|
|
268
|
+
function AssistantMessage({ agentName, allowRegenerate, branch, busy, components, messageId, onBranchSelect, onBranchStart, onRegenerateMessage, }) {
|
|
269
|
+
var _a;
|
|
270
|
+
const labels = useLabels();
|
|
271
|
+
const AssistantTextComponent = (_a = components === null || components === void 0 ? void 0 : components.AssistantText) !== null && _a !== void 0 ? _a : AssistantText;
|
|
272
|
+
const AssistantMessageBefore = components === null || components === void 0 ? void 0 : components.AssistantMessageBefore;
|
|
273
|
+
const AssistantMessageAfter = components === null || components === void 0 ? void 0 : components.AssistantMessageAfter;
|
|
274
|
+
const AssistantActions = components === null || components === void 0 ? void 0 : components.AssistantActions;
|
|
275
|
+
return (_jsx(MessagePrimitive.Root, { asChild: true, children: _jsxs("article", { id: `chat-message-${messageId}`, "data-ui": "assistant-reply", className: "group/message flex items-start justify-start gap-2.5 rounded-[10px] pt-2.5", children: [_jsx("div", { className: "flex size-[30px] shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Bot, { className: "size-[15px]" }) }), _jsxs("div", { className: "min-w-0 max-w-[calc(100%_-_2.5rem)] flex-1", children: [_jsx("div", { className: "text-sm font-semibold leading-5 text-foreground", children: agentName }), _jsxs("div", { className: "mt-2 min-w-0 break-words text-sm leading-[1.65] text-foreground", children: [AssistantMessageBefore ? _jsx(AssistantMessageBefore, { messageId: messageId }) : null, _jsx(MessagePrimitive.Parts, { components: { Text: AssistantTextComponent, File: FilePart, ChainOfThought: AssistantProcess, Empty: AssistantPendingPart } }), AssistantMessageAfter ? _jsx(AssistantMessageAfter, { messageId: messageId }) : null] }), _jsxs("div", { className: "mt-1 flex min-h-[26px] items-center gap-1", children: [_jsx(BranchNavigator, { branch: branch, disabled: busy, onSelect: onBranchSelect }), _jsxs(ActionBarPrimitive.Root, { hideWhenRunning: true, autohide: "not-last", className: "flex h-[26px] items-center gap-0.5", children: [onBranchStart ? (_jsx("button", { type: "button", disabled: busy, "aria-label": labels.startBranch, title: labels.startBranch, onClick: () => void onBranchStart(messageId), className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(Split, { className: "size-[15px]" }) })) : null, _jsx(ActionBarPrimitive.Copy, { "aria-label": labels.copy, title: labels.copy, className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(Copy, { className: "size-[15px]" }) }), AssistantActions ? _jsx(AssistantActions, { messageId: messageId }) : null, allowRegenerate && onRegenerateMessage ? (_jsx("button", { type: "button", disabled: busy, "aria-label": labels.regenerate, title: labels.regenerate, onClick: () => void onRegenerateMessage(messageId), className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(RefreshCw, { className: "size-[15px]" }) })) : allowRegenerate ? (_jsx(ActionBarPrimitive.Reload, { "aria-label": labels.regenerate, title: labels.regenerate, className: "flex size-[26px] items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(RefreshCw, { className: "size-[15px]" }) })) : null] })] })] })] }) }));
|
|
276
|
+
}
|
|
277
|
+
function ComposerExpand({ expanded, onToggle }) {
|
|
278
|
+
const labels = useLabels();
|
|
279
|
+
const Icon = expanded ? Minimize2 : Maximize2;
|
|
280
|
+
const label = expanded ? labels.restoreComposer : labels.expandComposer;
|
|
281
|
+
return (_jsx("div", { className: "absolute right-px top-px z-10 size-8", children: _jsx("button", { type: "button", onClick: onToggle, "aria-label": label, title: label, "aria-pressed": expanded, className: "pointer-events-none absolute right-1 top-1 flex size-[22px] -translate-y-2.5 translate-x-2.5 rotate-[-8deg] scale-80 items-center justify-center rounded-full bg-transparent text-muted-foreground opacity-0 transition-all duration-300 hover:bg-muted hover:text-foreground focus-visible:pointer-events-auto focus-visible:translate-x-0 focus-visible:translate-y-0 focus-visible:rotate-0 focus-visible:scale-100 focus-visible:opacity-100 group-focus-within/composer:pointer-events-auto group-focus-within/composer:translate-x-0 group-focus-within/composer:translate-y-0 group-focus-within/composer:rotate-0 group-focus-within/composer:scale-100 group-focus-within/composer:opacity-100 group-hover/composer:pointer-events-auto group-hover/composer:translate-x-0 group-hover/composer:translate-y-0 group-hover/composer:rotate-0 group-hover/composer:scale-100 group-hover/composer:opacity-100", children: _jsx(Icon, { className: "size-3" }) }) }));
|
|
282
|
+
}
|
|
283
|
+
function ChatThreadContent({ assistantName, allowAttachments, allowEdit, allowRegenerate, branchNavigation, busy, className, composerEnd, composerStatus, composerTools, components, disabled, emptyState, error, onBranchSelect, onBranchStart, onRegenerateMessage, }) {
|
|
284
|
+
const labels = useLabels();
|
|
285
|
+
const [composerRows, setComposerRows] = useState(2);
|
|
286
|
+
const composerInputRef = useRef(null);
|
|
287
|
+
const attachmentUploading = useAuiState((state) => (state.composer.attachments.some((attachment) => attachment.status.type === 'running')));
|
|
288
|
+
const composerExpanded = composerRows > 2;
|
|
289
|
+
const branchByMessageId = useMemo(() => new Map(branchNavigation.map((branch) => [branch.messageId, branch])), [branchNavigation]);
|
|
290
|
+
const toggleComposer = () => {
|
|
291
|
+
var _a;
|
|
292
|
+
setComposerRows(composerExpanded
|
|
293
|
+
? 2
|
|
294
|
+
: Math.ceil((Math.max(220, window.innerHeight * 0.5) - 6) / (14 * 1.4)));
|
|
295
|
+
(_a = composerInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
296
|
+
};
|
|
297
|
+
const blocked = disabled || busy;
|
|
298
|
+
return (_jsxs(ThreadPrimitive.Root, { "data-chat-ui": "chat-thread", className: cx('flex min-h-0 flex-1 flex-col', className), children: [_jsxs(ThreadPrimitive.Viewport, { className: "relative flex min-h-0 flex-1 flex-col overflow-y-auto bg-background", children: [_jsxs("div", { className: "flex-1 py-1.5", children: [_jsx(ThreadPrimitive.Empty, { children: _jsx("div", { className: "flex min-h-full items-center justify-center px-6 pb-24", children: emptyState !== null && emptyState !== void 0 ? emptyState : (_jsxs("div", { className: "max-w-md text-center", children: [_jsx("div", { className: "mx-auto mb-4 flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Bot, { className: "size-6" }) }), _jsx("h3", { className: "text-lg font-medium text-foreground", children: labels.startConversation })] })) }) }), _jsx("div", { className: "mx-auto flex w-full max-w-[53rem] flex-col gap-0 px-6", children: _jsx(ThreadPrimitive.Messages, { children: ({ message }) => message.role === 'user'
|
|
299
|
+
? (_jsx(UserMessage, { allowEdit: allowEdit, branch: branchByMessageId.get(message.id), busy: blocked, components: components, messageId: message.id, onBranchSelect: onBranchSelect }))
|
|
300
|
+
: (_jsx(AssistantMessage, { agentName: assistantName, allowRegenerate: allowRegenerate, branch: branchByMessageId.get(message.id), busy: blocked, components: components, messageId: message.id, onBranchSelect: onBranchSelect, onBranchStart: onBranchStart, onRegenerateMessage: onRegenerateMessage })) }) })] }), _jsx(ThreadPrimitive.ScrollToBottom, { "aria-label": labels.scrollToLatestMessage, title: labels.scrollToLatestMessage, className: "sticky bottom-3 z-10 mx-auto mb-3 flex size-9 shrink-0 items-center justify-center rounded-full border border-border bg-card text-muted-foreground hover:text-foreground disabled:invisible", children: _jsx(ChevronDown, { className: "size-4" }) })] }), _jsx("div", { className: "shrink-0 bg-background pb-3 pt-4", children: _jsxs("div", { className: "mx-auto w-full max-w-[53rem] px-6", children: [error ? (_jsx("div", { role: "alert", className: "mb-2 rounded-md border border-red-500/20 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300", children: error })) : null, _jsx(ComposerPrimitive.Root, { "data-ui": "chat.composer", className: "group/composer relative rounded-[20px] border-[0.5px] border-border bg-card pt-2 shadow-sm transition-all duration-200 ease-in-out hover:border-foreground/25 focus-within:border-foreground/25", children: _jsx(ComposerPrimitive.AttachmentDropzone, { asChild: true, children: _jsxs("div", { className: "contents", children: [_jsx(ComposerExpand, { expanded: composerExpanded, onToggle: toggleComposer }), _jsx("div", { className: "flex flex-wrap gap-1.5 px-[15px] empty:hidden", children: _jsx(ComposerPrimitive.Attachments, { children: ({ attachment }) => _jsx(ComposerAttachment, { attachment: attachment }) }) }), _jsx(ComposerPrimitive.Input, { ref: composerInputRef, placeholder: labels.messagePlaceholder, disabled: blocked, rows: 2, minRows: composerRows, submitMode: "enter", className: cx('block min-h-[46px] w-full resize-none overflow-y-auto bg-transparent pb-0 pl-[15px] pr-11 pt-1.5 text-sm leading-[1.4] text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-60 [&::-webkit-scrollbar]:w-[3px]', composerExpanded ? 'max-h-[max(220px,50vh)]' : 'max-h-[max(220px,40vh)]') }), _jsxs("div", { className: "relative z-[2] flex min-h-10 items-center justify-between gap-4 px-2 py-[5px]", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx(AttachmentPicker, { disabled: blocked, enabled: allowAttachments }), composerTools, composerStatus ? _jsx("div", { className: "min-w-0 text-[11px] text-muted-foreground", children: composerStatus }) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [composerEnd, _jsx(ThreadPrimitive.If, { running: false, children: _jsx(ComposerPrimitive.Send, { disabled: blocked || attachmentUploading, "aria-label": labels.send, title: attachmentUploading ? labels.processing : labels.send, className: "mr-0.5 mt-px flex size-[30px] shrink-0 items-center justify-center text-brand transition-all duration-200 disabled:cursor-not-allowed disabled:text-muted-foreground/50", children: _jsx(Send, { className: "size-[22px]" }) }) }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(ComposerPrimitive.Cancel, { "aria-label": labels.stop, title: labels.stop, className: "flex size-[30px] shrink-0 items-center justify-center rounded-full text-destructive hover:bg-muted", children: _jsx(CirclePause, { className: "size-5" }) }) })] })] })] }) }) })] }) })] }));
|
|
301
|
+
}
|
|
302
|
+
export function ChatThread(_a) {
|
|
303
|
+
var { runtime, assistantName, allowAttachments = false, allowEdit = false, allowRegenerate = true, branchNavigation = [], busy = false, disabled = false, labels: labelOverrides, transformUserText = (text) => text } = _a, props = __rest(_a, ["runtime", "assistantName", "allowAttachments", "allowEdit", "allowRegenerate", "branchNavigation", "busy", "disabled", "labels", "transformUserText"]);
|
|
304
|
+
const labels = useMemo(() => (Object.assign(Object.assign({}, chatThreadDefaultLabels), labelOverrides)), [labelOverrides]);
|
|
305
|
+
return (_jsx(LabelsContext.Provider, { value: labels, children: _jsx(UserTextTransformContext.Provider, { value: transformUserText, children: _jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(ChatThreadContent, Object.assign({}, props, { assistantName: assistantName, allowAttachments: allowAttachments, allowEdit: allowEdit, allowRegenerate: allowRegenerate, branchNavigation: branchNavigation, busy: busy, disabled: disabled })) }) }) }));
|
|
306
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function ContentPage({ title, children }) {
|
|
3
|
+
return (_jsxs("div", { "data-toolplane-ui": "content-page", className: "mx-auto max-w-4xl px-3 py-8 sm:py-12", children: [_jsx("h1", { className: "mb-5 text-3xl font-semibold text-foreground", children: title }), _jsx("div", { className: "ui-panel space-y-5 p-5 text-sm leading-6 text-muted-foreground sm:p-7", children: children })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type ComponentPropsWithoutRef, type InputHTMLAttributes, type ReactNode, type SelectHTMLAttributes, type TextareaHTMLAttributes } from 'react';
|
|
2
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'danger-secondary';
|
|
3
|
+
export type ControlSize = 'sm' | 'md' | 'lg';
|
|
4
|
+
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
5
|
+
asChild?: boolean;
|
|
6
|
+
loading?: boolean;
|
|
7
|
+
loadingLabel?: string;
|
|
8
|
+
size?: ControlSize;
|
|
9
|
+
variant?: ButtonVariant;
|
|
10
|
+
};
|
|
11
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
12
|
+
asChild?: boolean;
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
loadingLabel?: string;
|
|
15
|
+
size?: ControlSize;
|
|
16
|
+
variant?: ButtonVariant;
|
|
17
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
18
|
+
export type IconButtonProps = Omit<ButtonProps, 'children' | 'loadingLabel'> & {
|
|
19
|
+
icon: ReactNode;
|
|
20
|
+
label: string;
|
|
21
|
+
};
|
|
22
|
+
export declare const IconButton: import("react").ForwardRefExoticComponent<Omit<ButtonProps, "children" | "loadingLabel"> & {
|
|
23
|
+
icon: ReactNode;
|
|
24
|
+
label: string;
|
|
25
|
+
} & import("react").RefAttributes<HTMLButtonElement>>;
|
|
26
|
+
export type InputProps = InputHTMLAttributes<HTMLInputElement> & {
|
|
27
|
+
controlSize?: ControlSize;
|
|
28
|
+
};
|
|
29
|
+
export declare const Input: import("react").ForwardRefExoticComponent<InputHTMLAttributes<HTMLInputElement> & {
|
|
30
|
+
controlSize?: ControlSize;
|
|
31
|
+
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
32
|
+
export type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>;
|
|
33
|
+
export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
|
|
34
|
+
export type SelectProps = SelectHTMLAttributes<HTMLSelectElement> & {
|
|
35
|
+
controlSize?: ControlSize;
|
|
36
|
+
wrapperClassName?: string;
|
|
37
|
+
};
|
|
38
|
+
export declare const Select: import("react").ForwardRefExoticComponent<SelectHTMLAttributes<HTMLSelectElement> & {
|
|
39
|
+
controlSize?: ControlSize;
|
|
40
|
+
wrapperClassName?: string;
|
|
41
|
+
} & import("react").RefAttributes<HTMLSelectElement>>;
|
|
42
|
+
export type NativeSelectProps = SelectProps;
|
|
43
|
+
export declare const NativeSelect: import("react").ForwardRefExoticComponent<SelectHTMLAttributes<HTMLSelectElement> & {
|
|
44
|
+
controlSize?: ControlSize;
|
|
45
|
+
wrapperClassName?: string;
|
|
46
|
+
} & import("react").RefAttributes<HTMLSelectElement>>;
|
|
47
|
+
export type CheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>;
|
|
48
|
+
export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
49
|
+
export type RadioProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>;
|
|
50
|
+
export declare const Radio: import("react").ForwardRefExoticComponent<RadioProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
51
|
+
export declare function Field({ className, ...props }: ComponentPropsWithoutRef<'div'>): import("react").JSX.Element;
|
|
52
|
+
export declare function FieldLabel({ className, ...props }: ComponentPropsWithoutRef<'label'>): import("react").JSX.Element;
|
|
53
|
+
export declare function FieldDescription({ className, ...props }: ComponentPropsWithoutRef<'p'>): import("react").JSX.Element;
|
|
54
|
+
export declare function FieldError({ className, role, ...props }: ComponentPropsWithoutRef<'p'>): import("react").JSX.Element;
|
|
55
|
+
export type SearchInputProps = Omit<InputProps, 'defaultValue' | 'type' | 'value'> & {
|
|
56
|
+
clearLabel?: string;
|
|
57
|
+
label: string;
|
|
58
|
+
onClear: () => void;
|
|
59
|
+
value: string;
|
|
60
|
+
wrapperClassName?: string;
|
|
61
|
+
};
|
|
62
|
+
export declare const SearchInput: import("react").ForwardRefExoticComponent<Omit<InputProps, "type" | "value" | "defaultValue"> & {
|
|
63
|
+
clearLabel?: string;
|
|
64
|
+
label: string;
|
|
65
|
+
onClear: () => void;
|
|
66
|
+
value: string;
|
|
67
|
+
wrapperClassName?: string;
|
|
68
|
+
} & import("react").RefAttributes<HTMLInputElement>>;
|
package/dist/Controls.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { forwardRef, useImperativeHandle, useRef, } from 'react';
|
|
15
|
+
import { ChevronDown, Loader2, Search, X } from 'lucide-react';
|
|
16
|
+
import { Slot } from 'radix-ui';
|
|
17
|
+
function cx(...classes) {
|
|
18
|
+
return classes.filter(Boolean).join(' ');
|
|
19
|
+
}
|
|
20
|
+
const buttonVariants = {
|
|
21
|
+
primary: 'ui-button-primary',
|
|
22
|
+
secondary: 'ui-button-secondary',
|
|
23
|
+
ghost: 'ui-button-ghost',
|
|
24
|
+
danger: 'ui-button-primary ui-button-danger',
|
|
25
|
+
'danger-secondary': 'ui-button-secondary ui-button-danger-secondary',
|
|
26
|
+
};
|
|
27
|
+
const buttonSizes = {
|
|
28
|
+
sm: 'ui-button-sm',
|
|
29
|
+
md: '',
|
|
30
|
+
lg: 'ui-button-lg',
|
|
31
|
+
};
|
|
32
|
+
export const Button = forwardRef(function Button(_a, ref) {
|
|
33
|
+
var { 'aria-busy': ariaBusy, asChild = false, children, className, disabled, loading = false, onClick, loadingLabel, size = 'md', type = 'button', variant = 'secondary' } = _a, props = __rest(_a, ['aria-busy', "asChild", "children", "className", "disabled", "loading", "onClick", "loadingLabel", "size", "type", "variant"]);
|
|
34
|
+
const classes = cx(buttonVariants[variant], buttonSizes[size], className);
|
|
35
|
+
const busy = disabled || loading;
|
|
36
|
+
if (asChild) {
|
|
37
|
+
const handleClick = (event) => {
|
|
38
|
+
if (busy) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(event);
|
|
44
|
+
};
|
|
45
|
+
return (_jsx(Slot.Root, Object.assign({}, props, { ref: ref, "aria-busy": loading || ariaBusy || undefined, "aria-disabled": busy || undefined, "data-toolplane-ui": "button", tabIndex: busy ? -1 : props.tabIndex, className: classes, onClick: handleClick, children: children })));
|
|
46
|
+
}
|
|
47
|
+
return (_jsxs("button", Object.assign({}, props, { ref: ref, type: type, disabled: busy, "aria-busy": loading || ariaBusy || undefined, "data-toolplane-ui": "button", className: classes, onClick: onClick, children: [loading ? _jsx(Loader2, { "aria-hidden": "true", className: "size-4 animate-spin" }) : null, loading && loadingLabel ? loadingLabel : children] })));
|
|
48
|
+
});
|
|
49
|
+
export const IconButton = forwardRef(function IconButton(_a, ref) {
|
|
50
|
+
var { icon, label, loading, size = 'md', title, className } = _a, props = __rest(_a, ["icon", "label", "loading", "size", "title", "className"]);
|
|
51
|
+
return (_jsx(Button, Object.assign({}, props, { ref: ref, size: size, loading: loading, "aria-label": label, title: title !== null && title !== void 0 ? title : label, className: cx('ui-icon-button', `ui-icon-button-${size}`, className), children: loading ? null : _jsx("span", { "aria-hidden": "true", className: "inline-flex", children: icon }) })));
|
|
52
|
+
});
|
|
53
|
+
export const Input = forwardRef(function Input(_a, ref) {
|
|
54
|
+
var { className, controlSize = 'md' } = _a, props = __rest(_a, ["className", "controlSize"]);
|
|
55
|
+
return (_jsx("input", Object.assign({}, props, { ref: ref, "data-toolplane-ui": "input", className: cx('ui-input', controlSize === 'sm' && 'ui-input-sm', controlSize === 'lg' && 'ui-input-lg', className) })));
|
|
56
|
+
});
|
|
57
|
+
export const Textarea = forwardRef(function Textarea(_a, ref) {
|
|
58
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
59
|
+
return (_jsx("textarea", Object.assign({}, props, { ref: ref, "data-toolplane-ui": "textarea", className: cx('ui-textarea', className) })));
|
|
60
|
+
});
|
|
61
|
+
export const Select = forwardRef(function Select(_a, ref) {
|
|
62
|
+
var { children, className, controlSize = 'md', disabled, wrapperClassName } = _a, props = __rest(_a, ["children", "className", "controlSize", "disabled", "wrapperClassName"]);
|
|
63
|
+
return (_jsxs("span", { "data-toolplane-ui": "select", className: cx('ui-select', wrapperClassName), children: [_jsx("select", Object.assign({}, props, { ref: ref, disabled: disabled, className: cx('ui-input ui-select-control', controlSize === 'sm' && 'ui-input-sm', controlSize === 'lg' && 'ui-input-lg', className), children: children })), _jsx(ChevronDown, { "aria-hidden": "true", className: cx('ui-select-chevron', disabled && 'opacity-40') })] }));
|
|
64
|
+
});
|
|
65
|
+
export const NativeSelect = Select;
|
|
66
|
+
export const Checkbox = forwardRef(function Checkbox(_a, ref) {
|
|
67
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
68
|
+
return (_jsx("input", Object.assign({}, props, { ref: ref, type: "checkbox", "data-toolplane-ui": "checkbox", className: cx('ui-checkbox', className) })));
|
|
69
|
+
});
|
|
70
|
+
export const Radio = forwardRef(function Radio(_a, ref) {
|
|
71
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
72
|
+
return (_jsx("input", Object.assign({}, props, { ref: ref, type: "radio", "data-toolplane-ui": "radio", className: cx('ui-radio', className) })));
|
|
73
|
+
});
|
|
74
|
+
export function Field(_a) {
|
|
75
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
76
|
+
return _jsx("div", Object.assign({}, props, { "data-toolplane-ui": "field", className: cx('ui-field', className) }));
|
|
77
|
+
}
|
|
78
|
+
export function FieldLabel(_a) {
|
|
79
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
80
|
+
return _jsx("label", Object.assign({}, props, { className: cx('ui-field-label', className) }));
|
|
81
|
+
}
|
|
82
|
+
export function FieldDescription(_a) {
|
|
83
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
84
|
+
return _jsx("p", Object.assign({}, props, { className: cx('ui-field-description', className) }));
|
|
85
|
+
}
|
|
86
|
+
export function FieldError(_a) {
|
|
87
|
+
var { className, role = 'alert' } = _a, props = __rest(_a, ["className", "role"]);
|
|
88
|
+
return _jsx("p", Object.assign({}, props, { role: role, className: cx('ui-field-error', className) }));
|
|
89
|
+
}
|
|
90
|
+
export const SearchInput = forwardRef(function SearchInput(_a, ref) {
|
|
91
|
+
var { clearLabel = 'Clear search', className, label, onClear, value, wrapperClassName } = _a, props = __rest(_a, ["clearLabel", "className", "label", "onClear", "value", "wrapperClassName"]);
|
|
92
|
+
const inputRef = useRef(null);
|
|
93
|
+
useImperativeHandle(ref, () => inputRef.current);
|
|
94
|
+
return (_jsxs("div", { "data-toolplane-ui": "search-input", className: cx('tp-search-input', wrapperClassName), children: [_jsx(Search, { "aria-hidden": "true", className: "tp-search-input__icon" }), _jsx(Input, Object.assign({}, props, { ref: inputRef, type: "search", value: value, "aria-label": label, className: cx('tp-search-input__control', className) })), value ? (_jsx(IconButton, { icon: _jsx(X, { className: "size-3.5" }), label: clearLabel, size: "sm", variant: "ghost", onClick: () => {
|
|
95
|
+
var _a;
|
|
96
|
+
onClear();
|
|
97
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
98
|
+
}, className: "tp-search-input__clear" })) : null] }));
|
|
99
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type ConversationSidebarConversation = {
|
|
3
|
+
id: string;
|
|
4
|
+
title?: string | null;
|
|
5
|
+
meta?: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
deleting?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type ConversationSidebarGroup = {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
conversations: ConversationSidebarConversation[];
|
|
13
|
+
};
|
|
14
|
+
export type ConversationSidebarLabels = {
|
|
15
|
+
groups: string;
|
|
16
|
+
searchPlaceholder: string;
|
|
17
|
+
clearSearch: string;
|
|
18
|
+
newGroup: string;
|
|
19
|
+
newConversation: string;
|
|
20
|
+
untitledConversation: string;
|
|
21
|
+
noGroups: string;
|
|
22
|
+
noConversations: string;
|
|
23
|
+
noResults: string;
|
|
24
|
+
renameConversation: string;
|
|
25
|
+
deleteConversation: string;
|
|
26
|
+
showConversations: string;
|
|
27
|
+
hideConversations: string;
|
|
28
|
+
};
|
|
29
|
+
export declare const conversationSidebarDefaultLabels: ConversationSidebarLabels;
|
|
30
|
+
export type ConversationSidebarProps = {
|
|
31
|
+
groups: ConversationSidebarGroup[];
|
|
32
|
+
activeGroupId?: string | null;
|
|
33
|
+
activeConversationId?: string | null;
|
|
34
|
+
labels?: Partial<ConversationSidebarLabels>;
|
|
35
|
+
className?: string;
|
|
36
|
+
onSelectGroup?: (group: ConversationSidebarGroup) => void;
|
|
37
|
+
onSelectConversation: (conversation: ConversationSidebarConversation, group: ConversationSidebarGroup) => void;
|
|
38
|
+
onCreateGroup?: () => void;
|
|
39
|
+
onCreateConversation?: (group: ConversationSidebarGroup) => void;
|
|
40
|
+
onRenameConversation?: (conversation: ConversationSidebarConversation, group: ConversationSidebarGroup) => void;
|
|
41
|
+
onDeleteConversation?: (conversation: ConversationSidebarConversation, group: ConversationSidebarGroup) => void;
|
|
42
|
+
};
|
|
43
|
+
export declare function ConversationSidebar({ groups, activeGroupId, activeConversationId, labels: labelsOverride, className, onSelectGroup, onSelectConversation, onCreateGroup, onCreateConversation, onRenameConversation, onDeleteConversation, }: ConversationSidebarProps): import("react").JSX.Element;
|