@flanksource/clicky-ui 0.2.15 → 0.2.17
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/chat.cjs +16 -0
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.js +17 -1
- package/dist/chat.js.map +1 -1
- package/dist/components/Combobox.cjs +34 -11
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts +8 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +34 -11
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/DateTimePicker.cjs +3 -0
- package/dist/components/DateTimePicker.cjs.map +1 -1
- package/dist/components/DateTimePicker.d.ts +6 -2
- package/dist/components/DateTimePicker.d.ts.map +1 -1
- package/dist/components/DateTimePicker.js +3 -0
- package/dist/components/DateTimePicker.js.map +1 -1
- package/dist/components/Field.cjs +33 -0
- package/dist/components/Field.cjs.map +1 -0
- package/dist/components/Field.d.ts +27 -0
- package/dist/components/Field.d.ts.map +1 -0
- package/dist/components/Field.js +33 -0
- package/dist/components/Field.js.map +1 -0
- package/dist/components/FilterBar.cjs +81 -20
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts +30 -0
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +83 -22
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/JsonSchemaForm.cjs +26 -3
- package/dist/components/JsonSchemaForm.cjs.map +1 -1
- package/dist/components/JsonSchemaForm.d.ts.map +1 -1
- package/dist/components/JsonSchemaForm.js +26 -3
- package/dist/components/JsonSchemaForm.js.map +1 -1
- package/dist/components/MultiSelect.cjs +2 -1
- package/dist/components/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect.d.ts +5 -1
- package/dist/components/MultiSelect.d.ts.map +1 -1
- package/dist/components/MultiSelect.js +2 -1
- package/dist/components/MultiSelect.js.map +1 -1
- package/dist/components/json-schema-form-fields.cjs +15 -8
- package/dist/components/json-schema-form-fields.cjs.map +1 -1
- package/dist/components/json-schema-form-fields.d.ts.map +1 -1
- package/dist/components/json-schema-form-fields.js +15 -8
- package/dist/components/json-schema-form-fields.js.map +1 -1
- package/dist/components/json-schema-form-preferences.cjs +4 -0
- package/dist/components/json-schema-form-preferences.cjs.map +1 -1
- package/dist/components/json-schema-form-preferences.d.ts +2 -0
- package/dist/components/json-schema-form-preferences.d.ts.map +1 -1
- package/dist/components/json-schema-form-preferences.js +4 -0
- package/dist/components/json-schema-form-preferences.js.map +1 -1
- package/dist/components/json-schema-form-render.cjs +1 -1
- package/dist/components/json-schema-form-render.cjs.map +1 -1
- package/dist/components/json-schema-form-render.d.ts.map +1 -1
- package/dist/components/json-schema-form-render.js +2 -2
- package/dist/components/json-schema-form-render.js.map +1 -1
- package/dist/components/json-schema-form-types.d.ts +4 -2
- package/dist/components/json-schema-form-types.d.ts.map +1 -1
- package/dist/components/json-schema-form-utils.cjs +13 -0
- package/dist/components/json-schema-form-utils.cjs.map +1 -1
- package/dist/components/json-schema-form-utils.d.ts +2 -0
- package/dist/components/json-schema-form-utils.d.ts.map +1 -1
- package/dist/components/json-schema-form-utils.js +13 -0
- package/dist/components/json-schema-form-utils.js.map +1 -1
- package/dist/components/loading.cjs +21 -8
- package/dist/components/loading.cjs.map +1 -1
- package/dist/components/loading.d.ts +6 -4
- package/dist/components/loading.d.ts.map +1 -1
- package/dist/components/loading.js +21 -8
- package/dist/components/loading.js.map +1 -1
- package/dist/components/use-form.cjs +27 -0
- package/dist/components/use-form.cjs.map +1 -0
- package/dist/components/use-form.d.ts +30 -0
- package/dist/components/use-form.d.ts.map +1 -0
- package/dist/components/use-form.js +27 -0
- package/dist/components/use-form.js.map +1 -0
- package/dist/components.cjs +7 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +4 -1
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +8 -1
- package/dist/components.js.map +1 -1
- package/dist/data/Markdown.cjs +14 -40
- package/dist/data/Markdown.cjs.map +1 -1
- package/dist/data/Markdown.d.ts +3 -0
- package/dist/data/Markdown.d.ts.map +1 -1
- package/dist/data/Markdown.js +14 -40
- package/dist/data/Markdown.js.map +1 -1
- package/dist/data/TaskProgress.cjs +22 -0
- package/dist/data/TaskProgress.cjs.map +1 -1
- package/dist/data/TaskProgress.js +22 -0
- package/dist/data/TaskProgress.js.map +1 -1
- package/dist/data/TaskSnapshot.cjs.map +1 -1
- package/dist/data/TaskSnapshot.d.ts +5 -0
- package/dist/data/TaskSnapshot.d.ts.map +1 -1
- package/dist/data/TaskSnapshot.js.map +1 -1
- package/dist/data/Tree.cjs +32 -1
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts +9 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js +32 -1
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/chat/Attachment.cjs +79 -0
- package/dist/data/chat/Attachment.cjs.map +1 -0
- package/dist/data/chat/Attachment.d.ts +18 -0
- package/dist/data/chat/Attachment.d.ts.map +1 -0
- package/dist/data/chat/Attachment.js +79 -0
- package/dist/data/chat/Attachment.js.map +1 -0
- package/dist/data/chat/Chat.cjs +71 -14
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.d.ts +30 -12
- package/dist/data/chat/Chat.d.ts.map +1 -1
- package/dist/data/chat/Chat.fixtures.d.ts +9 -0
- package/dist/data/chat/Chat.fixtures.d.ts.map +1 -1
- package/dist/data/chat/Chat.js +73 -16
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/Conversation.cjs +32 -14
- package/dist/data/chat/Conversation.cjs.map +1 -1
- package/dist/data/chat/Conversation.d.ts +5 -3
- package/dist/data/chat/Conversation.d.ts.map +1 -1
- package/dist/data/chat/Conversation.js +34 -16
- package/dist/data/chat/Conversation.js.map +1 -1
- package/dist/data/chat/Message.cjs +73 -14
- package/dist/data/chat/Message.cjs.map +1 -1
- package/dist/data/chat/Message.d.ts +11 -4
- package/dist/data/chat/Message.d.ts.map +1 -1
- package/dist/data/chat/Message.js +75 -16
- package/dist/data/chat/Message.js.map +1 -1
- package/dist/data/chat/MessageActions.cjs +54 -0
- package/dist/data/chat/MessageActions.cjs.map +1 -0
- package/dist/data/chat/MessageActions.d.ts +11 -0
- package/dist/data/chat/MessageActions.d.ts.map +1 -0
- package/dist/data/chat/MessageActions.js +54 -0
- package/dist/data/chat/MessageActions.js.map +1 -0
- package/dist/data/chat/ModelSelector.cjs +43 -0
- package/dist/data/chat/ModelSelector.cjs.map +1 -0
- package/dist/data/chat/ModelSelector.d.ts +22 -0
- package/dist/data/chat/ModelSelector.d.ts.map +1 -0
- package/dist/data/chat/ModelSelector.js +43 -0
- package/dist/data/chat/ModelSelector.js.map +1 -0
- package/dist/data/chat/PromptInput.cjs +28 -15
- package/dist/data/chat/PromptInput.cjs.map +1 -1
- package/dist/data/chat/PromptInput.d.ts +14 -8
- package/dist/data/chat/PromptInput.d.ts.map +1 -1
- package/dist/data/chat/PromptInput.js +28 -15
- package/dist/data/chat/PromptInput.js.map +1 -1
- package/dist/data/chat/Reasoning.cjs +37 -0
- package/dist/data/chat/Reasoning.cjs.map +1 -0
- package/dist/data/chat/Reasoning.d.ts +11 -0
- package/dist/data/chat/Reasoning.d.ts.map +1 -0
- package/dist/data/chat/Reasoning.js +37 -0
- package/dist/data/chat/Reasoning.js.map +1 -0
- package/dist/data/chat/Suggestion.cjs +20 -0
- package/dist/data/chat/Suggestion.cjs.map +1 -0
- package/dist/data/chat/Suggestion.d.ts +10 -0
- package/dist/data/chat/Suggestion.d.ts.map +1 -0
- package/dist/data/chat/Suggestion.js +20 -0
- package/dist/data/chat/Suggestion.js.map +1 -0
- package/dist/data/chat/ToolCall.cjs +26 -3
- package/dist/data/chat/ToolCall.cjs.map +1 -1
- package/dist/data/chat/ToolCall.d.ts +5 -1
- package/dist/data/chat/ToolCall.d.ts.map +1 -1
- package/dist/data/chat/ToolCall.js +26 -3
- package/dist/data/chat/ToolCall.js.map +1 -1
- package/dist/data/chat/index.d.ts +7 -2
- package/dist/data/chat/index.d.ts.map +1 -1
- package/dist/data/chat/types.cjs +16 -0
- package/dist/data/chat/types.cjs.map +1 -1
- package/dist/data/chat/types.d.ts +29 -2
- package/dist/data/chat/types.d.ts.map +1 -1
- package/dist/data/chat/types.js +16 -0
- package/dist/data/chat/types.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/hooks/use-task-run.cjs +33 -20
- package/dist/hooks/use-task-run.cjs.map +1 -1
- package/dist/hooks/use-task-run.d.ts.map +1 -1
- package/dist/hooks/use-task-run.js +33 -20
- package/dist/hooks/use-task-run.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/overlay/Modal.cjs +27 -23
- package/dist/overlay/Modal.cjs.map +1 -1
- package/dist/overlay/Modal.d.ts +7 -1
- package/dist/overlay/Modal.d.ts.map +1 -1
- package/dist/overlay/Modal.js +27 -23
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/rpc/FilterForm.cjs +4 -4
- package/dist/rpc/FilterForm.cjs.map +1 -1
- package/dist/rpc/FilterForm.js +4 -4
- package/dist/rpc/FilterForm.js.map +1 -1
- package/dist/rpc/OperationCatalog.cjs +17 -21
- package/dist/rpc/OperationCatalog.cjs.map +1 -1
- package/dist/rpc/OperationCatalog.d.ts +3 -3
- package/dist/rpc/OperationCatalog.d.ts.map +1 -1
- package/dist/rpc/OperationCatalog.js +18 -22
- package/dist/rpc/OperationCatalog.js.map +1 -1
- package/dist/rpc/formMetadata.cjs +5 -3
- package/dist/rpc/formMetadata.cjs.map +1 -1
- package/dist/rpc/formMetadata.d.ts.map +1 -1
- package/dist/rpc/formMetadata.js +5 -3
- package/dist/rpc/formMetadata.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.cjs","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport type { UIMessage } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"Message.cjs","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n} from \"./types\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;\n};\n\nexport type MessageProps = MessageActionHandlers & {\n message: UIMessage;\n className?: string;\n};\n\n/** Renders one chat message. User messages are right-aligned bubbles; assistant\n * messages render text as markdown, reasoning and tool parts inline, file parts\n * as thumbnails/chips, and a hover action row (copy / regenerate). */\nexport function Message({ message, className, onRegenerate, onApprove }: MessageProps) {\n const isUser = message.role === \"user\";\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\");\n\n return (\n <div\n className={cn(\n \"group flex w-full max-w-[95%] flex-col gap-2\",\n isUser ? \"ml-auto items-end\" : \"items-start\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm\",\n isUser && \"rounded-lg bg-secondary px-4 py-3 text-secondary-foreground\",\n )}\n >\n {message.parts.map((part, i) => (\n <MessagePart\n key={`${message.id}-${i}`}\n part={part}\n isUser={isUser}\n onApprove={onApprove}\n />\n ))}\n </div>\n\n {!isUser && text && (\n <MessageActions\n text={text}\n onRegenerate={onRegenerate ? () => onRegenerate(message.id) : undefined}\n />\n )}\n </div>\n );\n}\n\nfunction MessagePart({\n part,\n isUser,\n onApprove,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n}) {\n if (part.type === \"text\") {\n if (isUser) {\n return <span className=\"whitespace-pre-wrap break-words\">{part.text}</span>;\n }\n return <Markdown text={part.text} />;\n }\n if (isReasoningPart(part)) {\n return <Reasoning text={part.text} />;\n }\n if (isFilePart(part)) {\n return <FilePart part={part} />;\n }\n if (isDynamicToolPart(part) || isTypedToolPart(part)) {\n return <ToolCall part={part as AnyToolPart} onApprove={onApprove} />;\n }\n return null;\n}\n\n/** Renders an attachment: images inline as a thumbnail, everything else as a\n * labelled file chip. */\nfunction FilePart({ part }: { part: FileUIPart }) {\n const isImage = part.mediaType?.startsWith(\"image/\");\n if (isImage && part.url) {\n return (\n <img\n src={part.url}\n alt={part.filename ?? \"attachment\"}\n className=\"max-h-48 max-w-full rounded-md border border-border\"\n />\n );\n }\n return (\n <a\n href={part.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFile} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate\">{part.filename ?? part.mediaType ?? \"file\"}</span>\n </a>\n );\n}\n"],"names":["jsxs","cn","jsx","MessageActions","Markdown","isReasoningPart","Reasoning","isFilePart","isDynamicToolPart","isTypedToolPart","ToolCall","Icon","UiFile"],"mappings":";;;;;;;;;;;AAgCO,SAAS,QAAQ,EAAE,SAAS,WAAW,cAAc,aAA2B;AACrF,QAAM,SAAS,QAAQ,SAAS;AAChC,QAAM,OAAO,QAAQ,MAClB,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAO,EAAuB,IAAI,EACvC,KAAK,EAAE;AAEV,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA,SAAS,sBAAsB;AAAA,QAC/B;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,MAAAA;AAAAA,cACT;AAAA,cACA,UAAU;AAAA,YAAA;AAAA,YAGX,UAAA,QAAQ,MAAM,IAAI,CAAC,MAAM,MACxBC,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBAEC;AAAA,gBACA;AAAA,gBACA;AAAA,cAAA;AAAA,cAHK,GAAG,QAAQ,EAAE,IAAI,CAAC;AAAA,YAAA,CAK1B;AAAA,UAAA;AAAA,QAAA;AAAA,QAGF,CAAC,UAAU,QACVA,2BAAAA;AAAAA,UAACC,eAAAA;AAAAA,UAAA;AAAA,YACC;AAAA,YACA,cAAc,eAAe,MAAM,aAAa,QAAQ,EAAE,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAChE;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,KAAK,SAAS,QAAQ;AACxB,QAAI,QAAQ;AACV,aAAOD,2BAAAA,IAAC,QAAA,EAAK,WAAU,mCAAmC,eAAK,MAAK;AAAA,IACtE;AACA,WAAOA,2BAAAA,IAACE,SAAAA,UAAA,EAAS,MAAM,KAAK,KAAA,CAAM;AAAA,EACpC;AACA,MAAIC,MAAAA,gBAAgB,IAAI,GAAG;AACzB,WAAOH,2BAAAA,IAACI,UAAAA,WAAA,EAAU,MAAM,KAAK,KAAA,CAAM;AAAA,EACrC;AACA,MAAIC,MAAAA,WAAW,IAAI,GAAG;AACpB,WAAOL,+BAAC,YAAS,MAAY;AAAA,EAC/B;AACA,MAAIM,MAAAA,kBAAkB,IAAI,KAAKC,MAAAA,gBAAgB,IAAI,GAAG;AACpD,WAAOP,2BAAAA,IAACQ,SAAAA,UAAA,EAAS,MAA2B,UAAA,CAAsB;AAAA,EACpE;AACA,SAAO;AACT;AAIA,SAAS,SAAS,EAAE,QAA8B;;AAChD,QAAM,WAAU,UAAK,cAAL,mBAAgB,WAAW;AAC3C,MAAI,WAAW,KAAK,KAAK;AACvB,WACER,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK,KAAK;AAAA,QACV,KAAK,KAAK,YAAY;AAAA,QACtB,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAGhB;AACA,SACEF,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,KAAK;AAAA,MACX,QAAO;AAAA,MACP,KAAI;AAAA,MACJ,WAAU;AAAA,MAEV,UAAA;AAAA,QAAAE,2BAAAA,IAACS,KAAAA,MAAA,EAAK,MAAMC,OAAAA,QAAQ,WAAU,qBAAoB;AAAA,QAClDV,2BAAAA,IAAC,UAAK,WAAU,YAAY,eAAK,YAAY,KAAK,aAAa,OAAA,CAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG5E;;"}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { UIMessage } from './types';
|
|
2
|
-
|
|
2
|
+
/** Callbacks the conversation threads down to each message. */
|
|
3
|
+
export type MessageActionHandlers = {
|
|
4
|
+
/** Re-generate the assistant message with the given id. */
|
|
5
|
+
onRegenerate?: ((messageId: string) => void) | undefined;
|
|
6
|
+
/** Respond to a tool approval request. */
|
|
7
|
+
onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;
|
|
8
|
+
};
|
|
9
|
+
export type MessageProps = MessageActionHandlers & {
|
|
3
10
|
message: UIMessage;
|
|
4
11
|
className?: string;
|
|
5
12
|
};
|
|
6
13
|
/** Renders one chat message. User messages are right-aligned bubbles; assistant
|
|
7
|
-
* messages render text
|
|
8
|
-
*
|
|
9
|
-
export declare function Message({ message, className }: MessageProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
* messages render text as markdown, reasoning and tool parts inline, file parts
|
|
15
|
+
* as thumbnails/chips, and a hover action row (copy / regenerate). */
|
|
16
|
+
export declare function Message({ message, className, onRegenerate, onApprove }: MessageProps): import("react/jsx-runtime").JSX.Element;
|
|
10
17
|
//# sourceMappingURL=Message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Message.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Message.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AASrD,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAAG;IAClC,2DAA2D;IAC3D,YAAY,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzD,0CAA0C;IAC1C,SAAS,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC5F,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,qBAAqB,GAAG;IACjD,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;uEAEuE;AACvE,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,YAAY,2CAuCpF"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { Icon } from "../Icon.js";
|
|
3
4
|
import { Markdown } from "../Markdown.js";
|
|
4
5
|
import { ToolCall } from "./ToolCall.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import { MessageActions } from "./MessageActions.js";
|
|
7
|
+
import { Reasoning } from "./Reasoning.js";
|
|
8
|
+
import { isReasoningPart, isFilePart, isDynamicToolPart, isTypedToolPart } from "./types.js";
|
|
9
|
+
import { UiFile } from "../../icons/components/UiFile.js";
|
|
10
|
+
function Message({ message, className, onRegenerate, onApprove }) {
|
|
7
11
|
const isUser = message.role === "user";
|
|
8
|
-
|
|
12
|
+
const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
13
|
+
return /* @__PURE__ */ jsxs(
|
|
9
14
|
"div",
|
|
10
15
|
{
|
|
11
16
|
className: cn(
|
|
@@ -13,31 +18,85 @@ function Message({ message, className }) {
|
|
|
13
18
|
isUser ? "ml-auto items-end" : "items-start",
|
|
14
19
|
className
|
|
15
20
|
),
|
|
16
|
-
children:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ jsx(
|
|
23
|
+
"div",
|
|
24
|
+
{
|
|
25
|
+
className: cn(
|
|
26
|
+
"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
|
|
27
|
+
isUser && "rounded-lg bg-secondary px-4 py-3 text-secondary-foreground"
|
|
28
|
+
),
|
|
29
|
+
children: message.parts.map((part, i) => /* @__PURE__ */ jsx(
|
|
30
|
+
MessagePart,
|
|
31
|
+
{
|
|
32
|
+
part,
|
|
33
|
+
isUser,
|
|
34
|
+
onApprove
|
|
35
|
+
},
|
|
36
|
+
`${message.id}-${i}`
|
|
37
|
+
))
|
|
38
|
+
}
|
|
39
|
+
),
|
|
40
|
+
!isUser && text && /* @__PURE__ */ jsx(
|
|
41
|
+
MessageActions,
|
|
42
|
+
{
|
|
43
|
+
text,
|
|
44
|
+
onRegenerate: onRegenerate ? () => onRegenerate(message.id) : void 0
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
]
|
|
26
48
|
}
|
|
27
49
|
);
|
|
28
50
|
}
|
|
29
|
-
function MessagePart({
|
|
51
|
+
function MessagePart({
|
|
52
|
+
part,
|
|
53
|
+
isUser,
|
|
54
|
+
onApprove
|
|
55
|
+
}) {
|
|
30
56
|
if (part.type === "text") {
|
|
31
57
|
if (isUser) {
|
|
32
58
|
return /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-words", children: part.text });
|
|
33
59
|
}
|
|
34
60
|
return /* @__PURE__ */ jsx(Markdown, { text: part.text });
|
|
35
61
|
}
|
|
62
|
+
if (isReasoningPart(part)) {
|
|
63
|
+
return /* @__PURE__ */ jsx(Reasoning, { text: part.text });
|
|
64
|
+
}
|
|
65
|
+
if (isFilePart(part)) {
|
|
66
|
+
return /* @__PURE__ */ jsx(FilePart, { part });
|
|
67
|
+
}
|
|
36
68
|
if (isDynamicToolPart(part) || isTypedToolPart(part)) {
|
|
37
|
-
return /* @__PURE__ */ jsx(ToolCall, { part });
|
|
69
|
+
return /* @__PURE__ */ jsx(ToolCall, { part, onApprove });
|
|
38
70
|
}
|
|
39
71
|
return null;
|
|
40
72
|
}
|
|
73
|
+
function FilePart({ part }) {
|
|
74
|
+
var _a;
|
|
75
|
+
const isImage = (_a = part.mediaType) == null ? void 0 : _a.startsWith("image/");
|
|
76
|
+
if (isImage && part.url) {
|
|
77
|
+
return /* @__PURE__ */ jsx(
|
|
78
|
+
"img",
|
|
79
|
+
{
|
|
80
|
+
src: part.url,
|
|
81
|
+
alt: part.filename ?? "attachment",
|
|
82
|
+
className: "max-h-48 max-w-full rounded-md border border-border"
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return /* @__PURE__ */ jsxs(
|
|
87
|
+
"a",
|
|
88
|
+
{
|
|
89
|
+
href: part.url,
|
|
90
|
+
target: "_blank",
|
|
91
|
+
rel: "noopener noreferrer",
|
|
92
|
+
className: "flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:text-foreground",
|
|
93
|
+
children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiFile, className: "size-3.5 shrink-0" }),
|
|
95
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: part.filename ?? part.mediaType ?? "file" })
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
41
100
|
export {
|
|
42
101
|
Message
|
|
43
102
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.js","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport type { UIMessage } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"Message.js","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n} from \"./types\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;\n};\n\nexport type MessageProps = MessageActionHandlers & {\n message: UIMessage;\n className?: string;\n};\n\n/** Renders one chat message. User messages are right-aligned bubbles; assistant\n * messages render text as markdown, reasoning and tool parts inline, file parts\n * as thumbnails/chips, and a hover action row (copy / regenerate). */\nexport function Message({ message, className, onRegenerate, onApprove }: MessageProps) {\n const isUser = message.role === \"user\";\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\");\n\n return (\n <div\n className={cn(\n \"group flex w-full max-w-[95%] flex-col gap-2\",\n isUser ? \"ml-auto items-end\" : \"items-start\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm\",\n isUser && \"rounded-lg bg-secondary px-4 py-3 text-secondary-foreground\",\n )}\n >\n {message.parts.map((part, i) => (\n <MessagePart\n key={`${message.id}-${i}`}\n part={part}\n isUser={isUser}\n onApprove={onApprove}\n />\n ))}\n </div>\n\n {!isUser && text && (\n <MessageActions\n text={text}\n onRegenerate={onRegenerate ? () => onRegenerate(message.id) : undefined}\n />\n )}\n </div>\n );\n}\n\nfunction MessagePart({\n part,\n isUser,\n onApprove,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n}) {\n if (part.type === \"text\") {\n if (isUser) {\n return <span className=\"whitespace-pre-wrap break-words\">{part.text}</span>;\n }\n return <Markdown text={part.text} />;\n }\n if (isReasoningPart(part)) {\n return <Reasoning text={part.text} />;\n }\n if (isFilePart(part)) {\n return <FilePart part={part} />;\n }\n if (isDynamicToolPart(part) || isTypedToolPart(part)) {\n return <ToolCall part={part as AnyToolPart} onApprove={onApprove} />;\n }\n return null;\n}\n\n/** Renders an attachment: images inline as a thumbnail, everything else as a\n * labelled file chip. */\nfunction FilePart({ part }: { part: FileUIPart }) {\n const isImage = part.mediaType?.startsWith(\"image/\");\n if (isImage && part.url) {\n return (\n <img\n src={part.url}\n alt={part.filename ?? \"attachment\"}\n className=\"max-h-48 max-w-full rounded-md border border-border\"\n />\n );\n }\n return (\n <a\n href={part.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFile} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate\">{part.filename ?? part.mediaType ?? \"file\"}</span>\n </a>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAgCO,SAAS,QAAQ,EAAE,SAAS,WAAW,cAAc,aAA2B;AACrF,QAAM,SAAS,QAAQ,SAAS;AAChC,QAAM,OAAO,QAAQ,MAClB,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAO,EAAuB,IAAI,EACvC,KAAK,EAAE;AAEV,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,sBAAsB;AAAA,QAC/B;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,UAAU;AAAA,YAAA;AAAA,YAGX,UAAA,QAAQ,MAAM,IAAI,CAAC,MAAM,MACxB;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC;AAAA,gBACA;AAAA,gBACA;AAAA,cAAA;AAAA,cAHK,GAAG,QAAQ,EAAE,IAAI,CAAC;AAAA,YAAA,CAK1B;AAAA,UAAA;AAAA,QAAA;AAAA,QAGF,CAAC,UAAU,QACV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,cAAc,eAAe,MAAM,aAAa,QAAQ,EAAE,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAChE;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,KAAK,SAAS,QAAQ;AACxB,QAAI,QAAQ;AACV,aAAO,oBAAC,QAAA,EAAK,WAAU,mCAAmC,eAAK,MAAK;AAAA,IACtE;AACA,WAAO,oBAAC,UAAA,EAAS,MAAM,KAAK,KAAA,CAAM;AAAA,EACpC;AACA,MAAI,gBAAgB,IAAI,GAAG;AACzB,WAAO,oBAAC,WAAA,EAAU,MAAM,KAAK,KAAA,CAAM;AAAA,EACrC;AACA,MAAI,WAAW,IAAI,GAAG;AACpB,WAAO,oBAAC,YAAS,MAAY;AAAA,EAC/B;AACA,MAAI,kBAAkB,IAAI,KAAK,gBAAgB,IAAI,GAAG;AACpD,WAAO,oBAAC,UAAA,EAAS,MAA2B,UAAA,CAAsB;AAAA,EACpE;AACA,SAAO;AACT;AAIA,SAAS,SAAS,EAAE,QAA8B;;AAChD,QAAM,WAAU,UAAK,cAAL,mBAAgB,WAAW;AAC3C,MAAI,WAAW,KAAK,KAAK;AACvB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK,KAAK;AAAA,QACV,KAAK,KAAK,YAAY;AAAA,QACtB,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAGhB;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,KAAK;AAAA,MACX,QAAO;AAAA,MACP,KAAI;AAAA,MACJ,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,MAAA,EAAK,MAAM,QAAQ,WAAU,qBAAoB;AAAA,QAClD,oBAAC,UAAK,WAAU,YAAY,eAAK,YAAY,KAAK,aAAa,OAAA,CAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG5E;"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
const Icon = require("../Icon.cjs");
|
|
7
|
+
const UiCheck = require("../../icons/components/UiCheck.cjs");
|
|
8
|
+
const UiCopy = require("../../icons/components/UiCopy.cjs");
|
|
9
|
+
const UiRefresh = require("../../icons/components/UiRefresh.cjs");
|
|
10
|
+
function MessageActions({ text, onRegenerate, className }) {
|
|
11
|
+
const [copied, setCopied] = react.useState(false);
|
|
12
|
+
const copy = async () => {
|
|
13
|
+
try {
|
|
14
|
+
await navigator.clipboard.writeText(text);
|
|
15
|
+
setCopied(true);
|
|
16
|
+
setTimeout(() => setCopied(false), 1500);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.warn("clicky-ui: copy to clipboard failed", err);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: utils.cn(
|
|
25
|
+
"flex items-center gap-1 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
|
|
26
|
+
className
|
|
27
|
+
),
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30
|
+
"button",
|
|
31
|
+
{
|
|
32
|
+
type: "button",
|
|
33
|
+
"aria-label": copied ? "Copied" : "Copy message",
|
|
34
|
+
onClick: copy,
|
|
35
|
+
className: "rounded p-1 hover:bg-accent hover:text-accent-foreground",
|
|
36
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: copied ? UiCheck.UiCheck : UiCopy.UiCopy, className: utils.cn("size-3.5", copied && "text-emerald-600") })
|
|
37
|
+
}
|
|
38
|
+
),
|
|
39
|
+
onRegenerate && /* @__PURE__ */ jsxRuntime.jsx(
|
|
40
|
+
"button",
|
|
41
|
+
{
|
|
42
|
+
type: "button",
|
|
43
|
+
"aria-label": "Regenerate response",
|
|
44
|
+
onClick: onRegenerate,
|
|
45
|
+
className: "rounded p-1 hover:bg-accent hover:text-accent-foreground",
|
|
46
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiRefresh.UiRefresh, className: "size-3.5" })
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
exports.MessageActions = MessageActions;
|
|
54
|
+
//# sourceMappingURL=MessageActions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageActions.cjs","sources":["../../../src/data/chat/MessageActions.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiCopy, UiCheck, UiRefresh } from \"../../icons\";\n\nexport type MessageActionsProps = {\n /** Plain-text content of the message, copied to the clipboard. */\n text: string;\n /** Called to re-generate this assistant message. Omit to hide the control. */\n onRegenerate?: (() => void) | undefined;\n className?: string;\n};\n\n/** Hover actions for an assistant message: copy its text and (optionally)\n * regenerate it. */\nexport function MessageActions({ text, onRegenerate, className }: MessageActionsProps) {\n const [copied, setCopied] = useState(false);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch (err) {\n console.warn(\"clicky-ui: copy to clipboard failed\", err);\n }\n };\n\n return (\n <div\n className={cn(\n \"flex items-center gap-1 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100\",\n className,\n )}\n >\n <button\n type=\"button\"\n aria-label={copied ? \"Copied\" : \"Copy message\"}\n onClick={copy}\n className=\"rounded p-1 hover:bg-accent hover:text-accent-foreground\"\n >\n <Icon icon={copied ? UiCheck : UiCopy} className={cn(\"size-3.5\", copied && \"text-emerald-600\")} />\n </button>\n {onRegenerate && (\n <button\n type=\"button\"\n aria-label=\"Regenerate response\"\n onClick={onRegenerate}\n className=\"rounded p-1 hover:bg-accent hover:text-accent-foreground\"\n >\n <Icon icon={UiRefresh} className=\"size-3.5\" />\n </button>\n )}\n </div>\n );\n}\n"],"names":["useState","jsxs","cn","jsx","Icon","UiCheck","UiCopy","UiRefresh"],"mappings":";;;;;;;;;AAeO,SAAS,eAAe,EAAE,MAAM,cAAc,aAAkC;AACrF,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA,SAAS,KAAK;AAE1C,QAAM,OAAO,YAAY;AACvB,QAAI;AACF,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,IACzC,SAAS,KAAK;AACZ,cAAQ,KAAK,uCAAuC,GAAG;AAAA,IACzD;AAAA,EACF;AAEA,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAY,SAAS,WAAW;AAAA,YAChC,SAAS;AAAA,YACT,WAAU;AAAA,YAEV,UAAAA,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAM,SAASC,QAAAA,UAAUC,OAAAA,QAAQ,WAAWJ,MAAAA,GAAG,YAAY,UAAU,kBAAkB,EAAA,CAAG;AAAA,UAAA;AAAA,QAAA;AAAA,QAEjG,gBACCC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS;AAAA,YACT,WAAU;AAAA,YAEV,UAAAA,2BAAAA,IAACC,WAAA,EAAK,MAAMG,UAAAA,WAAW,WAAU,WAAA,CAAW;AAAA,UAAA;AAAA,QAAA;AAAA,MAC9C;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type MessageActionsProps = {
|
|
2
|
+
/** Plain-text content of the message, copied to the clipboard. */
|
|
3
|
+
text: string;
|
|
4
|
+
/** Called to re-generate this assistant message. Omit to hide the control. */
|
|
5
|
+
onRegenerate?: (() => void) | undefined;
|
|
6
|
+
className?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Hover actions for an assistant message: copy its text and (optionally)
|
|
9
|
+
* regenerate it. */
|
|
10
|
+
export declare function MessageActions({ text, onRegenerate, className }: MessageActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=MessageActions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageActions.d.ts","sourceRoot":"","sources":["../../../src/data/chat/MessageActions.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,mBAAmB,GAAG;IAChC,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;qBACqB;AACrB,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAwCpF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { Icon } from "../Icon.js";
|
|
5
|
+
import { UiCheck } from "../../icons/components/UiCheck.js";
|
|
6
|
+
import { UiCopy } from "../../icons/components/UiCopy.js";
|
|
7
|
+
import { UiRefresh } from "../../icons/components/UiRefresh.js";
|
|
8
|
+
function MessageActions({ text, onRegenerate, className }) {
|
|
9
|
+
const [copied, setCopied] = useState(false);
|
|
10
|
+
const copy = async () => {
|
|
11
|
+
try {
|
|
12
|
+
await navigator.clipboard.writeText(text);
|
|
13
|
+
setCopied(true);
|
|
14
|
+
setTimeout(() => setCopied(false), 1500);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.warn("clicky-ui: copy to clipboard failed", err);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
return /* @__PURE__ */ jsxs(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
className: cn(
|
|
23
|
+
"flex items-center gap-1 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
|
|
24
|
+
className
|
|
25
|
+
),
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsx(
|
|
28
|
+
"button",
|
|
29
|
+
{
|
|
30
|
+
type: "button",
|
|
31
|
+
"aria-label": copied ? "Copied" : "Copy message",
|
|
32
|
+
onClick: copy,
|
|
33
|
+
className: "rounded p-1 hover:bg-accent hover:text-accent-foreground",
|
|
34
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: copied ? UiCheck : UiCopy, className: cn("size-3.5", copied && "text-emerald-600") })
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
onRegenerate && /* @__PURE__ */ jsx(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
type: "button",
|
|
41
|
+
"aria-label": "Regenerate response",
|
|
42
|
+
onClick: onRegenerate,
|
|
43
|
+
className: "rounded p-1 hover:bg-accent hover:text-accent-foreground",
|
|
44
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: UiRefresh, className: "size-3.5" })
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
MessageActions
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=MessageActions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageActions.js","sources":["../../../src/data/chat/MessageActions.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiCopy, UiCheck, UiRefresh } from \"../../icons\";\n\nexport type MessageActionsProps = {\n /** Plain-text content of the message, copied to the clipboard. */\n text: string;\n /** Called to re-generate this assistant message. Omit to hide the control. */\n onRegenerate?: (() => void) | undefined;\n className?: string;\n};\n\n/** Hover actions for an assistant message: copy its text and (optionally)\n * regenerate it. */\nexport function MessageActions({ text, onRegenerate, className }: MessageActionsProps) {\n const [copied, setCopied] = useState(false);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch (err) {\n console.warn(\"clicky-ui: copy to clipboard failed\", err);\n }\n };\n\n return (\n <div\n className={cn(\n \"flex items-center gap-1 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100\",\n className,\n )}\n >\n <button\n type=\"button\"\n aria-label={copied ? \"Copied\" : \"Copy message\"}\n onClick={copy}\n className=\"rounded p-1 hover:bg-accent hover:text-accent-foreground\"\n >\n <Icon icon={copied ? UiCheck : UiCopy} className={cn(\"size-3.5\", copied && \"text-emerald-600\")} />\n </button>\n {onRegenerate && (\n <button\n type=\"button\"\n aria-label=\"Regenerate response\"\n onClick={onRegenerate}\n className=\"rounded p-1 hover:bg-accent hover:text-accent-foreground\"\n >\n <Icon icon={UiRefresh} className=\"size-3.5\" />\n </button>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAeO,SAAS,eAAe,EAAE,MAAM,cAAc,aAAkC;AACrF,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,QAAM,OAAO,YAAY;AACvB,QAAI;AACF,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,IACzC,SAAS,KAAK;AACZ,cAAQ,KAAK,uCAAuC,GAAG;AAAA,IACzD;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAY,SAAS,WAAW;AAAA,YAChC,SAAS;AAAA,YACT,WAAU;AAAA,YAEV,UAAA,oBAAC,MAAA,EAAK,MAAM,SAAS,UAAU,QAAQ,WAAW,GAAG,YAAY,UAAU,kBAAkB,EAAA,CAAG;AAAA,UAAA;AAAA,QAAA;AAAA,QAEjG,gBACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS;AAAA,YACT,WAAU;AAAA,YAEV,UAAA,oBAAC,MAAA,EAAK,MAAM,WAAW,WAAU,WAAA,CAAW;AAAA,UAAA;AAAA,QAAA;AAAA,MAC9C;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const select = require("../../components/select.cjs");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
function ModelSelector({ models, value, onChange, className }) {
|
|
7
|
+
if (models.length === 0) return null;
|
|
8
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9
|
+
select.Select,
|
|
10
|
+
{
|
|
11
|
+
"aria-label": "Model",
|
|
12
|
+
value: value ?? "",
|
|
13
|
+
onChange: (e) => onChange(e.target.value),
|
|
14
|
+
className: utils.cn("h-8 text-xs", className),
|
|
15
|
+
options: models.map((m) => ({
|
|
16
|
+
value: m.id,
|
|
17
|
+
label: m.reasoning ? `${m.label} ·🧠` : m.label,
|
|
18
|
+
disabled: m.configured === false
|
|
19
|
+
}))
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function EffortSelector({ efforts, value, onChange, className }) {
|
|
24
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
25
|
+
select.Select,
|
|
26
|
+
{
|
|
27
|
+
"aria-label": "Reasoning effort",
|
|
28
|
+
value: value ?? "",
|
|
29
|
+
onChange: (e) => onChange(e.target.value),
|
|
30
|
+
className: utils.cn("h-8 w-auto text-xs", className),
|
|
31
|
+
options: [
|
|
32
|
+
{ value: "", label: "No reasoning" },
|
|
33
|
+
...efforts.map((e) => {
|
|
34
|
+
var _a;
|
|
35
|
+
return { value: e, label: `${(_a = e[0]) == null ? void 0 : _a.toUpperCase()}${e.slice(1)} reasoning` };
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
exports.EffortSelector = EffortSelector;
|
|
42
|
+
exports.ModelSelector = ModelSelector;
|
|
43
|
+
//# sourceMappingURL=ModelSelector.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelSelector.cjs","sources":["../../../src/data/chat/ModelSelector.tsx"],"sourcesContent":["import { Select } from \"../../components/select\";\nimport { cn } from \"../../lib/utils\";\nimport type { ChatModel } from \"./types\";\n\nexport type ModelSelectorProps = {\n models: ChatModel[];\n /** Currently selected model id. */\n value?: string | undefined;\n onChange: (id: string) => void;\n className?: string;\n};\n\n/** A model picker driven by the backend model menu. Models whose provider is\n * not configured are disabled rather than hidden, so the menu communicates\n * what would be available with the right API key. */\nexport function ModelSelector({ models, value, onChange, className }: ModelSelectorProps) {\n if (models.length === 0) return null;\n return (\n <Select\n aria-label=\"Model\"\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n className={cn(\"h-8 text-xs\", className)}\n options={models.map((m) => ({\n value: m.id,\n label: m.reasoning ? `${m.label} ·🧠` : m.label,\n disabled: m.configured === false,\n }))}\n />\n );\n}\n\nexport type EffortSelectorProps = {\n efforts: string[];\n value?: string | undefined;\n onChange: (effort: string) => void;\n className?: string;\n};\n\n/** Reasoning-effort picker, shown only for reasoning-capable models. The empty\n * value means \"no extended thinking\". */\nexport function EffortSelector({ efforts, value, onChange, className }: EffortSelectorProps) {\n return (\n <Select\n aria-label=\"Reasoning effort\"\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n className={cn(\"h-8 w-auto text-xs\", className)}\n options={[\n { value: \"\", label: \"No reasoning\" },\n ...efforts.map((e) => ({ value: e, label: `${e[0]?.toUpperCase()}${e.slice(1)} reasoning` })),\n ]}\n />\n );\n}\n"],"names":["jsx","Select","cn"],"mappings":";;;;;AAeO,SAAS,cAAc,EAAE,QAAQ,OAAO,UAAU,aAAiC;AACxF,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SACEA,2BAAAA;AAAAA,IAACC,OAAAA;AAAAA,IAAA;AAAA,MACC,cAAW;AAAA,MACX,OAAO,SAAS;AAAA,MAChB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAWC,MAAAA,GAAG,eAAe,SAAS;AAAA,MACtC,SAAS,OAAO,IAAI,CAAC,OAAO;AAAA,QAC1B,OAAO,EAAE;AAAA,QACT,OAAO,EAAE,YAAY,GAAG,EAAE,KAAK,SAAS,EAAE;AAAA,QAC1C,UAAU,EAAE,eAAe;AAAA,MAAA,EAC3B;AAAA,IAAA;AAAA,EAAA;AAGR;AAWO,SAAS,eAAe,EAAE,SAAS,OAAO,UAAU,aAAkC;AAC3F,SACEF,2BAAAA;AAAAA,IAACC,OAAAA;AAAAA,IAAA;AAAA,MACC,cAAW;AAAA,MACX,OAAO,SAAS;AAAA,MAChB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAWC,MAAAA,GAAG,sBAAsB,SAAS;AAAA,MAC7C,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,eAAA;AAAA,QACpB,GAAG,QAAQ,IAAI,CAAC;;AAAO,mBAAE,OAAO,GAAG,OAAO,IAAG,OAAE,CAAC,MAAH,mBAAM,aAAa,GAAG,EAAE,MAAM,CAAC,CAAC;SAAe;AAAA,MAAA;AAAA,IAC9F;AAAA,EAAA;AAGN;;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ChatModel } from './types';
|
|
2
|
+
export type ModelSelectorProps = {
|
|
3
|
+
models: ChatModel[];
|
|
4
|
+
/** Currently selected model id. */
|
|
5
|
+
value?: string | undefined;
|
|
6
|
+
onChange: (id: string) => void;
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
/** A model picker driven by the backend model menu. Models whose provider is
|
|
10
|
+
* not configured are disabled rather than hidden, so the menu communicates
|
|
11
|
+
* what would be available with the right API key. */
|
|
12
|
+
export declare function ModelSelector({ models, value, onChange, className }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
export type EffortSelectorProps = {
|
|
14
|
+
efforts: string[];
|
|
15
|
+
value?: string | undefined;
|
|
16
|
+
onChange: (effort: string) => void;
|
|
17
|
+
className?: string;
|
|
18
|
+
};
|
|
19
|
+
/** Reasoning-effort picker, shown only for reasoning-capable models. The empty
|
|
20
|
+
* value means "no extended thinking". */
|
|
21
|
+
export declare function EffortSelector({ efforts, value, onChange, className }: EffortSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=ModelSelector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelSelector.d.ts","sourceRoot":"","sources":["../../../src/data/chat/ModelSelector.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;sDAEsD;AACtD,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,kBAAkB,kDAevF;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;0CAC0C;AAC1C,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAa1F"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Select } from "../../components/select.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
function ModelSelector({ models, value, onChange, className }) {
|
|
5
|
+
if (models.length === 0) return null;
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
Select,
|
|
8
|
+
{
|
|
9
|
+
"aria-label": "Model",
|
|
10
|
+
value: value ?? "",
|
|
11
|
+
onChange: (e) => onChange(e.target.value),
|
|
12
|
+
className: cn("h-8 text-xs", className),
|
|
13
|
+
options: models.map((m) => ({
|
|
14
|
+
value: m.id,
|
|
15
|
+
label: m.reasoning ? `${m.label} ·🧠` : m.label,
|
|
16
|
+
disabled: m.configured === false
|
|
17
|
+
}))
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
function EffortSelector({ efforts, value, onChange, className }) {
|
|
22
|
+
return /* @__PURE__ */ jsx(
|
|
23
|
+
Select,
|
|
24
|
+
{
|
|
25
|
+
"aria-label": "Reasoning effort",
|
|
26
|
+
value: value ?? "",
|
|
27
|
+
onChange: (e) => onChange(e.target.value),
|
|
28
|
+
className: cn("h-8 w-auto text-xs", className),
|
|
29
|
+
options: [
|
|
30
|
+
{ value: "", label: "No reasoning" },
|
|
31
|
+
...efforts.map((e) => {
|
|
32
|
+
var _a;
|
|
33
|
+
return { value: e, label: `${(_a = e[0]) == null ? void 0 : _a.toUpperCase()}${e.slice(1)} reasoning` };
|
|
34
|
+
})
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
EffortSelector,
|
|
41
|
+
ModelSelector
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=ModelSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelSelector.js","sources":["../../../src/data/chat/ModelSelector.tsx"],"sourcesContent":["import { Select } from \"../../components/select\";\nimport { cn } from \"../../lib/utils\";\nimport type { ChatModel } from \"./types\";\n\nexport type ModelSelectorProps = {\n models: ChatModel[];\n /** Currently selected model id. */\n value?: string | undefined;\n onChange: (id: string) => void;\n className?: string;\n};\n\n/** A model picker driven by the backend model menu. Models whose provider is\n * not configured are disabled rather than hidden, so the menu communicates\n * what would be available with the right API key. */\nexport function ModelSelector({ models, value, onChange, className }: ModelSelectorProps) {\n if (models.length === 0) return null;\n return (\n <Select\n aria-label=\"Model\"\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n className={cn(\"h-8 text-xs\", className)}\n options={models.map((m) => ({\n value: m.id,\n label: m.reasoning ? `${m.label} ·🧠` : m.label,\n disabled: m.configured === false,\n }))}\n />\n );\n}\n\nexport type EffortSelectorProps = {\n efforts: string[];\n value?: string | undefined;\n onChange: (effort: string) => void;\n className?: string;\n};\n\n/** Reasoning-effort picker, shown only for reasoning-capable models. The empty\n * value means \"no extended thinking\". */\nexport function EffortSelector({ efforts, value, onChange, className }: EffortSelectorProps) {\n return (\n <Select\n aria-label=\"Reasoning effort\"\n value={value ?? \"\"}\n onChange={(e) => onChange(e.target.value)}\n className={cn(\"h-8 w-auto text-xs\", className)}\n options={[\n { value: \"\", label: \"No reasoning\" },\n ...efforts.map((e) => ({ value: e, label: `${e[0]?.toUpperCase()}${e.slice(1)} reasoning` })),\n ]}\n />\n );\n}\n"],"names":[],"mappings":";;;AAeO,SAAS,cAAc,EAAE,QAAQ,OAAO,UAAU,aAAiC;AACxF,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAW;AAAA,MACX,OAAO,SAAS;AAAA,MAChB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAW,GAAG,eAAe,SAAS;AAAA,MACtC,SAAS,OAAO,IAAI,CAAC,OAAO;AAAA,QAC1B,OAAO,EAAE;AAAA,QACT,OAAO,EAAE,YAAY,GAAG,EAAE,KAAK,SAAS,EAAE;AAAA,QAC1C,UAAU,EAAE,eAAe;AAAA,MAAA,EAC3B;AAAA,IAAA;AAAA,EAAA;AAGR;AAWO,SAAS,eAAe,EAAE,SAAS,OAAO,UAAU,aAAkC;AAC3F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAW;AAAA,MACX,OAAO,SAAS;AAAA,MAChB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAW,GAAG,sBAAsB,SAAS;AAAA,MAC7C,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,eAAA;AAAA,QACpB,GAAG,QAAQ,IAAI,CAAC;;AAAO,mBAAE,OAAO,GAAG,OAAO,IAAG,OAAE,CAAC,MAAH,mBAAM,aAAa,GAAG,EAAE,MAAM,CAAC,CAAC;SAAe;AAAA,MAAA;AAAA,IAC9F;AAAA,EAAA;AAGN;"}
|
|
@@ -5,6 +5,7 @@ const react = require("react");
|
|
|
5
5
|
const utils = require("../../lib/utils.cjs");
|
|
6
6
|
const button = require("../../components/button.cjs");
|
|
7
7
|
const Icon = require("../Icon.cjs");
|
|
8
|
+
const Attachment = require("./Attachment.cjs");
|
|
8
9
|
const UiStop = require("../../icons/components/UiStop.cjs");
|
|
9
10
|
const UiArrowUp = require("../../icons/components/UiArrowUp.cjs");
|
|
10
11
|
function PromptInput({
|
|
@@ -12,15 +13,19 @@ function PromptInput({
|
|
|
12
13
|
onStop,
|
|
13
14
|
status,
|
|
14
15
|
placeholder = "What would you like to know?",
|
|
16
|
+
enableAttachments = false,
|
|
17
|
+
toolbar,
|
|
15
18
|
className
|
|
16
19
|
}) {
|
|
17
20
|
const [value, setValue] = react.useState("");
|
|
21
|
+
const [files, setFiles] = react.useState([]);
|
|
18
22
|
const isGenerating = status === "submitted" || status === "streaming";
|
|
19
23
|
const submit = () => {
|
|
20
24
|
const text = value.trim();
|
|
21
|
-
if (!text || isGenerating) return;
|
|
22
|
-
onSubmit(text);
|
|
25
|
+
if (!text && files.length === 0 || isGenerating) return;
|
|
26
|
+
onSubmit(text, files);
|
|
23
27
|
setValue("");
|
|
28
|
+
setFiles([]);
|
|
24
29
|
};
|
|
25
30
|
const handleSubmit = (e) => {
|
|
26
31
|
e.preventDefault();
|
|
@@ -32,27 +37,35 @@ function PromptInput({
|
|
|
32
37
|
submit();
|
|
33
38
|
}
|
|
34
39
|
};
|
|
40
|
+
const canSubmit = value.trim().length > 0 || files.length > 0;
|
|
35
41
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36
42
|
"form",
|
|
37
43
|
{
|
|
38
44
|
onSubmit: handleSubmit,
|
|
39
45
|
className: utils.cn(
|
|
40
|
-
"flex
|
|
46
|
+
"flex flex-col gap-2 rounded-lg border border-input bg-background p-2",
|
|
41
47
|
className
|
|
42
48
|
),
|
|
43
49
|
children: [
|
|
44
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
enableAttachments && /* @__PURE__ */ jsxRuntime.jsx(Attachment.AttachmentList, { files, onRemove: (i) => setFiles((f) => f.filter((_, j) => j !== i)) }),
|
|
51
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
52
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53
|
+
"textarea",
|
|
54
|
+
{
|
|
55
|
+
value,
|
|
56
|
+
onChange: (e) => setValue(e.target.value),
|
|
57
|
+
onKeyDown: handleKeyDown,
|
|
58
|
+
placeholder,
|
|
59
|
+
rows: 1,
|
|
60
|
+
className: "max-h-48 min-h-9 flex-1 resize-none bg-transparent px-1 py-1.5 text-sm outline-none placeholder:text-muted-foreground"
|
|
61
|
+
}
|
|
62
|
+
),
|
|
63
|
+
isGenerating && onStop ? /* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "button", size: "icon", variant: "secondary", "aria-label": "Stop", onClick: onStop, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiStop.UiStop, className: "size-4" }) }) : /* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "submit", size: "icon", "aria-label": "Send", disabled: !canSubmit, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiArrowUp.UiArrowUp, className: "size-4" }) })
|
|
64
|
+
] }),
|
|
65
|
+
(toolbar || enableAttachments) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
66
|
+
enableAttachments && /* @__PURE__ */ jsxRuntime.jsx(Attachment.AttachmentButton, { onAdd: (p) => setFiles((f) => [...f, ...p]), disabled: isGenerating }),
|
|
67
|
+
toolbar
|
|
68
|
+
] })
|
|
56
69
|
]
|
|
57
70
|
}
|
|
58
71
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptInput.cjs","sources":["../../../src/data/chat/PromptInput.tsx"],"sourcesContent":["import { useState, type FormEvent, type KeyboardEvent } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../../components/button\";\nimport { Icon } from \"../Icon\";\nimport { UiArrowUp, UiStop } from \"../../icons\";\nimport type { ChatStatus } from \"./types\";\n\nexport type PromptInputProps = {\n /** Called with the trimmed text when the user submits
|
|
1
|
+
{"version":3,"file":"PromptInput.cjs","sources":["../../../src/data/chat/PromptInput.tsx"],"sourcesContent":["import { useState, type FormEvent, type KeyboardEvent, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../../components/button\";\nimport { Icon } from \"../Icon\";\nimport { UiArrowUp, UiStop } from \"../../icons\";\nimport { AttachmentButton, AttachmentList } from \"./Attachment\";\nimport type { ChatStatus, FileUIPart } from \"./types\";\n\nexport type PromptInputProps = {\n /** Called with the trimmed text and any attachments when the user submits. */\n onSubmit: (text: string, files: FileUIPart[]) => void;\n /** Called when the user stops an in-flight generation. */\n onStop?: (() => void) | undefined;\n /** Current chat status; drives the submit/stop button affordance. */\n status?: ChatStatus | undefined;\n placeholder?: string | undefined;\n /** Enables the attachment button and chips. */\n enableAttachments?: boolean | undefined;\n /** Toolbar content (e.g. model/effort selectors) rendered in the footer. */\n toolbar?: ReactNode;\n className?: string | undefined;\n};\n\n/** Auto-growing prompt textarea with a submit/stop button, optional attachment\n * controls, and a toolbar slot (model/effort selectors). Enter submits;\n * Shift+Enter inserts a newline; while streaming the button becomes a stop\n * control. */\nexport function PromptInput({\n onSubmit,\n onStop,\n status,\n placeholder = \"What would you like to know?\",\n enableAttachments = false,\n toolbar,\n className,\n}: PromptInputProps) {\n const [value, setValue] = useState(\"\");\n const [files, setFiles] = useState<FileUIPart[]>([]);\n const isGenerating = status === \"submitted\" || status === \"streaming\";\n\n const submit = () => {\n const text = value.trim();\n if ((!text && files.length === 0) || isGenerating) return;\n onSubmit(text, files);\n setValue(\"\");\n setFiles([]);\n };\n\n const handleSubmit = (e: FormEvent) => {\n e.preventDefault();\n submit();\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.nativeEvent.isComposing) {\n e.preventDefault();\n submit();\n }\n };\n\n const canSubmit = value.trim().length > 0 || files.length > 0;\n\n return (\n <form\n onSubmit={handleSubmit}\n className={cn(\n \"flex flex-col gap-2 rounded-lg border border-input bg-background p-2\",\n className,\n )}\n >\n {enableAttachments && (\n <AttachmentList files={files} onRemove={(i) => setFiles((f) => f.filter((_, j) => j !== i))} />\n )}\n\n <div className=\"flex items-end gap-2\">\n <textarea\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n className=\"max-h-48 min-h-9 flex-1 resize-none bg-transparent px-1 py-1.5 text-sm outline-none placeholder:text-muted-foreground\"\n />\n {isGenerating && onStop ? (\n <Button type=\"button\" size=\"icon\" variant=\"secondary\" aria-label=\"Stop\" onClick={onStop}>\n <Icon icon={UiStop} className=\"size-4\" />\n </Button>\n ) : (\n <Button type=\"submit\" size=\"icon\" aria-label=\"Send\" disabled={!canSubmit}>\n <Icon icon={UiArrowUp} className=\"size-4\" />\n </Button>\n )}\n </div>\n\n {(toolbar || enableAttachments) && (\n <div className=\"flex items-center gap-2\">\n {enableAttachments && (\n <AttachmentButton onAdd={(p) => setFiles((f) => [...f, ...p])} disabled={isGenerating} />\n )}\n {toolbar}\n </div>\n )}\n </form>\n );\n}\n"],"names":["useState","jsxs","cn","AttachmentList","jsx","Button","Icon","UiStop","UiArrowUp","AttachmentButton"],"mappings":";;;;;;;;;;AA2BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAS,EAAE;AACrC,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAuB,CAAA,CAAE;AACnD,QAAM,eAAe,WAAW,eAAe,WAAW;AAE1D,QAAM,SAAS,MAAM;AACnB,UAAM,OAAO,MAAM,KAAA;AACnB,QAAK,CAAC,QAAQ,MAAM,WAAW,KAAM,aAAc;AACnD,aAAS,MAAM,KAAK;AACpB,aAAS,EAAE;AACX,aAAS,CAAA,CAAE;AAAA,EACb;AAEA,QAAM,eAAe,CAAC,MAAiB;AACrC,MAAE,eAAA;AACF,WAAA;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,MAA0C;AAC/D,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,YAAY,CAAC,EAAE,YAAY,aAAa;AAClE,QAAE,eAAA;AACF,aAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,KAAA,EAAO,SAAS,KAAK,MAAM,SAAS;AAE5D,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,UAAU;AAAA,MACV,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGD,UAAA;AAAA,QAAA,oDACEC,2BAAA,EAAe,OAAc,UAAU,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,GAAG;AAAA,QAG/FF,2BAAAA,KAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,UAAAG,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC;AAAA,cACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,cACxC,WAAW;AAAA,cACX;AAAA,cACA,MAAM;AAAA,cACN,WAAU;AAAA,YAAA;AAAA,UAAA;AAAA,UAEX,gBAAgB,SACfA,2BAAAA,IAACC,eAAA,EAAO,MAAK,UAAS,MAAK,QAAO,SAAQ,aAAY,cAAW,QAAO,SAAS,QAC/E,UAAAD,+BAACE,KAAAA,MAAA,EAAK,MAAMC,OAAAA,QAAQ,WAAU,SAAA,CAAS,GACzC,IAEAH,+BAACC,OAAAA,QAAA,EAAO,MAAK,UAAS,MAAK,QAAO,cAAW,QAAO,UAAU,CAAC,WAC7D,UAAAD,2BAAAA,IAACE,KAAAA,MAAA,EAAK,MAAME,qBAAW,WAAU,UAAS,EAAA,CAC5C;AAAA,QAAA,GAEJ;AAAA,SAEE,WAAW,sBACXP,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,UAAA,oDACEQ,WAAAA,kBAAA,EAAiB,OAAO,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,UAAU,cAAc;AAAA,UAExF;AAAA,QAAA,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ChatStatus, FileUIPart } from './types';
|
|
2
3
|
export type PromptInputProps = {
|
|
3
|
-
/** Called with the trimmed text when the user submits
|
|
4
|
-
onSubmit: (text: string) => void;
|
|
4
|
+
/** Called with the trimmed text and any attachments when the user submits. */
|
|
5
|
+
onSubmit: (text: string, files: FileUIPart[]) => void;
|
|
5
6
|
/** Called when the user stops an in-flight generation. */
|
|
6
|
-
onStop?: () => void;
|
|
7
|
+
onStop?: (() => void) | undefined;
|
|
7
8
|
/** Current chat status; drives the submit/stop button affordance. */
|
|
8
9
|
status?: ChatStatus | undefined;
|
|
9
10
|
placeholder?: string | undefined;
|
|
11
|
+
/** Enables the attachment button and chips. */
|
|
12
|
+
enableAttachments?: boolean | undefined;
|
|
13
|
+
/** Toolbar content (e.g. model/effort selectors) rendered in the footer. */
|
|
14
|
+
toolbar?: ReactNode;
|
|
10
15
|
className?: string | undefined;
|
|
11
16
|
};
|
|
12
|
-
/** Auto-growing prompt textarea with a submit/stop button
|
|
13
|
-
*
|
|
14
|
-
* becomes a stop
|
|
15
|
-
|
|
17
|
+
/** Auto-growing prompt textarea with a submit/stop button, optional attachment
|
|
18
|
+
* controls, and a toolbar slot (model/effort selectors). Enter submits;
|
|
19
|
+
* Shift+Enter inserts a newline; while streaming the button becomes a stop
|
|
20
|
+
* control. */
|
|
21
|
+
export declare function PromptInput({ onSubmit, onStop, status, placeholder, enableAttachments, toolbar, className, }: PromptInputProps): import("react/jsx-runtime").JSX.Element;
|
|
16
22
|
//# sourceMappingURL=PromptInput.d.ts.map
|