@moldable-ai/ui 0.2.7 → 0.2.8
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/dist/components/chat/chat-input.d.ts +2 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-input.js +9 -3
- package/dist/components/chat/chat-message.d.ts +5 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +495 -164
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +16 -8
- package/dist/components/markdown.js +2 -2
- package/dist/components/ui/command.d.ts +2 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/commands.d.ts +8 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +18 -0
- package/package.json +14 -14
|
@@ -20,7 +20,8 @@ type ChatInputProps = {
|
|
|
20
20
|
onRemoveImageAttachment?: (id: string) => void;
|
|
21
21
|
footerStartControls?: ReactNode;
|
|
22
22
|
footerControls?: ReactNode;
|
|
23
|
+
submitDisabled?: boolean;
|
|
23
24
|
};
|
|
24
|
-
export declare function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder, inputRef, onStop, compact, imageAttachments, onRemoveImageAttachment, footerStartControls, footerControls, }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder, inputRef, onStop, compact, imageAttachments, onRemoveImageAttachment, footerStartControls, footerControls, submitDisabled, }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
25
26
|
export {};
|
|
26
27
|
//# sourceMappingURL=chat-input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-input.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAGT,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAA;AAKd,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,QAAQ,EAAE,CACR,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,SAAS,EACzC,IAAI,CAAC,EAAE,cAAc,KAClB,IAAI,CAAA;IACT,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACxC,uBAAuB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,mBAAmB,CAAC,EAAE,SAAS,CAAA;IAC/B,cAAc,CAAC,EAAE,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"chat-input.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAGT,SAAS,EACT,SAAS,EACV,MAAM,OAAO,CAAA;AAKd,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEvD,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,QAAQ,EAAE,CACR,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,SAAS,EACzC,IAAI,CAAC,EAAE,cAAc,KAClB,IAAI,CAAA;IACT,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACxC,uBAAuB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9C,mBAAmB,CAAC,EAAE,SAAS,CAAA;IAC/B,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAA+B,EAC/B,QAAQ,EACR,MAAM,EACN,OAAe,EACf,gBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,cAAc,EACd,cAAsB,GACvB,EAAE,cAAc,2CAqJhB"}
|
|
@@ -3,10 +3,10 @@ import { ArrowUp, Image as ImageIcon, Square, X } from 'lucide-react';
|
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import { Textarea } from '../ui/textarea';
|
|
6
|
-
export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder = 'Ask anything...', inputRef, onStop, compact = false, imageAttachments = [], onRemoveImageAttachment, footerStartControls, footerControls, }) {
|
|
6
|
+
export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder = 'Ask anything...', inputRef, onStop, compact = false, imageAttachments = [], onRemoveImageAttachment, footerStartControls, footerControls, submitDisabled = false, }) {
|
|
7
7
|
const hasInput = input.trim().length > 0;
|
|
8
8
|
const hasAttachments = imageAttachments.length > 0;
|
|
9
|
-
const canSubmit = hasInput || hasAttachments;
|
|
9
|
+
const canSubmit = !submitDisabled && (hasInput || hasAttachments);
|
|
10
10
|
const handleKeyDown = (e) => {
|
|
11
11
|
if (e.key === 'Enter') {
|
|
12
12
|
if (e.shiftKey) {
|
|
@@ -41,7 +41,13 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
|
|
|
41
41
|
}
|
|
42
42
|
// If type="submit", form handles submission
|
|
43
43
|
};
|
|
44
|
-
return (_jsx("div", { className: cn('isolate w-full p-2'), "data-chat-input-wrapper": true, children: _jsx("div", { className: "mx-auto max-w-5xl", children: _jsx("form", { onSubmit: (e) =>
|
|
44
|
+
return (_jsx("div", { className: cn('isolate w-full p-2'), "data-chat-input-wrapper": true, children: _jsx("div", { className: "mx-auto max-w-5xl", children: _jsx("form", { onSubmit: (e) => {
|
|
45
|
+
if (submitDisabled && !isResponding) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
onSubmit(e, isResponding ? 'queue' : 'send');
|
|
50
|
+
}, children: _jsxs("div", { className: cn('bg-background relative flex w-full flex-col rounded-3xl border', compact && !hasAttachments && 'h-14'), children: [!compact && hasAttachments && (_jsx("div", { className: "flex max-h-32 gap-2 overflow-x-auto px-3 pt-3", children: imageAttachments.map((attachment) => (_jsxs("div", { className: "bg-muted/70 border-border flex max-w-48 shrink-0 items-center gap-2 rounded-xl border p-1.5 pr-2", children: [_jsx("div", { className: "bg-background flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg border", children: attachment.previewUrl ? (_jsx("img", { src: attachment.previewUrl, alt: "", className: "size-full object-cover", draggable: false })) : (_jsx(ImageIcon, { className: "text-muted-foreground size-4" })) }), _jsx("span", { className: "min-w-0 truncate text-xs font-medium", title: attachment.path, children: attachment.name }), onRemoveImageAttachment && (_jsx("button", { type: "button", onClick: () => onRemoveImageAttachment(attachment.id), className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": `Remove ${attachment.name}`, children: _jsx(X, { className: "size-3.5" }) }))] }, attachment.id))) })), _jsx("div", { className: cn('w-full px-5', compact ? 'py-4' : 'pb-2 pt-4'), children: _jsx(Textarea, { ref: inputRef, value: input, placeholder: placeholder, onChange: onInputChange, onKeyDown: handleKeyDown, className: cn('max-h-32 min-h-[24px] resize-none rounded-none border-0 !bg-transparent p-0 text-sm shadow-none focus:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:!bg-transparent', compact && 'max-h-6 min-h-6') }) }), !compact && (_jsxs("div", { className: "flex min-h-11 items-center justify-between gap-3 px-3 pb-3", children: [_jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-start gap-1", children: footerStartControls }), _jsx("div", { className: "min-w-0 flex-1" }), _jsx("div", { className: "flex min-w-0 shrink-0 items-center justify-end gap-1.5", children: footerControls }), _jsxs(Button, { type: showStopButton ? 'button' : 'submit', size: "icon", className: "bg-primary text-primary-foreground size-8 cursor-pointer rounded-full hover:opacity-70 disabled:opacity-30", disabled: !canSubmit && !isResponding, onClick: handleButtonClick, children: [showStopButton ? (_jsx(Square, { className: "fill-primary-foreground size-4 animate-pulse" })) : (_jsx(ArrowUp, { className: "size-4" })), _jsx("span", { className: "sr-only", children: showStopButton
|
|
45
51
|
? 'Stop generating'
|
|
46
52
|
: isResponding
|
|
47
53
|
? 'Steer response'
|
|
@@ -27,6 +27,11 @@ export type ChatMessage = {
|
|
|
27
27
|
role: 'user' | 'assistant' | 'system';
|
|
28
28
|
content?: string;
|
|
29
29
|
parts?: ChatMessagePart[];
|
|
30
|
+
turn?: {
|
|
31
|
+
userMessageId?: string;
|
|
32
|
+
startedAtMs?: number;
|
|
33
|
+
completedAtMs?: number;
|
|
34
|
+
};
|
|
30
35
|
};
|
|
31
36
|
type MessageProps = {
|
|
32
37
|
message: ChatMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AA+BA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAA;QACV,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;IACzB,IAAI,CAAC,EAAE;QACL,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;CACF,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAopBD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CAied;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAc9B"}
|
|
@@ -1,58 +1,419 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown, Image as ImageIcon } from 'lucide-react';
|
|
4
4
|
import { memo, useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
6
6
|
import { Markdown } from '../markdown';
|
|
7
7
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '../ui/collapsible';
|
|
8
|
-
import { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
|
|
9
8
|
import { ToolApproval, ToolApprovalHeader, ToolApprovalRejected, } from './tool-approval';
|
|
10
9
|
import { useToolApprovalResponse } from './tool-approval-context';
|
|
11
|
-
import { getToolHandler } from './tool-handlers';
|
|
10
|
+
import { getToolHandler, } from './tool-handlers';
|
|
12
11
|
import { useToolProgress } from './tool-progress-context';
|
|
13
12
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
14
13
|
const DEFAULT_ACTIONS_LABEL = 'Thinking';
|
|
15
14
|
// OpenRouter sends these for encrypted reasoning msgs
|
|
16
15
|
const stripRedacted = (text) => text.replace(/\[REDACTED\]/g, '').trim();
|
|
16
|
+
function emptyActivityCounts() {
|
|
17
|
+
return {
|
|
18
|
+
createdFileCount: 0,
|
|
19
|
+
editedFileCount: 0,
|
|
20
|
+
deletedFileCount: 0,
|
|
21
|
+
exploredFileCount: 0,
|
|
22
|
+
searchCount: 0,
|
|
23
|
+
listCount: 0,
|
|
24
|
+
commandCount: 0,
|
|
25
|
+
runningCommandCount: 0,
|
|
26
|
+
webSearchCount: 0,
|
|
27
|
+
apiCallCount: 0,
|
|
28
|
+
skillActionCount: 0,
|
|
29
|
+
appActionCount: 0,
|
|
30
|
+
toolCallCount: 0,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function incrementActivityCount(counts, key, amount = 1) {
|
|
34
|
+
counts[key] += amount;
|
|
35
|
+
}
|
|
36
|
+
function asRecord(value) {
|
|
37
|
+
return value && typeof value === 'object'
|
|
38
|
+
? value
|
|
39
|
+
: undefined;
|
|
40
|
+
}
|
|
41
|
+
function stringValue(value) {
|
|
42
|
+
return typeof value === 'string' && value.trim().length > 0
|
|
43
|
+
? value
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
46
|
+
function getString(record, keys) {
|
|
47
|
+
if (!record) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
const value = stringValue(record[key]);
|
|
52
|
+
if (value) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
function getCount(record, keys) {
|
|
59
|
+
if (!record) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
for (const key of keys) {
|
|
63
|
+
const value = record[key];
|
|
64
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
return value.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
function getFileName(path) {
|
|
74
|
+
const parts = path.split('/');
|
|
75
|
+
return parts[parts.length - 1] || path;
|
|
76
|
+
}
|
|
77
|
+
function getPathLabel(path) {
|
|
78
|
+
return path ? getFileName(path) : undefined;
|
|
79
|
+
}
|
|
80
|
+
function getToolPath(part) {
|
|
81
|
+
const args = asRecord(part.args);
|
|
82
|
+
const output = asRecord(part.output);
|
|
83
|
+
return (getString(output, ['path', 'filePath', 'targetPath']) ??
|
|
84
|
+
getString(args, ['path', 'filePath', 'targetPath']));
|
|
85
|
+
}
|
|
86
|
+
function getToolQuery(part) {
|
|
87
|
+
const args = asRecord(part.args);
|
|
88
|
+
const output = asRecord(part.output);
|
|
89
|
+
return (getString(args, ['query', 'pattern', 'glob', 'text']) ??
|
|
90
|
+
getString(output, ['query', 'pattern', 'glob']));
|
|
91
|
+
}
|
|
92
|
+
function getToolCommand(part, progress) {
|
|
93
|
+
const args = asRecord(part.args);
|
|
94
|
+
const output = asRecord(part.output);
|
|
95
|
+
return (progress?.command ??
|
|
96
|
+
getString(output, ['command']) ??
|
|
97
|
+
getString(args, ['command']));
|
|
98
|
+
}
|
|
99
|
+
function getToolTarget(part) {
|
|
100
|
+
const args = asRecord(part.args);
|
|
101
|
+
const output = asRecord(part.output);
|
|
102
|
+
return (getString(output, ['appName', 'appId', 'name', 'id']) ??
|
|
103
|
+
getString(args, ['appName', 'appId', 'name', 'id', 'repoName', 'skillName']));
|
|
104
|
+
}
|
|
105
|
+
function isToolLoading(part, progress) {
|
|
106
|
+
const isApprovalResponded = part.state === 'approval-responded' || part.state === 'output-denied';
|
|
107
|
+
const wasApproved = part.approval?.approved === true;
|
|
108
|
+
return (part.state === 'partial-call' ||
|
|
109
|
+
part.state === 'call' ||
|
|
110
|
+
part.state === 'pending' ||
|
|
111
|
+
progress?.status === 'running' ||
|
|
112
|
+
(isApprovalResponded && wasApproved && part.output === undefined));
|
|
113
|
+
}
|
|
114
|
+
function toolHasError(part) {
|
|
115
|
+
const output = asRecord(part.output);
|
|
116
|
+
if (!output) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return (output.success === false ||
|
|
120
|
+
output.ok === false ||
|
|
121
|
+
(typeof output.exitCode === 'number' && output.exitCode !== 0) ||
|
|
122
|
+
Boolean(output.error));
|
|
123
|
+
}
|
|
124
|
+
function plural(count, singular, pluralForm = `${singular}s`) {
|
|
125
|
+
return `${count} ${count === 1 ? singular : pluralForm}`;
|
|
126
|
+
}
|
|
127
|
+
function sentenceCase(text) {
|
|
128
|
+
return text.length > 0 ? text.charAt(0).toUpperCase() + text.slice(1) : text;
|
|
129
|
+
}
|
|
130
|
+
function formatActivitySummary(counts, fallback, isStreaming) {
|
|
131
|
+
const segments = [];
|
|
132
|
+
const completedCommands = Math.max(0, counts.commandCount - counts.runningCommandCount);
|
|
133
|
+
const explorationDetails = [
|
|
134
|
+
counts.exploredFileCount > 0
|
|
135
|
+
? plural(counts.exploredFileCount, 'file')
|
|
136
|
+
: null,
|
|
137
|
+
counts.searchCount > 0
|
|
138
|
+
? plural(counts.searchCount, 'search', 'searches')
|
|
139
|
+
: null,
|
|
140
|
+
counts.listCount > 0
|
|
141
|
+
? plural(counts.listCount, 'directory', 'directories')
|
|
142
|
+
: null,
|
|
143
|
+
].filter(Boolean);
|
|
144
|
+
if (counts.createdFileCount > 0) {
|
|
145
|
+
segments.push(`created ${plural(counts.createdFileCount, 'file')}`);
|
|
146
|
+
}
|
|
147
|
+
if (counts.editedFileCount > 0) {
|
|
148
|
+
segments.push(`edited ${plural(counts.editedFileCount, 'file')}`);
|
|
149
|
+
}
|
|
150
|
+
if (counts.deletedFileCount > 0) {
|
|
151
|
+
segments.push(`deleted ${plural(counts.deletedFileCount, 'file')}`);
|
|
152
|
+
}
|
|
153
|
+
if (explorationDetails.length > 0) {
|
|
154
|
+
segments.push(`explored ${explorationDetails.join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
if (completedCommands > 0) {
|
|
157
|
+
segments.push(`ran ${plural(completedCommands, 'command')}`);
|
|
158
|
+
}
|
|
159
|
+
if (counts.runningCommandCount > 0) {
|
|
160
|
+
segments.push(`running ${plural(counts.runningCommandCount, 'command')}`);
|
|
161
|
+
}
|
|
162
|
+
if (counts.webSearchCount > 0) {
|
|
163
|
+
segments.push(`ran ${plural(counts.webSearchCount, 'web search', 'web searches')}`);
|
|
164
|
+
}
|
|
165
|
+
if (counts.apiCallCount > 0) {
|
|
166
|
+
segments.push(`called ${plural(counts.apiCallCount, 'app API', 'app APIs')}`);
|
|
167
|
+
}
|
|
168
|
+
if (counts.skillActionCount > 0) {
|
|
169
|
+
segments.push(`updated ${plural(counts.skillActionCount, 'skill setting')}`);
|
|
170
|
+
}
|
|
171
|
+
if (counts.appActionCount > 0) {
|
|
172
|
+
segments.push(`updated ${plural(counts.appActionCount, 'app')}`);
|
|
173
|
+
}
|
|
174
|
+
if (counts.toolCallCount > 0) {
|
|
175
|
+
segments.push(`used ${plural(counts.toolCallCount, 'tool')}`);
|
|
176
|
+
}
|
|
177
|
+
if (segments.length === 0) {
|
|
178
|
+
return isStreaming ? fallback : fallback || DEFAULT_ACTIONS_LABEL;
|
|
179
|
+
}
|
|
180
|
+
return sentenceCase(segments.join(', '));
|
|
181
|
+
}
|
|
182
|
+
function ActivityCode({ children, title, }) {
|
|
183
|
+
return (_jsx("code", { className: "text-foreground/80 inline max-w-full truncate align-baseline font-mono text-[0.95em]", title: title, children: children }));
|
|
184
|
+
}
|
|
185
|
+
function toolActivityText({ verb, path, detail, fallback, }) {
|
|
186
|
+
const label = path ?? detail;
|
|
187
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { children: verb }), label ? (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: path, children: label })] })) : fallback ? (_jsxs(_Fragment, { children: [" ", fallback] })) : null] }));
|
|
188
|
+
}
|
|
189
|
+
function summarizeToolActivity(part, toolHandler, progress, fallbackId) {
|
|
190
|
+
const counts = emptyActivityCounts();
|
|
191
|
+
const running = isToolLoading(part, progress);
|
|
192
|
+
const hasError = toolHasError(part);
|
|
193
|
+
const path = getToolPath(part);
|
|
194
|
+
const pathLabel = getPathLabel(path);
|
|
195
|
+
const query = getToolQuery(part);
|
|
196
|
+
const command = getToolCommand(part, progress);
|
|
197
|
+
const target = getToolTarget(part);
|
|
198
|
+
const output = asRecord(part.output);
|
|
199
|
+
const args = asRecord(part.args);
|
|
200
|
+
const entryId = part.toolCallId ?? fallbackId ?? `${part.toolName}-${part.state}`;
|
|
201
|
+
const loadingLabel = toolHandler.loadingLabel.replace(/\.\.\.$/, '');
|
|
202
|
+
let content = running ? loadingLabel : `Used ${part.toolName}`;
|
|
203
|
+
switch (part.toolName) {
|
|
204
|
+
case 'readFile':
|
|
205
|
+
case 'fileExists':
|
|
206
|
+
incrementActivityCount(counts, 'exploredFileCount');
|
|
207
|
+
content = toolActivityText({
|
|
208
|
+
verb: running
|
|
209
|
+
? 'Reading'
|
|
210
|
+
: part.toolName === 'fileExists'
|
|
211
|
+
? 'Checked'
|
|
212
|
+
: 'Read',
|
|
213
|
+
path: pathLabel,
|
|
214
|
+
fallback: 'file',
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
case 'readToolOutput':
|
|
218
|
+
incrementActivityCount(counts, 'exploredFileCount');
|
|
219
|
+
content = running ? 'Reading tool output' : 'Read tool output';
|
|
220
|
+
break;
|
|
221
|
+
case 'listDirectory': {
|
|
222
|
+
incrementActivityCount(counts, 'listCount');
|
|
223
|
+
const itemCount = getCount(output, ['entries', 'items']);
|
|
224
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? 'Listing' : 'Listed' }), ' ', _jsx(ActivityCode, { title: path, children: pathLabel ?? 'directory' }), typeof itemCount === 'number' && (_jsxs("span", { children: [" (", plural(itemCount, 'item'), ")"] }))] }));
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case 'grep':
|
|
228
|
+
case 'globFileSearch': {
|
|
229
|
+
incrementActivityCount(counts, 'searchCount');
|
|
230
|
+
const resultCount = getCount(output, ['matches', 'files', 'results']);
|
|
231
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? 'Searching' : 'Searched' }), query && (_jsxs(_Fragment, { children: [' ', "for ", _jsx(ActivityCode, { title: query, children: query })] })), typeof resultCount === 'number' && (_jsxs("span", { children: [" (", plural(resultCount, 'result'), ")"] }))] }));
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case 'webSearch':
|
|
235
|
+
incrementActivityCount(counts, 'webSearchCount');
|
|
236
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? 'Searching web' : 'Searched web' }), query && (_jsxs(_Fragment, { children: [' ', "for ", _jsx(ActivityCode, { title: query, children: query })] }))] }));
|
|
237
|
+
break;
|
|
238
|
+
case 'executeBashCommand':
|
|
239
|
+
case 'runCommand':
|
|
240
|
+
incrementActivityCount(counts, 'commandCount');
|
|
241
|
+
if (running) {
|
|
242
|
+
incrementActivityCount(counts, 'runningCommandCount');
|
|
243
|
+
}
|
|
244
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? 'Running' : 'Ran' }), ' ', _jsx(ActivityCode, { title: command, children: command ?? 'command' })] }));
|
|
245
|
+
break;
|
|
246
|
+
case 'writeFile': {
|
|
247
|
+
const created = output?.created === true || args?.create === true;
|
|
248
|
+
incrementActivityCount(counts, created ? 'createdFileCount' : 'editedFileCount');
|
|
249
|
+
content = toolActivityText({
|
|
250
|
+
verb: running ? 'Writing' : created ? 'Created' : 'Wrote',
|
|
251
|
+
path: pathLabel,
|
|
252
|
+
fallback: 'file',
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case 'editFile':
|
|
257
|
+
incrementActivityCount(counts, 'editedFileCount');
|
|
258
|
+
content = toolActivityText({
|
|
259
|
+
verb: running ? 'Editing' : 'Edited',
|
|
260
|
+
path: pathLabel,
|
|
261
|
+
fallback: 'file',
|
|
262
|
+
});
|
|
263
|
+
break;
|
|
264
|
+
case 'deleteFile':
|
|
265
|
+
incrementActivityCount(counts, 'deletedFileCount');
|
|
266
|
+
content = toolActivityText({
|
|
267
|
+
verb: running ? 'Deleting' : 'Deleted',
|
|
268
|
+
path: pathLabel,
|
|
269
|
+
fallback: 'file',
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
case 'listMoldableAppApi':
|
|
273
|
+
case 'callMoldableAppApi':
|
|
274
|
+
incrementActivityCount(counts, 'apiCallCount');
|
|
275
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running
|
|
276
|
+
? part.toolName === 'listMoldableAppApi'
|
|
277
|
+
? 'Listing app APIs'
|
|
278
|
+
: 'Calling app API'
|
|
279
|
+
: part.toolName === 'listMoldableAppApi'
|
|
280
|
+
? 'Listed app APIs'
|
|
281
|
+
: 'Called app API' }), target && (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: target, children: target })] }))] }));
|
|
282
|
+
break;
|
|
283
|
+
case 'listSkillRepos':
|
|
284
|
+
case 'listAvailableSkills':
|
|
285
|
+
case 'syncSkills':
|
|
286
|
+
case 'addSkillRepo':
|
|
287
|
+
case 'updateSkillSelection':
|
|
288
|
+
case 'initSkillsConfig':
|
|
289
|
+
incrementActivityCount(counts, 'skillActionCount');
|
|
290
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? loadingLabel : 'Updated skills' }), target && (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: target, children: target })] }))] }));
|
|
291
|
+
break;
|
|
292
|
+
case 'scaffoldApp':
|
|
293
|
+
case 'getAppInfo':
|
|
294
|
+
case 'unregisterApp':
|
|
295
|
+
case 'deleteAppData':
|
|
296
|
+
case 'deleteApp':
|
|
297
|
+
incrementActivityCount(counts, 'appActionCount');
|
|
298
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? loadingLabel : 'Updated app' }), target && (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: target, children: target })] }))] }));
|
|
299
|
+
break;
|
|
300
|
+
default:
|
|
301
|
+
incrementActivityCount(counts, 'toolCallCount');
|
|
302
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? loadingLabel : 'Used tool' }), ' ', _jsx(ActivityCode, { children: part.toolName })] }));
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
if (hasError) {
|
|
306
|
+
content = (_jsxs(_Fragment, { children: [content, _jsx("span", { children: " failed" })] }));
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
entry: {
|
|
310
|
+
id: entryId,
|
|
311
|
+
content,
|
|
312
|
+
isRunning: running,
|
|
313
|
+
isError: hasError,
|
|
314
|
+
},
|
|
315
|
+
counts,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function formatWorkDuration(ms) {
|
|
319
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
320
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
321
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
322
|
+
const seconds = totalSeconds % 60;
|
|
323
|
+
if (hours > 0) {
|
|
324
|
+
return `${hours}h ${minutes}m`;
|
|
325
|
+
}
|
|
326
|
+
if (minutes > 0) {
|
|
327
|
+
return `${minutes}m ${seconds}s`;
|
|
328
|
+
}
|
|
329
|
+
return `${seconds}s`;
|
|
330
|
+
}
|
|
331
|
+
function useNow(enabled) {
|
|
332
|
+
const [now, setNow] = useState(() => Date.now());
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
if (!enabled) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const interval = window.setInterval(() => {
|
|
338
|
+
setNow(Date.now());
|
|
339
|
+
}, 1000);
|
|
340
|
+
return () => window.clearInterval(interval);
|
|
341
|
+
}, [enabled]);
|
|
342
|
+
return now;
|
|
343
|
+
}
|
|
344
|
+
function WorkSummary({ messageId, startedAtMs, completedAtMs, isWorking, children, }) {
|
|
345
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
346
|
+
const now = useNow(isWorking && Boolean(startedAtMs));
|
|
347
|
+
const endAtMs = completedAtMs ?? (isWorking ? now : undefined);
|
|
348
|
+
const elapsedMs = typeof startedAtMs === 'number' && typeof endAtMs === 'number'
|
|
349
|
+
? Math.max(0, endAtMs - startedAtMs)
|
|
350
|
+
: null;
|
|
351
|
+
const durationLabel = elapsedMs !== null && (elapsedMs >= 1000 || !isWorking)
|
|
352
|
+
? formatWorkDuration(elapsedMs)
|
|
353
|
+
: null;
|
|
354
|
+
const summaryLabel = isWorking
|
|
355
|
+
? durationLabel
|
|
356
|
+
? `Working for ${durationLabel}`
|
|
357
|
+
: 'Working'
|
|
358
|
+
: durationLabel
|
|
359
|
+
? `Worked for ${durationLabel}`
|
|
360
|
+
: 'Worked';
|
|
361
|
+
return (_jsxs(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: "w-full", "data-work-summary": messageId, children: [_jsxs("div", { className: "text-muted-foreground/75 text-[13px] leading-5", children: [_jsxs(CollapsibleTrigger, { className: cn('group inline-flex max-w-full cursor-pointer items-center gap-1 rounded-md border border-transparent text-left transition-colors', 'hover:bg-muted/30 hover:text-foreground/80 focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2'), "aria-label": `${summaryLabel} activity`, children: [_jsx("span", { className: "truncate", children: summaryLabel }), _jsx(ChevronDown, { className: cn('size-3 shrink-0 opacity-40 transition-transform duration-200 group-hover:opacity-70', isOpen ? 'rotate-0' : '-rotate-90') })] }), _jsx("div", { className: "border-border/60 mt-1 w-full border-t" })] }), _jsx(CollapsibleContent, { className: "overflow-hidden", children: _jsx("div", { className: "pt-4", children: children }) })] }));
|
|
362
|
+
}
|
|
17
363
|
function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
18
364
|
// Get tool progress for streaming stdout/stderr
|
|
19
365
|
const toolProgress = useToolProgress();
|
|
20
366
|
// Get approval response handler
|
|
21
367
|
const onApprovalResponse = useToolApprovalResponse();
|
|
22
|
-
// Track open state for each
|
|
23
|
-
const [
|
|
24
|
-
// Process parts chronologically, grouping
|
|
25
|
-
//
|
|
368
|
+
// Track open state for each background activity group independently.
|
|
369
|
+
const [activityGroupStates, setActivityGroupStates] = useState(new Map());
|
|
370
|
+
// Process parts chronologically, grouping ordinary tool work and reasoning as
|
|
371
|
+
// low-emphasis background activity. Approval prompts remain prominent because
|
|
372
|
+
// they need direct user action.
|
|
26
373
|
const { contentItems, hasFinalAssistantText } = useMemo(() => {
|
|
27
374
|
const items = [];
|
|
28
375
|
const state = {
|
|
29
|
-
|
|
30
|
-
|
|
376
|
+
currentActivityGroup: null,
|
|
377
|
+
activityGroupIndex: 0,
|
|
31
378
|
hasText: false,
|
|
32
379
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
380
|
+
const updateActivityTitle = (group) => {
|
|
381
|
+
group.title = formatActivitySummary(group.counts, group.title, group.isStreaming);
|
|
382
|
+
};
|
|
383
|
+
const addActivity = (group, entry, counts) => {
|
|
384
|
+
group.entries.push(entry);
|
|
385
|
+
group.isStreaming = group.isStreaming || Boolean(entry.isRunning);
|
|
386
|
+
if (counts) {
|
|
387
|
+
for (const key of Object.keys(counts)) {
|
|
388
|
+
group.counts[key] += counts[key];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
updateActivityTitle(group);
|
|
392
|
+
};
|
|
393
|
+
// Helper to close current activity group
|
|
394
|
+
const closeActivityGroup = () => {
|
|
395
|
+
if (state.currentActivityGroup) {
|
|
36
396
|
items.push({
|
|
37
|
-
type: '
|
|
38
|
-
id: state.
|
|
39
|
-
|
|
397
|
+
type: 'activity',
|
|
398
|
+
id: state.currentActivityGroup.id,
|
|
399
|
+
activityGroup: state.currentActivityGroup,
|
|
40
400
|
});
|
|
41
|
-
state.
|
|
401
|
+
state.currentActivityGroup = null;
|
|
42
402
|
}
|
|
43
403
|
};
|
|
44
|
-
// Helper to ensure
|
|
45
|
-
const
|
|
46
|
-
if (!state.
|
|
47
|
-
state.
|
|
48
|
-
state.
|
|
49
|
-
id: `
|
|
50
|
-
|
|
404
|
+
// Helper to ensure activity group exists
|
|
405
|
+
const ensureActivityGroup = () => {
|
|
406
|
+
if (!state.currentActivityGroup) {
|
|
407
|
+
state.activityGroupIndex++;
|
|
408
|
+
state.currentActivityGroup = {
|
|
409
|
+
id: `activity-${message.id}-${state.activityGroupIndex}`,
|
|
410
|
+
entries: [],
|
|
411
|
+
counts: emptyActivityCounts(),
|
|
51
412
|
title: DEFAULT_ACTIONS_LABEL,
|
|
52
413
|
isStreaming: false,
|
|
53
414
|
};
|
|
54
415
|
}
|
|
55
|
-
return state.
|
|
416
|
+
return state.currentActivityGroup;
|
|
56
417
|
};
|
|
57
418
|
// If no parts, use content as text
|
|
58
419
|
const parts = message.parts ?? [];
|
|
@@ -74,85 +435,40 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
74
435
|
// Handle tool calls
|
|
75
436
|
if (part.type === 'tool-invocation') {
|
|
76
437
|
const toolHandler = getToolHandler(part.toolName);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const wasRejected = part.approval?.approved === false;
|
|
86
|
-
const wasApproved = part.approval?.approved === true;
|
|
87
|
-
// Render inline tool
|
|
88
|
-
// Also treat approval-responded (when approved) as loading until output arrives
|
|
89
|
-
const isToolLoading = part.state === 'partial-call' ||
|
|
90
|
-
part.state === 'call' ||
|
|
91
|
-
part.state === 'pending' ||
|
|
92
|
-
(isApprovalResponded && wasApproved && part.output === undefined);
|
|
93
|
-
// Check for streaming progress (stdout/stderr from command execution)
|
|
94
|
-
const progress = part.toolCallId
|
|
95
|
-
? toolProgress[part.toolCallId]
|
|
96
|
-
: undefined;
|
|
97
|
-
const hasStreamingOutput = progress && (progress.stdout || progress.stderr);
|
|
98
|
-
let toolContent;
|
|
99
|
-
// Handle approval request state
|
|
100
|
-
if (isApprovalRequested &&
|
|
101
|
-
toolHandler.renderApproval &&
|
|
102
|
-
part.approval?.id &&
|
|
103
|
-
onApprovalResponse) {
|
|
104
|
-
toolContent = toolHandler.renderApproval({
|
|
105
|
-
approvalId: part.approval.id,
|
|
106
|
-
toolCallId: part.toolCallId || key,
|
|
107
|
-
args: part.args,
|
|
108
|
-
}, onApprovalResponse);
|
|
109
|
-
}
|
|
110
|
-
else if (isApprovalResponded && wasRejected) {
|
|
111
|
-
// Show rejection message
|
|
112
|
-
toolContent = (_jsx(ToolApproval, { state: "output-denied", approved: false, children: _jsx(ToolApprovalHeader, { children: _jsx(ToolApprovalRejected, { children: "Command execution was rejected" }) }) }));
|
|
113
|
-
}
|
|
114
|
-
else if (hasStreamingOutput && toolHandler.renderStreaming) {
|
|
115
|
-
// Show streaming output (live stdout/stderr)
|
|
116
|
-
toolContent = toolHandler.renderStreaming(part.args, progress);
|
|
117
|
-
}
|
|
118
|
-
else if (isToolLoading) {
|
|
119
|
-
// Show loading state (arguments streaming or waiting)
|
|
120
|
-
toolContent = toolHandler.renderLoading?.(part.args) || (_jsx("div", { className: "text-muted-foreground text-xs", children: toolHandler.loadingLabel }));
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
// Show final output
|
|
124
|
-
toolContent = toolHandler.renderOutput(part.output, part.toolCallId || key);
|
|
125
|
-
}
|
|
438
|
+
const isApprovalRequested = part.state === 'approval-requested';
|
|
439
|
+
const isApprovalResponded = part.state === 'approval-responded' || part.state === 'output-denied';
|
|
440
|
+
const wasRejected = part.approval?.approved === false;
|
|
441
|
+
if (isApprovalRequested &&
|
|
442
|
+
toolHandler.renderApproval &&
|
|
443
|
+
part.approval?.id &&
|
|
444
|
+
onApprovalResponse) {
|
|
445
|
+
closeActivityGroup();
|
|
126
446
|
items.push({
|
|
127
447
|
type: 'inline-tool',
|
|
128
448
|
id: key,
|
|
129
|
-
content:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const group = ensureThinkingGroup();
|
|
135
|
-
// Show loading state for tools
|
|
136
|
-
if (part.state === 'partial-call' ||
|
|
137
|
-
part.state === 'call' ||
|
|
138
|
-
part.state === 'pending') {
|
|
139
|
-
const toolLabel = toolHandler.loadingLabel;
|
|
140
|
-
group.title = toolLabel;
|
|
141
|
-
group.isStreaming = true;
|
|
142
|
-
group.timeline.push({
|
|
143
|
-
marker: ThinkingTimelineMarker.Loading,
|
|
144
|
-
content: (_jsx("div", { className: "px-2 py-1", children: _jsx("span", { className: "text-muted-foreground font-mono text-xs", children: toolLabel }) }, `${part.toolCallId || key}-loading`)),
|
|
449
|
+
content: toolHandler.renderApproval({
|
|
450
|
+
approvalId: part.approval.id,
|
|
451
|
+
toolCallId: part.toolCallId || key,
|
|
452
|
+
args: part.args,
|
|
453
|
+
}, onApprovalResponse),
|
|
145
454
|
});
|
|
146
455
|
continue;
|
|
147
456
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
457
|
+
if (isApprovalResponded && wasRejected) {
|
|
458
|
+
closeActivityGroup();
|
|
459
|
+
items.push({
|
|
460
|
+
type: 'inline-tool',
|
|
461
|
+
id: key,
|
|
462
|
+
content: (_jsx(ToolApproval, { state: "output-denied", approved: false, children: _jsx(ToolApprovalHeader, { children: _jsx(ToolApprovalRejected, { children: "Command execution was rejected" }) }) })),
|
|
153
463
|
});
|
|
154
464
|
continue;
|
|
155
465
|
}
|
|
466
|
+
const group = ensureActivityGroup();
|
|
467
|
+
const progress = part.toolCallId
|
|
468
|
+
? toolProgress[part.toolCallId]
|
|
469
|
+
: undefined;
|
|
470
|
+
const activity = summarizeToolActivity(part, toolHandler, progress, key);
|
|
471
|
+
addActivity(group, activity.entry, activity.counts);
|
|
156
472
|
continue;
|
|
157
473
|
}
|
|
158
474
|
if (part.type === 'reasoning') {
|
|
@@ -161,7 +477,7 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
161
477
|
if (cleaned.length === 0) {
|
|
162
478
|
continue;
|
|
163
479
|
}
|
|
164
|
-
const group =
|
|
480
|
+
const group = ensureActivityGroup();
|
|
165
481
|
// Extract title from markdown **title** or use first line
|
|
166
482
|
const markdownTitleMatch = cleaned.match(/\*\*([^*]+)\*\*/);
|
|
167
483
|
const candidateTitle = markdownTitleMatch?.[1];
|
|
@@ -172,15 +488,15 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
172
488
|
const body = candidateTitle
|
|
173
489
|
? trimmed.replace(/\*\*[^*]+\*\*\s*/, '')
|
|
174
490
|
: trimmed;
|
|
175
|
-
group
|
|
176
|
-
|
|
177
|
-
content:
|
|
491
|
+
addActivity(group, {
|
|
492
|
+
id: key,
|
|
493
|
+
content: _jsx("span", { className: "whitespace-pre-wrap", children: body }),
|
|
178
494
|
});
|
|
179
495
|
continue;
|
|
180
496
|
}
|
|
181
497
|
if (part.type === 'text') {
|
|
182
|
-
// Close current
|
|
183
|
-
|
|
498
|
+
// Close current background activity group before text.
|
|
499
|
+
closeActivityGroup();
|
|
184
500
|
state.hasText = true;
|
|
185
501
|
items.push({
|
|
186
502
|
type: 'text',
|
|
@@ -190,7 +506,7 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
190
506
|
continue;
|
|
191
507
|
}
|
|
192
508
|
if (part.type === 'file') {
|
|
193
|
-
|
|
509
|
+
closeActivityGroup();
|
|
194
510
|
items.push({
|
|
195
511
|
type: 'file',
|
|
196
512
|
id: key,
|
|
@@ -198,8 +514,8 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
198
514
|
});
|
|
199
515
|
}
|
|
200
516
|
}
|
|
201
|
-
// Close any remaining
|
|
202
|
-
|
|
517
|
+
// Close any remaining activity group
|
|
518
|
+
closeActivityGroup();
|
|
203
519
|
return {
|
|
204
520
|
contentItems: items,
|
|
205
521
|
hasFinalAssistantText: state.hasText,
|
|
@@ -211,27 +527,27 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
211
527
|
toolProgress,
|
|
212
528
|
onApprovalResponse,
|
|
213
529
|
]);
|
|
214
|
-
// Determine if last
|
|
215
|
-
const
|
|
216
|
-
.filter((item) => item.type === '
|
|
217
|
-
.map((item) => (item.type === '
|
|
530
|
+
// Determine if last activity group is streaming
|
|
531
|
+
const lastActivityGroup = contentItems
|
|
532
|
+
.filter((item) => item.type === 'activity')
|
|
533
|
+
.map((item) => (item.type === 'activity' ? item.activityGroup : null))
|
|
218
534
|
.filter((g) => g !== null)
|
|
219
535
|
.pop();
|
|
220
536
|
// Check if we're still streaming overall and waiting for text
|
|
221
537
|
const isStreamingOverall = isStreaming && isLast && !hasFinalAssistantText;
|
|
222
|
-
// For the last
|
|
538
|
+
// For the last activity group, also keep it open if we just finished streaming
|
|
223
539
|
// but text hasn't appeared yet (grace period for AI to start responding)
|
|
224
540
|
const shouldKeepLastGroupOpen = isLast && !hasFinalAssistantText && message.role === 'assistant';
|
|
225
|
-
// Auto-open/close
|
|
541
|
+
// Auto-open/close activity groups based on streaming state
|
|
226
542
|
useEffect(() => {
|
|
227
|
-
|
|
543
|
+
setActivityGroupStates((prevStates) => {
|
|
228
544
|
let hasChanges = false;
|
|
229
545
|
const nextStates = new Map(prevStates);
|
|
230
546
|
contentItems.forEach((item) => {
|
|
231
|
-
if (item.type === '
|
|
232
|
-
const groupId = item.
|
|
233
|
-
const group = item.
|
|
234
|
-
const isLastGroup =
|
|
547
|
+
if (item.type === 'activity' && item.activityGroup) {
|
|
548
|
+
const groupId = item.activityGroup.id;
|
|
549
|
+
const group = item.activityGroup;
|
|
550
|
+
const isLastGroup = lastActivityGroup?.id === groupId;
|
|
235
551
|
const currentState = nextStates.get(groupId);
|
|
236
552
|
const shouldBeOpen = group.isStreaming ||
|
|
237
553
|
(isLastGroup && isStreamingOverall) ||
|
|
@@ -262,58 +578,73 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
262
578
|
}, [
|
|
263
579
|
contentItems,
|
|
264
580
|
isStreamingOverall,
|
|
265
|
-
|
|
581
|
+
lastActivityGroup,
|
|
266
582
|
shouldKeepLastGroupOpen,
|
|
267
583
|
]);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
584
|
+
const renderContentItem = (item) => {
|
|
585
|
+
if (item.type === 'activity' &&
|
|
586
|
+
item.activityGroup &&
|
|
587
|
+
message.role === 'assistant') {
|
|
588
|
+
const group = item.activityGroup;
|
|
589
|
+
const groupState = activityGroupStates.get(group.id);
|
|
590
|
+
const isLastGroup = lastActivityGroup?.id === group.id;
|
|
591
|
+
const isGroupStreaming = group.isStreaming || (isLastGroup && isStreamingOverall);
|
|
592
|
+
const shouldBeOpenByDefault = isGroupStreaming;
|
|
593
|
+
const isOpen = groupState?.isOpen ?? shouldBeOpenByDefault;
|
|
594
|
+
const titleForTrigger = group.title || DEFAULT_ACTIONS_LABEL;
|
|
595
|
+
return (_jsxs(Collapsible, { open: isOpen, onOpenChange: (open) => {
|
|
596
|
+
setActivityGroupStates((prev) => {
|
|
597
|
+
const next = new Map(prev);
|
|
598
|
+
const current = next.get(group.id) ?? {
|
|
599
|
+
isOpen: false,
|
|
600
|
+
userOpened: false,
|
|
601
|
+
};
|
|
602
|
+
next.set(group.id, {
|
|
603
|
+
...current,
|
|
604
|
+
isOpen: open,
|
|
605
|
+
userOpened: open ? true : current.userOpened,
|
|
606
|
+
});
|
|
607
|
+
return next;
|
|
608
|
+
});
|
|
609
|
+
}, className: "mb-1 w-full", "data-ai-reasoning": true, children: [_jsxs(CollapsibleTrigger, { className: cn('group flex max-w-full cursor-pointer items-center gap-1 py-0.5 text-[13px] font-normal leading-5 transition-colors', 'text-muted-foreground/75 hover:text-foreground/80 min-w-0'), children: [_jsx("span", { className: "flex-1 truncate", children: titleForTrigger }), _jsx(ChevronDown, { className: cn('size-3 shrink-0 opacity-0 transition-all duration-200 group-hover:opacity-70', isOpen && 'rotate-180 opacity-50') })] }), _jsx(CollapsibleContent, { className: "text-muted-foreground/75 min-w-0 overflow-hidden pb-1.5 pl-3 pt-0 text-[13px] leading-5", children: _jsx("div", { className: "max-h-28 space-y-0.5 overflow-y-auto pr-2", children: group.entries.map((entry) => (_jsx("div", { className: cn('min-w-0 overflow-hidden truncate', entry.isError && 'text-destructive'), children: entry.content }, entry.id))) }) })] }, item.id));
|
|
610
|
+
}
|
|
611
|
+
if (item.type === 'inline-tool') {
|
|
612
|
+
return (_jsx("div", { className: "w-full min-w-0", children: item.content }, item.id));
|
|
613
|
+
}
|
|
614
|
+
if (item.type === 'file' && item.filePart) {
|
|
615
|
+
const isImage = item.filePart.mediaType.startsWith('image/');
|
|
616
|
+
const label = item.filePart.filename || 'Attached image';
|
|
617
|
+
return (_jsxs("div", { className: cn('bg-secondary text-secondary-foreground flex w-full items-center gap-3 rounded-2xl px-3 py-2.5', message.role !== 'user' && 'bg-muted text-foreground'), children: [_jsx("div", { className: "bg-background flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-lg border", children: isImage && item.filePart.previewUrl ? (_jsx("img", { src: item.filePart.previewUrl, alt: "", className: "size-full object-cover", draggable: false })) : (_jsx(ImageIcon, { className: "text-muted-foreground size-5" })) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm font-medium", children: label }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: item.filePart.mediaType })] })] }, item.id));
|
|
618
|
+
}
|
|
619
|
+
if (item.type === 'text' && item.textPart) {
|
|
620
|
+
if (message.role === 'user') {
|
|
621
|
+
return (_jsx("div", { className: "flex w-full justify-end", children: _jsx("div", { className: "bg-foreground/5 text-foreground max-w-[77%] break-words rounded-2xl px-3 py-2 text-[13px] leading-5", children: _jsx("div", { className: "whitespace-pre-wrap break-words", children: item.textPart.text }) }) }, item.id));
|
|
622
|
+
}
|
|
623
|
+
return (_jsx("div", { className: cn('flex min-w-0 flex-col gap-4', {
|
|
624
|
+
'w-full': message.role === 'assistant',
|
|
625
|
+
}), children: _jsx(Markdown, { markdown: item.textPart.text, proseSize: "sm", className: cn('[&_.prose]:text-[13px] [&_.prose]:leading-[21px] [&_.prose]:text-current', '[&_.prose_pre]:bg-background/80 [&_.prose_code]:text-current', '[&_.prose_p:last-child]:mb-0 [&_.prose_p]:mb-2', '[&_.prose_ol:first-child]:mt-0 [&_.prose_ul:first-child]:mt-0', '[&_.prose>*:last-child]:mb-0') }) }, item.id));
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
628
|
+
};
|
|
629
|
+
const finalTextItemIndex = message.role === 'assistant'
|
|
630
|
+
? contentItems.reduce((lastIndex, item, index) => item.type === 'text' ? index : lastIndex, -1)
|
|
631
|
+
: -1;
|
|
632
|
+
const shouldCollapsePreAnswerWork = message.role === 'assistant' &&
|
|
633
|
+
hasFinalAssistantText &&
|
|
634
|
+
finalTextItemIndex > 0;
|
|
635
|
+
const preAnswerWorkItems = shouldCollapsePreAnswerWork
|
|
636
|
+
? contentItems.slice(0, finalTextItemIndex)
|
|
637
|
+
: [];
|
|
638
|
+
const finalAnswerItems = shouldCollapsePreAnswerWork
|
|
639
|
+
? contentItems.slice(finalTextItemIndex)
|
|
640
|
+
: contentItems;
|
|
641
|
+
const isTurnStillWorking = isLast &&
|
|
642
|
+
isStreaming &&
|
|
643
|
+
message.role === 'assistant' &&
|
|
644
|
+
!message.turn?.completedAtMs;
|
|
645
|
+
return (_jsx(AnimatePresence, { children: _jsx(motion.div, { "data-testid": `message-${message.role}`, className: "group/message w-full min-w-0 px-2", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1 }, "data-role": message.role, children: _jsx("div", { className: "flex w-full min-w-0 items-start gap-4", children: _jsxs("div", { className: "mt-1 flex w-full min-w-0 flex-col gap-2", children: [shouldCollapsePreAnswerWork && (_jsx(WorkSummary, { messageId: message.id, startedAtMs: message.turn?.startedAtMs, completedAtMs: message.turn?.completedAtMs, isWorking: isTurnStillWorking, children: _jsx("div", { className: "flex min-w-0 flex-col gap-2", children: preAnswerWorkItems.map(renderContentItem) }) })), finalAnswerItems.map(renderContentItem)] }) }) }) }));
|
|
315
646
|
}
|
|
316
647
|
export const Message = memo(PureMessage);
|
|
317
648
|
export function ThinkingMessage() {
|
|
318
|
-
return (_jsx(motion.div, { "data-testid": "message-assistant-loading", className: "group/message w-full px-2", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 0.5 } }, "data-role": "assistant", children:
|
|
649
|
+
return (_jsx(motion.div, { "data-testid": "message-assistant-loading", className: "group/message w-full px-2", initial: { y: 5, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 0.5 } }, "data-role": "assistant", children: _jsx("div", { className: "text-muted-foreground/75 px-2 text-[13px] leading-5", children: _jsx("span", { className: "animate-pulse select-none", children: "Thinking" }) }) }));
|
|
319
650
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -71,8 +71,20 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
71
71
|
return () => clearTimeout(timer);
|
|
72
72
|
}
|
|
73
73
|
}, [isExpanded]);
|
|
74
|
+
const showMissingApiKeyPrompt = missingApiKey ||
|
|
75
|
+
(error &&
|
|
76
|
+
(error.message?.includes('API_KEY not configured') ||
|
|
77
|
+
error.message?.includes('credential required in Vault') ||
|
|
78
|
+
error.message?.includes('credential not configured in Vault')));
|
|
79
|
+
const submitDisabled = Boolean(showMissingApiKeyPrompt);
|
|
74
80
|
const handleSubmit = useCallback((e, mode) => {
|
|
75
81
|
e?.preventDefault();
|
|
82
|
+
if (submitDisabled) {
|
|
83
|
+
if (!isExpanded) {
|
|
84
|
+
onExpandedChange(true);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
76
88
|
if (!input.trim() && imageAttachments.length === 0)
|
|
77
89
|
return;
|
|
78
90
|
const submitMode = mode ?? (isResponding ? 'steer' : 'send');
|
|
@@ -94,6 +106,7 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
94
106
|
onExpandedChange,
|
|
95
107
|
onSubmit,
|
|
96
108
|
onStop,
|
|
109
|
+
submitDisabled,
|
|
97
110
|
]);
|
|
98
111
|
const handleNewChat = useCallback(() => {
|
|
99
112
|
onNewChat?.();
|
|
@@ -136,11 +149,6 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
136
149
|
}, [isExpanded, onExpandedChange, onMinimizedChange]);
|
|
137
150
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
138
151
|
const isThinking = useMemo(() => computeIsThinking(messages, status), [messages, status]);
|
|
139
|
-
const showMissingApiKeyPrompt = missingApiKey ||
|
|
140
|
-
(error &&
|
|
141
|
-
(error.message?.includes('API_KEY not configured') ||
|
|
142
|
-
error.message?.includes('credential required in Vault') ||
|
|
143
|
-
error.message?.includes('credential not configured in Vault')));
|
|
144
152
|
const showErrorMessage = Boolean(error) && status === 'error' && !showMissingApiKeyPrompt;
|
|
145
153
|
const lastMessageId = messages[messages.length - 1]?.id;
|
|
146
154
|
const listItems = useMemo(() => {
|
|
@@ -174,10 +182,10 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
174
182
|
return (_jsx("div", { className: "px-2 pb-4", children: messages.length === 0 && welcomeMessage && (_jsx("div", { className: "bg-muted/50 text-muted-foreground mx-2 rounded-2xl p-4 text-sm", children: welcomeMessage })) }));
|
|
175
183
|
case 'message': {
|
|
176
184
|
const checkpoint = checkpoints?.get(item.message.id);
|
|
177
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
185
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(MessageRow, { message: item.message, isLast: item.message.id === lastMessageId, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }) }));
|
|
178
186
|
}
|
|
179
187
|
case 'thinking':
|
|
180
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
188
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(ThinkingMessage, {}) }));
|
|
181
189
|
case 'missing-api-key':
|
|
182
190
|
return (_jsx("div", { className: "px-2 pb-4", children: _jsxs("div", { className: "border-primary/30 bg-primary/5 mx-2 flex flex-col items-center gap-3 rounded-lg border p-4 text-center", children: [_jsx("div", { className: "bg-primary/10 flex size-10 items-center justify-center rounded-full", children: _jsx(AlertCircle, { className: "text-primary size-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground font-medium", children: "API key required" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: "Add an API key to Vault to start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] }) }));
|
|
183
191
|
case 'error':
|
|
@@ -267,5 +275,5 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
|
|
|
267
275
|
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Steer active response with queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Steer" })] })), onRemoveQueuedMessage && (_jsx("button", { type: "button", onClick: (event) => {
|
|
268
276
|
event.stopPropagation();
|
|
269
277
|
onRemoveQueuedMessage(message.id);
|
|
270
|
-
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Remove queued follow-up", children: _jsx(X, { className: "size-3.5" }) }))] }, message.id))) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null })] })] }) })] }));
|
|
278
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Remove queued follow-up", children: _jsx(X, { className: "size-3.5" }) }))] }, message.id))) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null, submitDisabled: submitDisabled })] })] }) })] }));
|
|
271
279
|
}
|
|
@@ -142,9 +142,9 @@ const components = {
|
|
|
142
142
|
return _jsx(CodeBlock, { code: codeContent, language: language });
|
|
143
143
|
}
|
|
144
144
|
// Inline code - style nicely, remove prose backticks via before:/after:content-none
|
|
145
|
-
return (_jsx("code", { className: "bg-muted text-foreground rounded
|
|
145
|
+
return (_jsx("code", { className: "bg-muted/70 text-foreground rounded px-1 py-0.5 font-mono text-[0.92em] before:content-none after:content-none", ...props, children: children }));
|
|
146
146
|
},
|
|
147
|
-
a: ({ children, href, ...props }) => (_jsx("a", { className: "text-primary break-all font-
|
|
147
|
+
a: ({ children, href, ...props }) => (_jsx("a", { className: "text-primary break-all font-medium hover:underline", href: href ?? '#', target: "_blank", rel: "noreferrer", ...props, children: children })),
|
|
148
148
|
// Process @mentions in text and paragraph nodes
|
|
149
149
|
p: ({ children, ...props }) => {
|
|
150
150
|
const processedChildren = React.Children.map(children, (child) => {
|
|
@@ -2,11 +2,12 @@ import * as React from 'react';
|
|
|
2
2
|
import { Dialog } from './dialog';
|
|
3
3
|
import { Command as CommandPrimitive } from 'cmdk';
|
|
4
4
|
declare function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
declare function CommandDialog({ title, description, children, className, showCloseButton, ...props }: React.ComponentProps<typeof Dialog> & {
|
|
5
|
+
declare function CommandDialog({ title, description, children, className, showCloseButton, filter, ...props }: React.ComponentProps<typeof Dialog> & {
|
|
6
6
|
title?: string;
|
|
7
7
|
description?: string;
|
|
8
8
|
className?: string;
|
|
9
9
|
showCloseButton?: boolean;
|
|
10
|
+
filter?: React.ComponentProps<typeof CommandPrimitive>['filter'];
|
|
10
11
|
}): import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
declare function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>): import("react/jsx-runtime").JSX.Element;
|
|
12
13
|
declare function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/components/ui/command.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EACL,MAAM,EAKP,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAElD,iBAAS,OAAO,CAAC,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,2CAW/C;AAED,iBAAS,aAAa,CAAC,EACrB,KAAyB,EACzB,WAA8C,EAC9C,QAAQ,EACR,SAAS,EACT,eAAsB,EACtB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/components/ui/command.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EACL,MAAM,EAKP,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAA;AAElD,iBAAS,OAAO,CAAC,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,2CAW/C;AAED,iBAAS,aAAa,CAAC,EACrB,KAAyB,EACzB,WAA8C,EAC9C,QAAQ,EACR,SAAS,EACT,eAAsB,EACtB,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAA;CACjE,2CAoBA;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAiBrD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAQrD;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,2CAWrD;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,2CAQzD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,2CAWpD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,2CAW9B;AAED,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,CAAA"}
|
|
@@ -7,8 +7,8 @@ import { Command as CommandPrimitive } from 'cmdk';
|
|
|
7
7
|
function Command({ className, ...props }) {
|
|
8
8
|
return (_jsx(CommandPrimitive, { "data-slot": "command", className: cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', className), ...props }));
|
|
9
9
|
}
|
|
10
|
-
function CommandDialog({ title = 'Command Palette', description = 'Search for a command to run...', children, className, showCloseButton = true, ...props }) {
|
|
11
|
-
return (_jsxs(Dialog, { ...props, children: [_jsxs(DialogHeader, { className: "sr-only", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsx(DialogContent, { className: cn('overflow-hidden p-0', className), showCloseButton: showCloseButton, children: _jsx(Command, { className: "[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children: children }) })] }));
|
|
10
|
+
function CommandDialog({ title = 'Command Palette', description = 'Search for a command to run...', children, className, showCloseButton = true, filter, ...props }) {
|
|
11
|
+
return (_jsxs(Dialog, { ...props, children: [_jsxs(DialogHeader, { className: "sr-only", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsx(DialogContent, { className: cn('overflow-hidden p-0', className), showCloseButton: showCloseButton, children: _jsx(Command, { filter: filter, className: "[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", children: children }) })] }));
|
|
12
12
|
}
|
|
13
13
|
function CommandInput({ className, ...props }) {
|
|
14
14
|
return (_jsxs("div", { "data-slot": "command-input-wrapper", className: "flex h-9 items-center gap-2 border-b px-3", children: [_jsx(SearchIcon, { className: "size-4 shrink-0 opacity-50" }), _jsx(CommandPrimitive.Input, { "data-slot": "command-input", className: cn('placeholder:text-muted-foreground outline-hidden flex h-10 w-full rounded-md bg-transparent py-3 text-sm disabled:cursor-not-allowed disabled:opacity-50', className), ...props })] }));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { cn } from './lib/utils';
|
|
2
2
|
export { ThemeProvider, useTheme, themeScript, type Theme } from './lib/theme';
|
|
3
3
|
export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
|
|
4
|
-
export { useMoldableCommands, useMoldableCommand, isInMoldable, sendToMoldable, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type DownloadFileOptions, } from './lib/commands';
|
|
4
|
+
export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, type AppCommand, type CommandAction, type CommandsResponse, type CommandMessage, type DownloadFileOptions, } from './lib/commands';
|
|
5
5
|
export * from './components/ui';
|
|
6
6
|
export { useIsMobile } from './hooks/use-mobile';
|
|
7
7
|
export { Markdown } from './components/markdown';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAGzD,cAAc,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAA;AAG9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,gBAAgB,CAAA;AAGvB,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAGzD,cAAc,mBAAmB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { ThemeProvider, useTheme, themeScript } from './lib/theme';
|
|
|
5
5
|
// Export workspace
|
|
6
6
|
export { WorkspaceProvider, useWorkspace, WORKSPACE_HEADER, } from './lib/workspace';
|
|
7
7
|
// Export commands
|
|
8
|
-
export { useMoldableCommands, useMoldableCommand, isInMoldable, sendToMoldable, downloadFile, } from './lib/commands';
|
|
8
|
+
export { useMoldableCommands, useMoldableCommand, useMoldableAppCommands, isInMoldable, sendToMoldable, downloadFile, } from './lib/commands';
|
|
9
9
|
// Export UI components
|
|
10
10
|
export * from './components/ui';
|
|
11
11
|
// Export hooks
|
package/dist/lib/commands.d.ts
CHANGED
|
@@ -70,6 +70,14 @@ export interface CommandMessage {
|
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
export declare function useMoldableCommands(handlers: Record<string, (payload?: unknown) => void>): void;
|
|
73
|
+
/**
|
|
74
|
+
* Publish the commands that make sense for the app's current view.
|
|
75
|
+
*
|
|
76
|
+
* Apps should call this with a stable, memoized command list. The desktop
|
|
77
|
+
* command menu will prefer these commands over the app's static
|
|
78
|
+
* /api/moldable/commands response while the app is running.
|
|
79
|
+
*/
|
|
80
|
+
export declare function useMoldableAppCommands(appId: string, commands: AppCommand[]): void;
|
|
73
81
|
/**
|
|
74
82
|
* Hook to register a single command handler
|
|
75
83
|
* Useful when you want to register handlers in different components
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/lib/commands.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAErC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,IAAI,CAAC,EAAE,KAAK,CAAA;IACZ,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,SAAS,CAAC,EAAE,mBAAmB,CAAA;IAC/B,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,MAAM,EAAE,aAAa,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,UAAU,EAAE,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC,QAwBtD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,QAgBrC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,QAMA;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,mBAAmB,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,4BAA4B,CAAC,CAAC,GAAG,OAAO;IACvD,IAAI,EAAE,0BAA0B,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,CAAC,EAAE,CAAC,CAAA;IACV,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,CAAA;CACF;AAUD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,GAAG,OAAO,EACzC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,EAChB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,CAAC,CAAC,CAiDZ;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqExE"}
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/lib/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/commands.js
CHANGED
|
@@ -33,6 +33,24 @@ export function useMoldableCommands(handlers) {
|
|
|
33
33
|
return () => window.removeEventListener('message', handleMessage);
|
|
34
34
|
}, []);
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Publish the commands that make sense for the app's current view.
|
|
38
|
+
*
|
|
39
|
+
* Apps should call this with a stable, memoized command list. The desktop
|
|
40
|
+
* command menu will prefer these commands over the app's static
|
|
41
|
+
* /api/moldable/commands response while the app is running.
|
|
42
|
+
*/
|
|
43
|
+
export function useMoldableAppCommands(appId, commands) {
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!isInMoldable())
|
|
46
|
+
return;
|
|
47
|
+
window.parent.postMessage({
|
|
48
|
+
type: 'moldable:set-app-commands',
|
|
49
|
+
appId,
|
|
50
|
+
commands,
|
|
51
|
+
}, '*');
|
|
52
|
+
}, [appId, commands]);
|
|
53
|
+
}
|
|
36
54
|
/**
|
|
37
55
|
* Hook to register a single command handler
|
|
38
56
|
* Useful when you want to register handlers in different components
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moldable-ai/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Shared UI components for Moldable applications",
|
|
5
5
|
"author": "Desiderata LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
"src/styles",
|
|
34
34
|
"LICENSE"
|
|
35
35
|
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"dev": "tsc --watch",
|
|
39
|
+
"lint": "eslint . --ext .ts,.tsx --max-warnings 0",
|
|
40
|
+
"format": "prettier --write . --ignore-path ../../.prettierignore",
|
|
41
|
+
"check-types": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run"
|
|
43
|
+
},
|
|
36
44
|
"dependencies": {
|
|
37
45
|
"@base-ui/react": "^1.1.0",
|
|
38
46
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -89,26 +97,18 @@
|
|
|
89
97
|
"zod": "^4.3.5"
|
|
90
98
|
},
|
|
91
99
|
"devDependencies": {
|
|
100
|
+
"@moldable-ai/eslint-config": "workspace:*",
|
|
101
|
+
"@moldable-ai/prettier-config": "workspace:*",
|
|
102
|
+
"@moldable-ai/typescript-config": "workspace:*",
|
|
92
103
|
"@types/node": "^24.0.3",
|
|
93
104
|
"@types/react": "^19.1.6",
|
|
94
105
|
"@types/react-dom": "^19.1.6",
|
|
95
106
|
"jsdom": "^26.1.0",
|
|
96
107
|
"typescript": "^5.8.3",
|
|
97
|
-
"vitest": "^3.2.4"
|
|
98
|
-
"@moldable-ai/typescript-config": "0.1.3",
|
|
99
|
-
"@moldable-ai/eslint-config": "0.1.4",
|
|
100
|
-
"@moldable-ai/prettier-config": "0.1.3"
|
|
108
|
+
"vitest": "^3.2.4"
|
|
101
109
|
},
|
|
102
110
|
"peerDependencies": {
|
|
103
111
|
"react": "^19.0.0",
|
|
104
112
|
"react-dom": "^19.0.0"
|
|
105
|
-
},
|
|
106
|
-
"scripts": {
|
|
107
|
-
"build": "tsc",
|
|
108
|
-
"dev": "tsc --watch",
|
|
109
|
-
"lint": "eslint . --ext .ts,.tsx --max-warnings 0",
|
|
110
|
-
"format": "prettier --write . --ignore-path ../../.prettierignore",
|
|
111
|
-
"check-types": "tsc --noEmit",
|
|
112
|
-
"test": "vitest run"
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
}
|