@springbrand/message-panel 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/demo/camel-chat-showcase.d.ts +1 -0
  2. package/dist/demo/camel-chat-showcase.js +1007 -0
  3. package/dist/demo/fixtures.d.ts +32 -0
  4. package/dist/demo/fixtures.js +250 -0
  5. package/dist/demo/index.js +4 -0
  6. package/dist/demo/message-panel-chat-showcase.d.ts +1 -0
  7. package/dist/demo/message-panel-chat-showcase.js +114 -0
  8. package/dist/demo/message-panel-gallery.d.ts +1 -0
  9. package/dist/demo/message-panel-gallery.js +130 -0
  10. package/dist/src/adapters/ai-sdk.d.ts +4 -0
  11. package/dist/src/adapters/ai-sdk.js +25 -0
  12. package/dist/src/camel/camel-chat-messages.d.ts +44 -0
  13. package/dist/src/camel/camel-chat-messages.js +421 -0
  14. package/dist/src/camel/camel-model-select.d.ts +13 -0
  15. package/dist/src/camel/camel-model-select.js +7 -0
  16. package/dist/src/camel/camel-plan-bar.d.ts +5 -0
  17. package/dist/src/camel/camel-plan-bar.js +24 -0
  18. package/dist/src/camel/camel-prompt-input.d.ts +32 -0
  19. package/dist/src/camel/camel-prompt-input.js +182 -0
  20. package/dist/src/camel/camel-tool-presentation.d.ts +21 -0
  21. package/dist/src/camel/camel-tool-presentation.js +784 -0
  22. package/dist/src/camel/camel-turn.d.ts +57 -0
  23. package/dist/src/camel/camel-turn.js +203 -0
  24. package/dist/src/camel/index.js +7 -0
  25. package/dist/src/camel/turn-summary-bar.d.ts +8 -0
  26. package/dist/src/camel/turn-summary-bar.js +12 -0
  27. package/dist/src/chat-summary-panel.d.ts +22 -0
  28. package/dist/src/chat-summary-panel.js +26 -0
  29. package/dist/src/composer/chat-composer.d.ts +4 -0
  30. package/dist/src/composer/chat-composer.js +90 -0
  31. package/dist/src/composer/composer-attachments.d.ts +13 -0
  32. package/dist/src/composer/composer-attachments.js +47 -0
  33. package/dist/src/composer/composer-trigger-popover.d.ts +31 -0
  34. package/dist/src/composer/composer-trigger-popover.js +29 -0
  35. package/dist/src/composer/composer.d.ts +15 -0
  36. package/dist/src/composer/composer.js +180 -0
  37. package/dist/src/composer/drop-controller.d.ts +8 -0
  38. package/dist/src/composer/drop-controller.js +69 -0
  39. package/dist/src/composer/index.js +6 -0
  40. package/dist/src/composer/types.d.ts +62 -0
  41. package/dist/src/composer/types.js +1 -0
  42. package/dist/src/contracts.d.ts +94 -0
  43. package/dist/src/contracts.js +1 -0
  44. package/dist/src/conversation.d.ts +13 -0
  45. package/dist/src/conversation.js +18 -0
  46. package/dist/src/index.js +12 -0
  47. package/dist/src/internal/cn.d.ts +1 -0
  48. package/dist/src/internal/cn.js +3 -0
  49. package/dist/src/message-panel.d.ts +4 -0
  50. package/dist/src/message-panel.js +144 -0
  51. package/dist/src/message.d.ts +36 -0
  52. package/dist/src/message.js +83 -0
  53. package/dist/src/parts/index.d.ts +187 -0
  54. package/dist/src/parts/index.js +332 -0
  55. package/dist/src/parts/kind.d.ts +3 -0
  56. package/dist/src/parts/kind.js +69 -0
  57. package/dist/src/parts/plan.d.ts +10 -0
  58. package/dist/src/parts/plan.js +40 -0
  59. package/dist/src/parts/primitives.d.ts +70 -0
  60. package/dist/src/parts/primitives.js +153 -0
  61. package/dist/src/registry.d.ts +4 -0
  62. package/dist/src/registry.js +31 -0
  63. package/dist/src/status.d.ts +26 -0
  64. package/dist/src/status.js +74 -0
  65. package/package.json +17 -8
  66. package/demo/camel-chat-showcase.tsx +0 -1265
  67. package/demo/fixtures.ts +0 -292
  68. package/demo/message-panel-chat-showcase.tsx +0 -250
  69. package/demo/message-panel-gallery.tsx +0 -389
  70. package/src/adapters/ai-sdk.ts +0 -32
  71. package/src/camel/camel-chat-messages.tsx +0 -1420
  72. package/src/camel/camel-model-select.tsx +0 -88
  73. package/src/camel/camel-plan-bar.tsx +0 -115
  74. package/src/camel/camel-prompt-input.tsx +0 -540
  75. package/src/camel/camel-tool-presentation.tsx +0 -1270
  76. package/src/camel/camel-turn.ts +0 -315
  77. package/src/camel/turn-summary-bar.tsx +0 -81
  78. package/src/chat-summary-panel.tsx +0 -229
  79. package/src/composer/chat-composer.tsx +0 -216
  80. package/src/composer/composer-attachments.tsx +0 -138
  81. package/src/composer/composer-trigger-popover.tsx +0 -236
  82. package/src/composer/composer.tsx +0 -425
  83. package/src/composer/drop-controller.ts +0 -80
  84. package/src/composer/types.ts +0 -79
  85. package/src/contracts.ts +0 -139
  86. package/src/conversation.tsx +0 -76
  87. package/src/internal/cn.ts +0 -3
  88. package/src/message-panel.tsx +0 -368
  89. package/src/message.tsx +0 -254
  90. package/src/parts/index.tsx +0 -1149
  91. package/src/parts/kind.ts +0 -67
  92. package/src/parts/plan.ts +0 -63
  93. package/src/parts/primitives.tsx +0 -439
  94. package/src/registry.ts +0 -44
  95. package/src/status.tsx +0 -132
  96. /package/{demo/index.ts → dist/demo/index.d.ts} +0 -0
  97. /package/{src/camel/index.ts → dist/src/camel/index.d.ts} +0 -0
  98. /package/{src/composer/index.ts → dist/src/composer/index.d.ts} +0 -0
  99. /package/{src/index.ts → dist/src/index.d.ts} +0 -0
@@ -0,0 +1,182 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AssistantRuntimeProvider, ComposerPrimitive, unstable_useSlashCommandAdapter, unstable_useTriggerPopoverAriaProps, unstable_useTriggerPopoverTriggers, useExternalStoreRuntime, } from "@assistant-ui/react";
3
+ import { ArrowUpIcon, ClockIcon, FileIcon, FileTextIcon, GlobeIcon, ListChecksIcon, Loader2Icon, PlugIcon, PlusIcon, RotateCcwIcon, SquareIcon, TerminalIcon, WrenchIcon, XIcon, } from "lucide-react";
4
+ import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from "react";
5
+ import { ComposerTriggerPopover } from "../composer/composer-trigger-popover";
6
+ import { cn } from "../internal/cn";
7
+ const COMMAND_ICON_MAP = {
8
+ plan: ListChecksIcon,
9
+ research: GlobeIcon,
10
+ file: FileTextIcon,
11
+ time: ClockIcon,
12
+ tool: WrenchIcon,
13
+ mcp: PlugIcon,
14
+ };
15
+ function Button({ className, size = "sm", variant = "default", type = "button", ...props }) {
16
+ return (_jsx("button", { type: type, className: cn("inline-flex shrink-0 items-center justify-center gap-1 rounded-md text-xs font-medium transition-all outline-none disabled:pointer-events-none disabled:opacity-50", variant === "default" &&
17
+ "bg-primary text-primary-foreground hover:bg-primary/80", variant === "outline" &&
18
+ "border border-border bg-background hover:bg-muted", variant === "ghost" && "hover:bg-muted hover:text-foreground", variant === "destructive" &&
19
+ "bg-destructive/10 text-destructive hover:bg-destructive/20", size === "sm" && "h-7 px-2", size === "icon-xs" && "size-6 p-0", size === "icon-sm" && "size-8 p-0", className), ...props }));
20
+ }
21
+ function InputGroup({ className, ...props }) {
22
+ return (_jsx("div", { role: "group", className: cn("group/input-group relative flex w-full min-w-0 flex-col items-center rounded-md border border-input bg-input/20 outline-none transition-colors dark:bg-input/30", "has-[[data-camel-input=true]:focus-visible]:border-ring has-[[data-camel-input=true]:focus-visible]:ring-2 has-[[data-camel-input=true]:focus-visible]:ring-ring/30", className), ...props }));
23
+ }
24
+ function InputGroupAddon({ className, align, ...props }) {
25
+ return (_jsx("div", { role: "group", "data-align": align, className: cn("flex h-auto w-full cursor-text items-center gap-1 px-2 text-xs font-medium text-muted-foreground", align === "block-start"
26
+ ? "order-first justify-start pt-2"
27
+ : "order-last pb-2", className), ...props }));
28
+ }
29
+ function InputGroupButton(props) {
30
+ return _jsx(Button, { ...props });
31
+ }
32
+ const InputGroupTextarea = forwardRef(({ className, ...props }, ref) => (_jsx("textarea", { ref: ref, "data-camel-input": "true", className: cn("w-full flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none outline-none ring-0 placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", className), ...props })));
33
+ InputGroupTextarea.displayName = "InputGroupTextarea";
34
+ function formatBytes(size) {
35
+ if (!size)
36
+ return "";
37
+ if (size < 1024)
38
+ return `${size} B`;
39
+ if (size < 1024 * 1024)
40
+ return `${Math.round(size / 1024)} KB`;
41
+ return `${(size / 1024 / 1024).toFixed(1)} MB`;
42
+ }
43
+ function AttachmentList({ attachments, onRemove, onRetry, }) {
44
+ return (_jsx("div", { className: "flex w-full flex-wrap gap-2", children: attachments.map((attachment) => (_jsxs("div", { className: "group/attachment flex max-w-64 items-center gap-2 rounded-lg border border-border bg-muted/30 p-2", "data-attachment-status": attachment.status, children: [attachment.mediaType?.startsWith("image/") ? (_jsx("img", { src: attachment.url, alt: "", className: "size-9 shrink-0 rounded-md object-cover" })) : (_jsx("span", { className: "grid size-9 shrink-0 place-items-center rounded-md bg-muted", children: _jsx(FileIcon, { className: "size-4 text-muted-foreground" }) })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-xs text-foreground", children: attachment.filename ?? "Attachment" }), _jsxs("div", { className: "mt-0.5 flex items-center gap-1 text-[10px] text-muted-foreground", children: [attachment.status === "uploading" && (_jsx(Loader2Icon, { className: "size-3 animate-spin" })), _jsx("span", { children: attachment.status === "uploading"
45
+ ? "Uploading"
46
+ : attachment.status === "error"
47
+ ? "Upload failed"
48
+ : formatBytes(attachment.size) })] })] }), attachment.status === "error" && (_jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => onRetry(attachment.id), "aria-label": "\u91CD\u8BD5\u4E0A\u4F20", children: _jsx(RotateCcwIcon, { className: "size-3" }) })), _jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => onRemove(attachment.id), "aria-label": "\u79FB\u9664\u9644\u4EF6", children: _jsx(XIcon, { className: "size-3" }) })] }, attachment.id))) }));
49
+ }
50
+ function CamelPromptInputContent({ value, onChange, onSubmit, onStop, status, attachments, commands, onFilesSelected, onAttachmentRemove, onAttachmentRetry, tools, submitControl, placeholder = "Type a message...", submissionBlocked = false, turnActive, runtime, }) {
51
+ const fileInputRef = useRef(null);
52
+ const textareaRef = useRef(null);
53
+ const [isDragOver, setIsDragOver] = useState(false);
54
+ const isRunning = turnActive ??
55
+ (status === "submitted" || status === "streaming");
56
+ const hasUploading = attachments.some((attachment) => attachment.status === "uploading");
57
+ const hasReady = attachments.some((attachment) => attachment.status === "ready");
58
+ const slashCommands = useMemo(() => commands.map((command) => ({
59
+ id: command.name,
60
+ description: command.description,
61
+ icon: command.icon,
62
+ execute: () => {
63
+ runtime.thread.composer.setText(command.prompt);
64
+ onChange(command.prompt);
65
+ requestAnimationFrame(() => textareaRef.current?.focus());
66
+ },
67
+ })), [commands, onChange, runtime]);
68
+ const slash = unstable_useSlashCommandAdapter({
69
+ commands: slashCommands,
70
+ removeOnExecute: true,
71
+ iconMap: COMMAND_ICON_MAP,
72
+ fallbackIcon: TerminalIcon,
73
+ });
74
+ const triggers = unstable_useTriggerPopoverTriggers();
75
+ const triggerAria = unstable_useTriggerPopoverAriaProps();
76
+ const setTriggerCursor = useCallback((position) => {
77
+ for (const trigger of triggers.values()) {
78
+ trigger.resource.setCursorPosition(position);
79
+ }
80
+ }, [triggers]);
81
+ useEffect(() => {
82
+ const textarea = textareaRef.current;
83
+ if (!textarea)
84
+ return;
85
+ textarea.style.height = "auto";
86
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 384)}px`;
87
+ }, [value]);
88
+ const submit = useCallback(() => {
89
+ if (isRunning && !value.trim() && !hasReady) {
90
+ onStop();
91
+ return;
92
+ }
93
+ if (submissionBlocked)
94
+ return;
95
+ if (!hasUploading && (value.trim() || hasReady))
96
+ onSubmit();
97
+ }, [
98
+ hasReady,
99
+ hasUploading,
100
+ isRunning,
101
+ onStop,
102
+ onSubmit,
103
+ submissionBlocked,
104
+ value,
105
+ ]);
106
+ const handleKeyDown = (event) => {
107
+ for (const trigger of triggers.values()) {
108
+ if (trigger.resource.handleKeyDown(event))
109
+ return;
110
+ }
111
+ if (event.key === "Enter" &&
112
+ !event.shiftKey &&
113
+ !event.nativeEvent.isComposing) {
114
+ event.preventDefault();
115
+ submit();
116
+ }
117
+ };
118
+ const handlePaste = (event) => {
119
+ if (!onFilesSelected)
120
+ return;
121
+ const files = Array.from(event.clipboardData.files);
122
+ if (files.length > 0) {
123
+ event.preventDefault();
124
+ onFilesSelected(files);
125
+ }
126
+ };
127
+ const handleFiles = (event) => {
128
+ const files = Array.from(event.currentTarget.files ?? []);
129
+ if (files.length > 0)
130
+ onFilesSelected?.(files);
131
+ event.currentTarget.value = "";
132
+ };
133
+ const handleDragOver = (event) => {
134
+ if (!onFilesSelected)
135
+ return;
136
+ event.preventDefault();
137
+ event.stopPropagation();
138
+ setIsDragOver(true);
139
+ };
140
+ const handleDragLeave = (event) => {
141
+ event.preventDefault();
142
+ event.stopPropagation();
143
+ const nextTarget = event.relatedTarget;
144
+ if (!(nextTarget instanceof Node) || !event.currentTarget.contains(nextTarget)) {
145
+ setIsDragOver(false);
146
+ }
147
+ };
148
+ const handleDrop = (event) => {
149
+ if (!onFilesSelected)
150
+ return;
151
+ event.preventDefault();
152
+ event.stopPropagation();
153
+ setIsDragOver(false);
154
+ const files = Array.from(event.dataTransfer.files);
155
+ if (files.length > 0)
156
+ onFilesSelected(files);
157
+ };
158
+ const handleFormSubmit = (event) => {
159
+ event.preventDefault();
160
+ submit();
161
+ };
162
+ const showStopButton = isRunning && !value.trim() && !hasReady;
163
+ const submitDisabled = hasUploading || (!showStopButton &&
164
+ (submissionBlocked || (!value.trim() && !hasReady)));
165
+ return (_jsxs("form", { onSubmit: handleFormSubmit, className: "relative shrink-0", "data-camel-prompt-input": "", "data-drop-scope": "document", children: [onFilesSelected && (_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFiles, className: "hidden", "aria-hidden": "true" })), _jsx(ComposerTriggerPopover, { char: "/", ...slash, "aria-label": "Commands", backLabel: "Back", emptyItemsLabel: "No matching commands", className: "w-full" }), _jsxs("div", { onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, className: cn("relative rounded-2xl transition-all duration-200", isDragOver && "ring-2 ring-primary ring-offset-2"), children: [isDragOver && (_jsx("div", { className: "absolute inset-0 z-20 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary bg-primary/10", children: _jsx("span", { className: "text-sm font-medium text-primary", children: "Drop files here" }) })), _jsxs(InputGroup, { className: "cursor-text rounded-2xl border-border bg-background shadow-sm transition-all duration-200 hover:shadow-md focus-within:border-ring focus-within:shadow-md", children: [attachments.length > 0 && (_jsx(InputGroupAddon, { align: "block-start", className: "border-b border-border", children: _jsx(AttachmentList, { attachments: attachments, onRemove: onAttachmentRemove, onRetry: onAttachmentRetry }) })), _jsx(InputGroupTextarea, { ref: textareaRef, value: value, onChange: (event) => {
166
+ const next = event.currentTarget.value;
167
+ runtime.thread.composer.setText(next);
168
+ onChange(next);
169
+ setTriggerCursor(event.currentTarget.selectionStart ?? next.length);
170
+ }, onKeyDown: handleKeyDown, onSelect: (event) => setTriggerCursor(event.currentTarget.selectionStart ?? value.length), onPaste: handlePaste, placeholder: placeholder, autoFocus: true, className: "relative z-10 max-h-96 min-h-11 overflow-y-auto bg-transparent p-3.5 text-sm selection:bg-primary/30", style: { caretColor: "var(--foreground)" }, ...triggerAria }), _jsxs(InputGroupAddon, { align: "block-end", className: "justify-between px-3 pb-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [onFilesSelected && (_jsx(InputGroupButton, { type: "button", size: "icon-sm", variant: "ghost", onClick: () => fileInputRef.current?.click(), className: "rounded-full text-muted-foreground hover:bg-muted hover:text-foreground", "aria-label": "Upload file", children: _jsx(PlusIcon, { className: "size-4" }) })), tools] }), _jsxs("div", { className: "flex items-center gap-1", children: [!showStopButton && submitControl, _jsx(InputGroupButton, { type: showStopButton ? "button" : "submit", size: "icon-sm", variant: "default", disabled: submitDisabled, onClick: showStopButton ? onStop : undefined, className: "rounded-full", "aria-label": showStopButton ? "停止" : "发送", children: showStopButton ? (_jsx(SquareIcon, { className: "size-3" })) : (_jsx(ArrowUpIcon, { className: "size-4" })) })] })] })] })] })] }));
171
+ }
172
+ const EMPTY_MESSAGES = [];
173
+ export function CamelPromptInput(props) {
174
+ const runtime = useExternalStoreRuntime({
175
+ messages: EMPTY_MESSAGES,
176
+ onNew: async () => undefined,
177
+ });
178
+ useEffect(() => {
179
+ runtime.thread.composer.setText(props.value);
180
+ }, [props.value, runtime]);
181
+ return (_jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx(ComposerPrimitive.Unstable_TriggerPopoverRoot, { children: _jsx(CamelPromptInputContent, { ...props, runtime: runtime }) }) }));
182
+ }
@@ -0,0 +1,21 @@
1
+ type UnknownRecord = Record<string, unknown>;
2
+ export type CamelToolKind = "read" | "write" | "edit" | "bash" | "glob" | "grep" | "list" | "find" | "web-search" | "web-fetch" | "javascript" | "tasks" | "agent" | "skill" | "skill-resource" | "ask-user" | "suggestions" | "notebook" | "mcp" | "generic";
3
+ export interface CamelToolPresentation {
4
+ kind: CamelToolKind;
5
+ name: string;
6
+ summary: string;
7
+ input: UnknownRecord;
8
+ output: unknown;
9
+ outputText: string;
10
+ errorText: string;
11
+ running: boolean;
12
+ failed: boolean;
13
+ isSubAgent: boolean;
14
+ hasDetails: boolean;
15
+ }
16
+ interface CamelToolDetailsProps {
17
+ presentation: CamelToolPresentation;
18
+ }
19
+ export declare function getCamelToolPresentation(part: unknown, isActive?: boolean): CamelToolPresentation;
20
+ export declare function CamelToolDetails({ presentation, }: CamelToolDetailsProps): import("react").JSX.Element;
21
+ export {};