@moldable-ai/ui 0.2.8 → 0.2.9
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/app-error-boundary.d.ts +26 -0
- package/dist/components/app-error-boundary.d.ts.map +1 -0
- package/dist/components/app-error-boundary.js +87 -0
- package/dist/components/chat/chat-input.d.ts +8 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-input.js +6 -3
- package/dist/components/chat/chat-message.d.ts +1 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +100 -12
- package/dist/components/chat/chat-messages.d.ts +2 -1
- package/dist/components/chat/chat-messages.d.ts.map +1 -1
- package/dist/components/chat/chat-messages.js +3 -3
- package/dist/components/chat/chat-panel.d.ts +25 -3
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +158 -42
- package/dist/components/chat/index.d.ts +3 -3
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/model-selector.d.ts.map +1 -1
- package/dist/components/chat/model-selector.js +1 -1
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/chat/tool-handlers.js +89 -1
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +16 -2
- package/dist/components/rich-media-player.d.ts +8 -0
- package/dist/components/rich-media-player.d.ts.map +1 -0
- package/dist/components/rich-media-player.js +99 -0
- package/dist/components/ui/resizable.d.ts +4 -1
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/resizable.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/package.json +4 -1
- package/src/styles/index.css +25 -0
- package/dist/lib/commands.test.d.ts +0 -2
- package/dist/lib/commands.test.d.ts.map +0 -1
- package/dist/lib/commands.test.js +0 -111
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
|
2
|
+
type ErrorSource = 'react' | 'window' | 'promise';
|
|
3
|
+
interface AppErrorBoundaryProps {
|
|
4
|
+
appName: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
surface?: 'app' | 'widget';
|
|
7
|
+
}
|
|
8
|
+
interface AppErrorBoundaryState {
|
|
9
|
+
error: Error | null;
|
|
10
|
+
source: ErrorSource;
|
|
11
|
+
componentStack: string | null;
|
|
12
|
+
}
|
|
13
|
+
export declare class AppErrorBoundary extends Component<AppErrorBoundaryProps, AppErrorBoundaryState> {
|
|
14
|
+
state: AppErrorBoundaryState;
|
|
15
|
+
static getDerivedStateFromError(error: Error): Partial<AppErrorBoundaryState>;
|
|
16
|
+
componentDidMount(): void;
|
|
17
|
+
componentWillUnmount(): void;
|
|
18
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
19
|
+
private handleWindowError;
|
|
20
|
+
private handleUnhandledRejection;
|
|
21
|
+
private reload;
|
|
22
|
+
private askMoldable;
|
|
23
|
+
render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=app-error-boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-error-boundary.d.ts","sourceRoot":"","sources":["../../src/components/app-error-boundary.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjE,KAAK,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEjD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,SAAS,CAAA;IACnB,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;CAC3B;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,WAAW,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAiCD,qBAAa,gBAAiB,SAAQ,SAAS,CAC7C,qBAAqB,EACrB,qBAAqB,CACtB;IACC,KAAK,EAAE,qBAAqB,CAI3B;IAED,MAAM,CAAC,wBAAwB,CAC7B,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,qBAAqB,CAAC;IAIjC,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAU5B,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKtD,OAAO,CAAC,iBAAiB,CAKxB;IAED,OAAO,CAAC,wBAAwB,CAI/B;IAED,OAAO,CAAC,MAAM,CAEb;IAED,OAAO,CAAC,WAAW,CAclB;IAED,MAAM;CA8CP"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { MessageSquareWarning, RefreshCw } from 'lucide-react';
|
|
4
|
+
import { Component } from 'react';
|
|
5
|
+
import { Button } from './ui/button';
|
|
6
|
+
function toError(value) {
|
|
7
|
+
if (value instanceof Error)
|
|
8
|
+
return value;
|
|
9
|
+
if (typeof value === 'string')
|
|
10
|
+
return new Error(value);
|
|
11
|
+
try {
|
|
12
|
+
return new Error(JSON.stringify(value));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return new Error(String(value));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function formatError(error, source, componentStack) {
|
|
19
|
+
const parts = [`${error.name}: ${error.message}`];
|
|
20
|
+
if (error.stack) {
|
|
21
|
+
parts.push(error.stack);
|
|
22
|
+
}
|
|
23
|
+
if (componentStack) {
|
|
24
|
+
parts.push(`React component stack:${componentStack}`);
|
|
25
|
+
}
|
|
26
|
+
parts.push(`Source: ${source}`);
|
|
27
|
+
return parts.join('\n\n');
|
|
28
|
+
}
|
|
29
|
+
export class AppErrorBoundary extends Component {
|
|
30
|
+
state = {
|
|
31
|
+
error: null,
|
|
32
|
+
source: 'react',
|
|
33
|
+
componentStack: null,
|
|
34
|
+
};
|
|
35
|
+
static getDerivedStateFromError(error) {
|
|
36
|
+
return { error, source: 'react' };
|
|
37
|
+
}
|
|
38
|
+
componentDidMount() {
|
|
39
|
+
if (typeof window === 'undefined')
|
|
40
|
+
return;
|
|
41
|
+
window.addEventListener('error', this.handleWindowError);
|
|
42
|
+
window.addEventListener('unhandledrejection', this.handleUnhandledRejection);
|
|
43
|
+
}
|
|
44
|
+
componentWillUnmount() {
|
|
45
|
+
if (typeof window === 'undefined')
|
|
46
|
+
return;
|
|
47
|
+
window.removeEventListener('error', this.handleWindowError);
|
|
48
|
+
window.removeEventListener('unhandledrejection', this.handleUnhandledRejection);
|
|
49
|
+
}
|
|
50
|
+
componentDidCatch(error, info) {
|
|
51
|
+
console.error(`${this.props.appName} UI crashed:`, error, info);
|
|
52
|
+
this.setState({ componentStack: info.componentStack ?? null });
|
|
53
|
+
}
|
|
54
|
+
handleWindowError = (event) => {
|
|
55
|
+
const error = event.error instanceof Error ? event.error : new Error(event.message);
|
|
56
|
+
console.error(`${this.props.appName} uncaught frontend error:`, error);
|
|
57
|
+
this.setState({ error, source: 'window', componentStack: null });
|
|
58
|
+
};
|
|
59
|
+
handleUnhandledRejection = (event) => {
|
|
60
|
+
const error = toError(event.reason);
|
|
61
|
+
console.error(`${this.props.appName} unhandled promise rejection:`, error);
|
|
62
|
+
this.setState({ error, source: 'promise', componentStack: null });
|
|
63
|
+
};
|
|
64
|
+
reload = () => {
|
|
65
|
+
window.location.reload();
|
|
66
|
+
};
|
|
67
|
+
askMoldable = () => {
|
|
68
|
+
const { error, source, componentStack } = this.state;
|
|
69
|
+
if (!error)
|
|
70
|
+
return;
|
|
71
|
+
const surfaceLabel = this.props.surface === 'widget' ? ' widget' : ' full app';
|
|
72
|
+
const errorReport = formatError(error, source, componentStack);
|
|
73
|
+
const text = [
|
|
74
|
+
`${this.props.appName}${surfaceLabel} hit an uncaught frontend error.`,
|
|
75
|
+
'Please inspect and fix the client crash.',
|
|
76
|
+
errorReport,
|
|
77
|
+
].join('\n\n');
|
|
78
|
+
window.parent.postMessage({ type: 'moldable:set-chat-input', text }, '*');
|
|
79
|
+
};
|
|
80
|
+
render() {
|
|
81
|
+
const { error, source, componentStack } = this.state;
|
|
82
|
+
if (!error)
|
|
83
|
+
return this.props.children;
|
|
84
|
+
const errorSummary = formatError(error, source, componentStack);
|
|
85
|
+
return (_jsx("main", { className: "bg-background text-foreground flex min-h-screen items-center justify-center p-4 sm:p-6", children: _jsxs("section", { className: "border-border bg-card w-full max-w-xl rounded-lg border p-5 shadow-lg sm:p-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("h1", { className: "text-xl font-semibold tracking-tight", children: [this.props.appName, " hit a UI error"] }), _jsx("p", { className: "text-muted-foreground text-sm leading-6", children: "Reload to recover, or send the captured client error to Moldable so it can inspect the crash." })] }), _jsx("pre", { className: "bg-muted text-muted-foreground mt-5 max-h-44 overflow-auto whitespace-pre-wrap rounded-md border p-3 font-mono text-xs", children: errorSummary }), _jsxs("div", { className: "mt-5 flex flex-col gap-2 sm:flex-row", children: [_jsxs(Button, { type: "button", className: "cursor-pointer", onClick: this.reload, children: [_jsx(RefreshCw, {}), "Reload"] }), _jsxs(Button, { type: "button", variant: "secondary", className: "cursor-pointer", onClick: this.askMoldable, children: [_jsx(MessageSquareWarning, {}), "Ask Moldable to fix this"] })] })] }) }));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ChangeEvent, FormEvent, ReactNode, RefObject } from 'react';
|
|
2
2
|
export type ChatSubmitMode = 'send' | 'steer' | 'queue';
|
|
3
|
+
export type ChatSlashCommandOption = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
};
|
|
3
8
|
export type ChatImageAttachment = {
|
|
4
9
|
id: string;
|
|
5
10
|
name: string;
|
|
@@ -21,7 +26,9 @@ type ChatInputProps = {
|
|
|
21
26
|
footerStartControls?: ReactNode;
|
|
22
27
|
footerControls?: ReactNode;
|
|
23
28
|
submitDisabled?: boolean;
|
|
29
|
+
slashCommands?: ChatSlashCommandOption[];
|
|
30
|
+
onSlashCommandSelect?: (command: ChatSlashCommandOption) => void;
|
|
24
31
|
};
|
|
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;
|
|
32
|
+
export declare function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder, inputRef, onStop, compact, imageAttachments, onRemoveImageAttachment, footerStartControls, footerControls, submitDisabled, slashCommands, onSlashCommandSelect, }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
26
33
|
export {};
|
|
27
34
|
//# 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;IAC1B,cAAc,CAAC,EAAE,OAAO,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,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,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;IACxB,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACxC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACjE,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,EACtB,aAAkB,EAClB,oBAAoB,GACrB,EAAE,cAAc,2CA2KhB"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
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, submitDisabled = false, }) {
|
|
6
|
+
export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeholder = 'Ask anything...', inputRef, onStop, compact = false, imageAttachments = [], onRemoveImageAttachment, footerStartControls, footerControls, submitDisabled = false, slashCommands = [], onSlashCommandSelect, }) {
|
|
7
7
|
const hasInput = input.trim().length > 0;
|
|
8
8
|
const hasAttachments = imageAttachments.length > 0;
|
|
9
9
|
const canSubmit = !submitDisabled && (hasInput || hasAttachments);
|
|
@@ -47,7 +47,10 @@ export function ChatInput({ input, onInputChange, onSubmit, isResponding, placeh
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
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 &&
|
|
50
|
+
}, children: _jsxs("div", { className: cn('bg-background relative flex w-full flex-col rounded-3xl border', compact && !hasAttachments && 'h-14'), children: [!compact && slashCommands.length > 0 && (_jsx("div", { className: "border-border bg-popover text-popover-foreground absolute bottom-full left-4 right-4 z-20 mb-2 overflow-hidden rounded-lg border shadow-lg", children: slashCommands.map((command) => (_jsxs("button", { type: "button", className: "hover:bg-accent hover:text-accent-foreground flex min-h-12 w-full cursor-pointer items-center gap-3 px-3 py-2.5 text-left transition-colors", onMouseDown: (event) => {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
onSlashCommandSelect?.(command);
|
|
53
|
+
}, children: [_jsxs("span", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: ["/", command.name] }), _jsx("span", { className: "min-w-0 text-xs", children: command.description })] }, command.id))) })), !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-0.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
|
|
51
54
|
? 'Stop generating'
|
|
52
55
|
: isResponding
|
|
53
56
|
? 'Steer response'
|
|
@@ -41,5 +41,6 @@ type MessageProps = {
|
|
|
41
41
|
declare function PureMessage({ message, isLast, isStreaming, }: MessageProps): import("react/jsx-runtime").JSX.Element;
|
|
42
42
|
export declare const Message: import("react").MemoExoticComponent<typeof PureMessage>;
|
|
43
43
|
export declare function ThinkingMessage(): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export declare function CompactingMessage(): import("react/jsx-runtime").JSX.Element;
|
|
44
45
|
export {};
|
|
45
46
|
//# sourceMappingURL=chat-message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAsvBD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CAqgBd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAc9B;AAED,wBAAgB,iBAAiB,4CAkBhC"}
|
|
@@ -102,14 +102,32 @@ function getToolTarget(part) {
|
|
|
102
102
|
return (getString(output, ['appName', 'appId', 'name', 'id']) ??
|
|
103
103
|
getString(args, ['appName', 'appId', 'name', 'id', 'repoName', 'skillName']));
|
|
104
104
|
}
|
|
105
|
+
function getAppApiTarget(part) {
|
|
106
|
+
const args = asRecord(part.args);
|
|
107
|
+
const output = asRecord(part.output);
|
|
108
|
+
return (getString(args, ['targetAppId', 'appId']) ??
|
|
109
|
+
getString(output, ['targetAppId', 'appId', 'appName']));
|
|
110
|
+
}
|
|
111
|
+
function getAppApiMethod(part) {
|
|
112
|
+
const args = asRecord(part.args);
|
|
113
|
+
const output = asRecord(part.output);
|
|
114
|
+
return getString(args, ['method']) ?? getString(output, ['method']);
|
|
115
|
+
}
|
|
105
116
|
function isToolLoading(part, progress) {
|
|
106
117
|
const isApprovalResponded = part.state === 'approval-responded' || part.state === 'output-denied';
|
|
107
118
|
const wasApproved = part.approval?.approved === true;
|
|
119
|
+
const hasOutput = part.output !== undefined;
|
|
108
120
|
return (part.state === 'partial-call' ||
|
|
109
121
|
part.state === 'call' ||
|
|
122
|
+
part.state === 'input-streaming' ||
|
|
123
|
+
part.state === 'input-available' ||
|
|
110
124
|
part.state === 'pending' ||
|
|
111
125
|
progress?.status === 'running' ||
|
|
112
|
-
(isApprovalResponded && wasApproved &&
|
|
126
|
+
(isApprovalResponded && wasApproved && !hasOutput) ||
|
|
127
|
+
(!hasOutput &&
|
|
128
|
+
part.state !== 'output-available' &&
|
|
129
|
+
part.state !== 'output-error' &&
|
|
130
|
+
part.state !== 'output-denied'));
|
|
113
131
|
}
|
|
114
132
|
function toolHasError(part) {
|
|
115
133
|
const output = asRecord(part.output);
|
|
@@ -127,7 +145,19 @@ function plural(count, singular, pluralForm = `${singular}s`) {
|
|
|
127
145
|
function sentenceCase(text) {
|
|
128
146
|
return text.length > 0 ? text.charAt(0).toUpperCase() + text.slice(1) : text;
|
|
129
147
|
}
|
|
130
|
-
function
|
|
148
|
+
function formatAppApiSummary(count, labels) {
|
|
149
|
+
if (labels.length === 0) {
|
|
150
|
+
return `called ${plural(count, 'app API', 'app APIs')}`;
|
|
151
|
+
}
|
|
152
|
+
if (count === 1) {
|
|
153
|
+
return `called app API ${labels[0]}`;
|
|
154
|
+
}
|
|
155
|
+
const shown = labels.slice(0, 2);
|
|
156
|
+
const remaining = labels.length - shown.length;
|
|
157
|
+
const labelText = remaining > 0 ? `${shown.join(', ')} +${remaining}` : shown.join(', ');
|
|
158
|
+
return `called ${plural(count, 'app API', 'app APIs')} (${labelText})`;
|
|
159
|
+
}
|
|
160
|
+
function formatActivitySummary(counts, fallback, isStreaming, apiCallLabels = []) {
|
|
131
161
|
const segments = [];
|
|
132
162
|
const completedCommands = Math.max(0, counts.commandCount - counts.runningCommandCount);
|
|
133
163
|
const explorationDetails = [
|
|
@@ -163,7 +193,7 @@ function formatActivitySummary(counts, fallback, isStreaming) {
|
|
|
163
193
|
segments.push(`ran ${plural(counts.webSearchCount, 'web search', 'web searches')}`);
|
|
164
194
|
}
|
|
165
195
|
if (counts.apiCallCount > 0) {
|
|
166
|
-
segments.push(
|
|
196
|
+
segments.push(formatAppApiSummary(counts.apiCallCount, apiCallLabels));
|
|
167
197
|
}
|
|
168
198
|
if (counts.skillActionCount > 0) {
|
|
169
199
|
segments.push(`updated ${plural(counts.skillActionCount, 'skill setting')}`);
|
|
@@ -272,14 +302,44 @@ function summarizeToolActivity(part, toolHandler, progress, fallbackId) {
|
|
|
272
302
|
case 'listMoldableAppApi':
|
|
273
303
|
case 'callMoldableAppApi':
|
|
274
304
|
incrementActivityCount(counts, 'apiCallCount');
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
305
|
+
{
|
|
306
|
+
const apiTarget = getAppApiTarget(part);
|
|
307
|
+
const apiMethod = getAppApiMethod(part);
|
|
308
|
+
const apiSummaryLabel = apiMethod ?? apiTarget;
|
|
309
|
+
const apiTitle = apiMethod && apiTarget
|
|
310
|
+
? `${apiTarget}: ${apiMethod}`
|
|
311
|
+
: apiSummaryLabel;
|
|
312
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running
|
|
313
|
+
? part.toolName === 'listMoldableAppApi'
|
|
314
|
+
? 'Listing app APIs'
|
|
315
|
+
: 'Calling app API'
|
|
316
|
+
: part.toolName === 'listMoldableAppApi'
|
|
317
|
+
? 'Listed app APIs'
|
|
318
|
+
: 'Called app API' }), apiSummaryLabel && (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: apiTitle, children: apiSummaryLabel })] }))] }));
|
|
319
|
+
return {
|
|
320
|
+
entry: {
|
|
321
|
+
id: entryId,
|
|
322
|
+
content: hasError ? (_jsxs(_Fragment, { children: [content, _jsx("span", { children: " failed" })] })) : (content),
|
|
323
|
+
isRunning: running,
|
|
324
|
+
isError: hasError,
|
|
325
|
+
summaryLabel: part.toolName === 'callMoldableAppApi'
|
|
326
|
+
? apiSummaryLabel
|
|
327
|
+
: undefined,
|
|
328
|
+
},
|
|
329
|
+
counts,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
case 'take_screenshot':
|
|
333
|
+
incrementActivityCount(counts, 'toolCallCount');
|
|
334
|
+
content = running ? 'Taking app screenshot' : 'Captured app screenshot';
|
|
282
335
|
break;
|
|
336
|
+
case 'viewImage': {
|
|
337
|
+
incrementActivityCount(counts, 'toolCallCount');
|
|
338
|
+
const imagePath = stringValue(args?.imagePath) ?? stringValue(output?.imagePath);
|
|
339
|
+
const imageLabel = getPathLabel(imagePath);
|
|
340
|
+
content = (_jsxs(_Fragment, { children: [_jsx("span", { children: running ? 'Viewing image' : 'Viewed image' }), imageLabel && (_jsxs(_Fragment, { children: [' ', _jsx(ActivityCode, { title: imagePath, children: imageLabel })] }))] }));
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
283
343
|
case 'listSkillRepos':
|
|
284
344
|
case 'listAvailableSkills':
|
|
285
345
|
case 'syncSkills':
|
|
@@ -378,10 +438,14 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
378
438
|
hasText: false,
|
|
379
439
|
};
|
|
380
440
|
const updateActivityTitle = (group) => {
|
|
381
|
-
group.title = formatActivitySummary(group.counts, group.title, group.isStreaming);
|
|
441
|
+
group.title = formatActivitySummary(group.counts, group.title, group.isStreaming, group.apiCallLabels);
|
|
382
442
|
};
|
|
383
443
|
const addActivity = (group, entry, counts) => {
|
|
384
444
|
group.entries.push(entry);
|
|
445
|
+
if (entry.summaryLabel &&
|
|
446
|
+
!group.apiCallLabels.includes(entry.summaryLabel)) {
|
|
447
|
+
group.apiCallLabels.push(entry.summaryLabel);
|
|
448
|
+
}
|
|
385
449
|
group.isStreaming = group.isStreaming || Boolean(entry.isRunning);
|
|
386
450
|
if (counts) {
|
|
387
451
|
for (const key of Object.keys(counts)) {
|
|
@@ -409,6 +473,7 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
409
473
|
id: `activity-${message.id}-${state.activityGroupIndex}`,
|
|
410
474
|
entries: [],
|
|
411
475
|
counts: emptyActivityCounts(),
|
|
476
|
+
apiCallLabels: [],
|
|
412
477
|
title: DEFAULT_ACTIONS_LABEL,
|
|
413
478
|
isStreaming: false,
|
|
414
479
|
};
|
|
@@ -463,10 +528,30 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
463
528
|
});
|
|
464
529
|
continue;
|
|
465
530
|
}
|
|
466
|
-
const group = ensureActivityGroup();
|
|
467
531
|
const progress = part.toolCallId
|
|
468
532
|
? toolProgress[part.toolCallId]
|
|
469
533
|
: undefined;
|
|
534
|
+
const shouldRenderImageToolInline = part.toolName === 'take_screenshot' ||
|
|
535
|
+
part.toolName === 'generateImage';
|
|
536
|
+
if (shouldRenderImageToolInline && isToolLoading(part, progress)) {
|
|
537
|
+
closeActivityGroup();
|
|
538
|
+
items.push({
|
|
539
|
+
type: 'inline-tool',
|
|
540
|
+
id: key,
|
|
541
|
+
content: toolHandler.renderLoading?.(part.args) ?? null,
|
|
542
|
+
});
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (shouldRenderImageToolInline && part.output !== undefined) {
|
|
546
|
+
closeActivityGroup();
|
|
547
|
+
items.push({
|
|
548
|
+
type: 'inline-tool',
|
|
549
|
+
id: key,
|
|
550
|
+
content: toolHandler.renderOutput(part.output, part.toolCallId || key),
|
|
551
|
+
});
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
const group = ensureActivityGroup();
|
|
470
555
|
const activity = summarizeToolActivity(part, toolHandler, progress, key);
|
|
471
556
|
addActivity(group, activity.entry, activity.counts);
|
|
472
557
|
continue;
|
|
@@ -648,3 +733,6 @@ export const Message = memo(PureMessage);
|
|
|
648
733
|
export function ThinkingMessage() {
|
|
649
734
|
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" }) }) }));
|
|
650
735
|
}
|
|
736
|
+
export function CompactingMessage() {
|
|
737
|
+
return (_jsx(motion.div, { "data-testid": "message-compacting", className: "w-full px-4 py-1", initial: { y: 3, opacity: 0 }, animate: { y: 0, opacity: 1 }, "data-role": "assistant", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "bg-border h-px min-w-8 flex-1" }), _jsx("span", { className: "moldable-chat-shimmer select-none whitespace-nowrap text-[13px] font-medium leading-5", children: "Compacting conversation..." }), _jsx("div", { className: "bg-border h-px min-w-8 flex-1" })] }) }));
|
|
738
|
+
}
|
|
@@ -17,6 +17,7 @@ interface MessagesProps {
|
|
|
17
17
|
restoringMessageId?: string | null;
|
|
18
18
|
/** Callback when restore is requested for a message */
|
|
19
19
|
onRestoreCheckpoint?: (messageId: string) => void;
|
|
20
|
+
isCompacting?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export declare function computeIsThinking(messages: ChatMessage[], status: ChatStatus): boolean;
|
|
22
23
|
interface MessageRowProps {
|
|
@@ -29,7 +30,7 @@ interface MessageRowProps {
|
|
|
29
30
|
}
|
|
30
31
|
declare function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMessageId, onRestoreCheckpoint, }: MessageRowProps): import("react/jsx-runtime").JSX.Element;
|
|
31
32
|
export declare const MessageRow: import("react").MemoExoticComponent<typeof PureMessageRow>;
|
|
32
|
-
declare function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, }: MessagesProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
declare function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, isCompacting, }: MessagesProps): import("react/jsx-runtime").JSX.Element;
|
|
33
34
|
export declare const Messages: import("react").MemoExoticComponent<typeof PureMessages>;
|
|
34
35
|
export {};
|
|
35
36
|
//# sourceMappingURL=chat-messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-messages.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-messages.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"chat-messages.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-messages.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,gBAAgB,CAAA;AAGvB,KAAK,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,MAAM,EAAE,UAAU,CAAA;IAClB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAC5C,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACjD,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,UAAU,WAwB5E;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;CAClD;AAED,iBAAS,cAAc,CAAC,EACtB,OAAO,EACP,MAAM,EACN,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,mBAAmB,GACpB,EAAE,eAAe,2CAsBjB;AAED,eAAO,MAAM,UAAU,4DAAuB,CAAA;AAE9C,iBAAS,YAAY,CAAC,EACpB,QAAQ,EACR,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,YAAoB,GACrB,EAAE,aAAa,2CA0Bf;AAED,eAAO,MAAM,QAAQ,0DAAqB,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { memo } from 'react';
|
|
3
|
-
import { Message, ThinkingMessage } from './chat-message';
|
|
3
|
+
import { CompactingMessage, Message, ThinkingMessage, } from './chat-message';
|
|
4
4
|
import { CheckpointBadge } from './checkpoint-badge';
|
|
5
5
|
export function computeIsThinking(messages, status) {
|
|
6
6
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -30,12 +30,12 @@ function PureMessageRow({ message, isLast, isStreaming, checkpoint, restoringMes
|
|
|
30
30
|
return (_jsxs("div", { className: "group/message-row relative", children: [_jsx(Message, { message: message, isLast: isLast, isStreaming: isStreaming }), showCheckpointBadge && (_jsx("div", { className: "absolute bottom-1 right-4 opacity-0 transition-opacity group-hover/message-row:opacity-100", children: _jsx(CheckpointBadge, { messageId: message.id, fileCount: checkpoint.fileCount, totalBytes: checkpoint.totalBytes, isRestoring: restoringMessageId === message.id, onRestore: () => onRestoreCheckpoint(message.id) }) }))] }));
|
|
31
31
|
}
|
|
32
32
|
export const MessageRow = memo(PureMessageRow);
|
|
33
|
-
function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, }) {
|
|
33
|
+
function PureMessages({ messages, status, checkpoints, restoringMessageId, onRestoreCheckpoint, isCompacting = false, }) {
|
|
34
34
|
const isThinking = computeIsThinking(messages, status);
|
|
35
35
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
36
36
|
return (_jsxs(_Fragment, { children: [messages.map((message, index) => {
|
|
37
37
|
const checkpoint = checkpoints?.get(message.id);
|
|
38
38
|
return (_jsx(MessageRow, { message: message, isLast: index === messages.length - 1, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }, message.id));
|
|
39
|
-
}), isThinking && _jsx(ThinkingMessage, {})] }));
|
|
39
|
+
}), isThinking && _jsx(ThinkingMessage, {}), isCompacting && _jsx(CompactingMessage, {})] }));
|
|
40
40
|
}
|
|
41
41
|
export const Messages = memo(PureMessages);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ChangeEvent, type FormEvent, type ReactNode } from 'react';
|
|
2
|
-
import { type ChatImageAttachment, type ChatSubmitMode } from './chat-input';
|
|
2
|
+
import { type ChatImageAttachment, type ChatSlashCommandOption, type ChatSubmitMode } from './chat-input';
|
|
3
3
|
import { type ChatMessage } from './chat-message';
|
|
4
4
|
import { type MessageCheckpoint } from './chat-messages';
|
|
5
5
|
import { type ConversationMeta } from './conversation-history';
|
|
@@ -21,9 +21,15 @@ export interface QueuedChatMessage {
|
|
|
21
21
|
id: string;
|
|
22
22
|
preview: string;
|
|
23
23
|
attachmentCount: number;
|
|
24
|
-
status?: 'queued' | 'failed';
|
|
24
|
+
status?: 'queued' | 'failed' | 'steering';
|
|
25
25
|
error?: string;
|
|
26
26
|
}
|
|
27
|
+
export interface ChatContextUsage {
|
|
28
|
+
usedTokens: number;
|
|
29
|
+
maxTokens: number;
|
|
30
|
+
isEstimate?: boolean;
|
|
31
|
+
compactsAutomatically?: boolean;
|
|
32
|
+
}
|
|
27
33
|
export interface ChatPanelProps {
|
|
28
34
|
/** Chat messages */
|
|
29
35
|
messages: ChatMessage[];
|
|
@@ -57,6 +63,8 @@ export interface ChatPanelProps {
|
|
|
57
63
|
conversations?: ConversationMeta[];
|
|
58
64
|
/** Current conversation ID */
|
|
59
65
|
currentConversationId?: string | null;
|
|
66
|
+
/** Incremented when a conversation is explicitly loaded from history */
|
|
67
|
+
scrollToBottomSignal?: number;
|
|
60
68
|
/** Handle conversation selection */
|
|
61
69
|
onSelectConversation?: (id: string) => void;
|
|
62
70
|
/** Handle conversation deletion */
|
|
@@ -111,10 +119,24 @@ export interface ChatPanelProps {
|
|
|
111
119
|
onRetryQueuedMessage?: (id: string) => void;
|
|
112
120
|
/** Send a queued follow-up into the active response immediately */
|
|
113
121
|
onSteerQueuedMessage?: (id: string) => void;
|
|
122
|
+
/** Reorder queued follow-ups */
|
|
123
|
+
onReorderQueuedMessages?: (activeId: string, overId: string) => void;
|
|
124
|
+
/** Edit a queued follow-up in the composer */
|
|
125
|
+
onEditQueuedMessage?: (id: string) => void;
|
|
126
|
+
/** Slash commands matching the current input */
|
|
127
|
+
slashCommands?: ChatSlashCommandOption[];
|
|
128
|
+
/** Fill the composer with a slash command */
|
|
129
|
+
onSlashCommandSelect?: (command: ChatSlashCommandOption) => void;
|
|
130
|
+
/** Whether a manual compaction command is running */
|
|
131
|
+
isCompacting?: boolean;
|
|
132
|
+
/** Display-message index where the currently compacted context resumes */
|
|
133
|
+
compactedHistoryStartIndex?: number | null;
|
|
134
|
+
/** Estimated chat context usage for the selected model */
|
|
135
|
+
contextUsage?: ChatContextUsage;
|
|
114
136
|
}
|
|
115
137
|
/**
|
|
116
138
|
* Floating chat panel with model selector
|
|
117
139
|
*/
|
|
118
|
-
export declare function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments, onRemoveImageAttachment, isImageDropActive, queuedMessages, onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
140
|
+
export declare function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, scrollToBottomSignal, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments, onRemoveImageAttachment, isImageDropActive, queuedMessages, onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, onReorderQueuedMessages, onEditQueuedMessage, slashCommands, onSlashCommandSelect, isCompacting, compactedHistoryStartIndex, contextUsage, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
119
141
|
export {};
|
|
120
142
|
//# sourceMappingURL=chat-panel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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"}
|