@flanksource/clicky-ui 0.2.16 → 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.
Files changed (143) hide show
  1. package/dist/chat.cjs +16 -0
  2. package/dist/chat.cjs.map +1 -1
  3. package/dist/chat.js +17 -1
  4. package/dist/chat.js.map +1 -1
  5. package/dist/components/Combobox.cjs +19 -4
  6. package/dist/components/Combobox.cjs.map +1 -1
  7. package/dist/components/Combobox.d.ts.map +1 -1
  8. package/dist/components/Combobox.js +19 -4
  9. package/dist/components/Combobox.js.map +1 -1
  10. package/dist/components/FilterBar.cjs +40 -6
  11. package/dist/components/FilterBar.cjs.map +1 -1
  12. package/dist/components/FilterBar.d.ts +28 -0
  13. package/dist/components/FilterBar.d.ts.map +1 -1
  14. package/dist/components/FilterBar.js +40 -6
  15. package/dist/components/FilterBar.js.map +1 -1
  16. package/dist/components/JsonSchemaForm.cjs +26 -3
  17. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  18. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  19. package/dist/components/JsonSchemaForm.js +26 -3
  20. package/dist/components/JsonSchemaForm.js.map +1 -1
  21. package/dist/components/json-schema-form-preferences.cjs +4 -0
  22. package/dist/components/json-schema-form-preferences.cjs.map +1 -1
  23. package/dist/components/json-schema-form-preferences.d.ts +2 -0
  24. package/dist/components/json-schema-form-preferences.d.ts.map +1 -1
  25. package/dist/components/json-schema-form-preferences.js +4 -0
  26. package/dist/components/json-schema-form-preferences.js.map +1 -1
  27. package/dist/components/json-schema-form-render.cjs +1 -1
  28. package/dist/components/json-schema-form-render.cjs.map +1 -1
  29. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  30. package/dist/components/json-schema-form-render.js +2 -2
  31. package/dist/components/json-schema-form-render.js.map +1 -1
  32. package/dist/components/json-schema-form-types.d.ts +3 -2
  33. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  34. package/dist/components/json-schema-form-utils.cjs +13 -0
  35. package/dist/components/json-schema-form-utils.cjs.map +1 -1
  36. package/dist/components/json-schema-form-utils.d.ts +2 -0
  37. package/dist/components/json-schema-form-utils.d.ts.map +1 -1
  38. package/dist/components/json-schema-form-utils.js +13 -0
  39. package/dist/components/json-schema-form-utils.js.map +1 -1
  40. package/dist/components/loading.cjs +19 -6
  41. package/dist/components/loading.cjs.map +1 -1
  42. package/dist/components/loading.d.ts.map +1 -1
  43. package/dist/components/loading.js +19 -6
  44. package/dist/components/loading.js.map +1 -1
  45. package/dist/data/Markdown.cjs +14 -40
  46. package/dist/data/Markdown.cjs.map +1 -1
  47. package/dist/data/Markdown.d.ts +3 -0
  48. package/dist/data/Markdown.d.ts.map +1 -1
  49. package/dist/data/Markdown.js +14 -40
  50. package/dist/data/Markdown.js.map +1 -1
  51. package/dist/data/TaskProgress.cjs +22 -0
  52. package/dist/data/TaskProgress.cjs.map +1 -1
  53. package/dist/data/TaskProgress.js +22 -0
  54. package/dist/data/TaskProgress.js.map +1 -1
  55. package/dist/data/TaskSnapshot.cjs.map +1 -1
  56. package/dist/data/TaskSnapshot.d.ts +5 -0
  57. package/dist/data/TaskSnapshot.d.ts.map +1 -1
  58. package/dist/data/TaskSnapshot.js.map +1 -1
  59. package/dist/data/Tree.cjs +32 -1
  60. package/dist/data/Tree.cjs.map +1 -1
  61. package/dist/data/Tree.d.ts +9 -1
  62. package/dist/data/Tree.d.ts.map +1 -1
  63. package/dist/data/Tree.js +32 -1
  64. package/dist/data/Tree.js.map +1 -1
  65. package/dist/data/chat/Attachment.cjs +79 -0
  66. package/dist/data/chat/Attachment.cjs.map +1 -0
  67. package/dist/data/chat/Attachment.d.ts +18 -0
  68. package/dist/data/chat/Attachment.d.ts.map +1 -0
  69. package/dist/data/chat/Attachment.js +79 -0
  70. package/dist/data/chat/Attachment.js.map +1 -0
  71. package/dist/data/chat/Chat.cjs +71 -14
  72. package/dist/data/chat/Chat.cjs.map +1 -1
  73. package/dist/data/chat/Chat.d.ts +30 -12
  74. package/dist/data/chat/Chat.d.ts.map +1 -1
  75. package/dist/data/chat/Chat.fixtures.d.ts +9 -0
  76. package/dist/data/chat/Chat.fixtures.d.ts.map +1 -1
  77. package/dist/data/chat/Chat.js +73 -16
  78. package/dist/data/chat/Chat.js.map +1 -1
  79. package/dist/data/chat/Conversation.cjs +32 -14
  80. package/dist/data/chat/Conversation.cjs.map +1 -1
  81. package/dist/data/chat/Conversation.d.ts +5 -3
  82. package/dist/data/chat/Conversation.d.ts.map +1 -1
  83. package/dist/data/chat/Conversation.js +34 -16
  84. package/dist/data/chat/Conversation.js.map +1 -1
  85. package/dist/data/chat/Message.cjs +73 -14
  86. package/dist/data/chat/Message.cjs.map +1 -1
  87. package/dist/data/chat/Message.d.ts +11 -4
  88. package/dist/data/chat/Message.d.ts.map +1 -1
  89. package/dist/data/chat/Message.js +75 -16
  90. package/dist/data/chat/Message.js.map +1 -1
  91. package/dist/data/chat/MessageActions.cjs +54 -0
  92. package/dist/data/chat/MessageActions.cjs.map +1 -0
  93. package/dist/data/chat/MessageActions.d.ts +11 -0
  94. package/dist/data/chat/MessageActions.d.ts.map +1 -0
  95. package/dist/data/chat/MessageActions.js +54 -0
  96. package/dist/data/chat/MessageActions.js.map +1 -0
  97. package/dist/data/chat/ModelSelector.cjs +43 -0
  98. package/dist/data/chat/ModelSelector.cjs.map +1 -0
  99. package/dist/data/chat/ModelSelector.d.ts +22 -0
  100. package/dist/data/chat/ModelSelector.d.ts.map +1 -0
  101. package/dist/data/chat/ModelSelector.js +43 -0
  102. package/dist/data/chat/ModelSelector.js.map +1 -0
  103. package/dist/data/chat/PromptInput.cjs +28 -15
  104. package/dist/data/chat/PromptInput.cjs.map +1 -1
  105. package/dist/data/chat/PromptInput.d.ts +14 -8
  106. package/dist/data/chat/PromptInput.d.ts.map +1 -1
  107. package/dist/data/chat/PromptInput.js +28 -15
  108. package/dist/data/chat/PromptInput.js.map +1 -1
  109. package/dist/data/chat/Reasoning.cjs +37 -0
  110. package/dist/data/chat/Reasoning.cjs.map +1 -0
  111. package/dist/data/chat/Reasoning.d.ts +11 -0
  112. package/dist/data/chat/Reasoning.d.ts.map +1 -0
  113. package/dist/data/chat/Reasoning.js +37 -0
  114. package/dist/data/chat/Reasoning.js.map +1 -0
  115. package/dist/data/chat/Suggestion.cjs +20 -0
  116. package/dist/data/chat/Suggestion.cjs.map +1 -0
  117. package/dist/data/chat/Suggestion.d.ts +10 -0
  118. package/dist/data/chat/Suggestion.d.ts.map +1 -0
  119. package/dist/data/chat/Suggestion.js +20 -0
  120. package/dist/data/chat/Suggestion.js.map +1 -0
  121. package/dist/data/chat/ToolCall.cjs +26 -3
  122. package/dist/data/chat/ToolCall.cjs.map +1 -1
  123. package/dist/data/chat/ToolCall.d.ts +5 -1
  124. package/dist/data/chat/ToolCall.d.ts.map +1 -1
  125. package/dist/data/chat/ToolCall.js +26 -3
  126. package/dist/data/chat/ToolCall.js.map +1 -1
  127. package/dist/data/chat/index.d.ts +7 -2
  128. package/dist/data/chat/index.d.ts.map +1 -1
  129. package/dist/data/chat/types.cjs +16 -0
  130. package/dist/data/chat/types.cjs.map +1 -1
  131. package/dist/data/chat/types.d.ts +29 -2
  132. package/dist/data/chat/types.d.ts.map +1 -1
  133. package/dist/data/chat/types.js +16 -0
  134. package/dist/data/chat/types.js.map +1 -1
  135. package/dist/data/version-info.cjs +3 -3
  136. package/dist/data/version-info.js +3 -3
  137. package/dist/hooks/use-task-run.cjs +33 -20
  138. package/dist/hooks/use-task-run.cjs.map +1 -1
  139. package/dist/hooks/use-task-run.d.ts.map +1 -1
  140. package/dist/hooks/use-task-run.js +33 -20
  141. package/dist/hooks/use-task-run.js.map +1 -1
  142. package/dist/styles.css +1 -1
  143. package/package.json +6 -1
@@ -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 { isDynamicToolPart, isTypedToolPart } from "./types.js";
6
- function Message({ message, className }) {
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
- return /* @__PURE__ */ jsx(
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: /* @__PURE__ */ jsx(
17
- "div",
18
- {
19
- className: cn(
20
- "flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
21
- isUser && "rounded-lg bg-secondary px-4 py-3 text-secondary-foreground"
22
- ),
23
- children: message.parts.map((part, i) => /* @__PURE__ */ jsx(MessagePart, { part, isUser }, `${message.id}-${i}`))
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({ part, isUser }) {
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 { isDynamicToolPart, isTypedToolPart, type AnyToolPart } from \"./types\";\n\nexport type MessageProps = {\n message: UIMessage;\n className?: string;\n};\n\n/** Renders one chat message. User messages are right-aligned bubbles; assistant\n * messages render text parts as markdown and tool parts as collapsible tool\n * calls, in document order. */\nexport function Message({ message, className }: MessageProps) {\n const isUser = message.role === \"user\";\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 key={`${message.id}-${i}`} part={part} isUser={isUser} />\n ))}\n </div>\n </div>\n );\n}\n\nfunction MessagePart({ part, isUser }: { part: UIMessage[\"parts\"][number]; isUser: boolean }) {\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 (isDynamicToolPart(part) || isTypedToolPart(part)) {\n return <ToolCall part={part as AnyToolPart} />;\n }\n return null;\n}\n"],"names":[],"mappings":";;;;;AAcO,SAAS,QAAQ,EAAE,SAAS,aAA2B;AAC5D,QAAM,SAAS,QAAQ,SAAS;AAChC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,sBAAsB;AAAA,QAC/B;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,UAAU;AAAA,UAAA;AAAA,UAGX,kBAAQ,MAAM,IAAI,CAAC,MAAM,MACxB,oBAAC,aAAA,EAAuC,MAAY,OAAA,GAAlC,GAAG,QAAQ,EAAE,IAAI,CAAC,EAAgC,CACrE;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN;AAEA,SAAS,YAAY,EAAE,MAAM,UAAiE;AAC5F,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,kBAAkB,IAAI,KAAK,gBAAgB,IAAI,GAAG;AACpD,WAAO,oBAAC,YAAS,MAA2B;AAAA,EAC9C;AACA,SAAO;AACT;"}
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 items-end gap-2 rounded-lg border border-input bg-background p-2",
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
- "textarea",
46
- {
47
- value,
48
- onChange: (e) => setValue(e.target.value),
49
- onKeyDown: handleKeyDown,
50
- placeholder,
51
- rows: 1,
52
- 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"
53
- }
54
- ),
55
- 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: !value.trim(), children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiArrowUp.UiArrowUp, className: "size-4" }) })
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 a non-empty message. */\n onSubmit: (text: string) => void;\n /** Called when the user stops an in-flight generation. */\n onStop?: () => void;\n /** Current chat status; drives the submit/stop button affordance. */\n status?: ChatStatus | undefined;\n placeholder?: string | undefined;\n className?: string | undefined;\n};\n\n/** Auto-growing prompt textarea with a submit/stop button. Enter (or Cmd/Ctrl+\n * Enter) submits; Shift+Enter inserts a newline. While streaming, the button\n * becomes a stop control. */\nexport function PromptInput({\n onSubmit,\n onStop,\n status,\n placeholder = \"What would you like to know?\",\n className,\n}: PromptInputProps) {\n const [value, setValue] = useState(\"\");\n const isGenerating = status === \"submitted\" || status === \"streaming\";\n\n const submit = () => {\n const text = value.trim();\n if (!text || isGenerating) return;\n onSubmit(text);\n setValue(\"\");\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 return (\n <form\n onSubmit={handleSubmit}\n className={cn(\n \"flex items-end gap-2 rounded-lg border border-input bg-background p-2\",\n className,\n )}\n >\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={!value.trim()}>\n <Icon icon={UiArrowUp} className=\"size-4\" />\n </Button>\n )}\n </form>\n );\n}\n"],"names":["useState","jsxs","cn","jsx","Button","Icon","UiStop","UiArrowUp"],"mappings":";;;;;;;;;AAqBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAqB;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAS,EAAE;AACrC,QAAM,eAAe,WAAW,eAAe,WAAW;AAE1D,QAAM,SAAS,MAAM;AACnB,UAAM,OAAO,MAAM,KAAA;AACnB,QAAI,CAAC,QAAQ,aAAc;AAC3B,aAAS,IAAI;AACb,aAAS,EAAE;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,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,UAAU;AAAA,MACV,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YACxC,WAAW;AAAA,YACX;AAAA,YACA,MAAM;AAAA,YACN,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAEX,gBAAgB,SACfA,2BAAAA,IAACC,iBAAO,MAAK,UAAS,MAAK,QAAO,SAAQ,aAAY,cAAW,QAAO,SAAS,QAC/E,yCAACC,KAAAA,MAAA,EAAK,MAAMC,eAAQ,WAAU,SAAA,CAAS,EAAA,CACzC,mCAECF,OAAAA,QAAA,EAAO,MAAK,UAAS,MAAK,QAAO,cAAW,QAAO,UAAU,CAAC,MAAM,KAAA,GACnE,UAAAD,2BAAAA,IAACE,KAAAA,MAAA,EAAK,MAAME,qBAAW,WAAU,UAAS,EAAA,CAC5C;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
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 { ChatStatus } from './types';
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 a non-empty message. */
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. Enter (or Cmd/Ctrl+
13
- * Enter) submits; Shift+Enter inserts a newline. While streaming, the button
14
- * becomes a stop control. */
15
- export declare function PromptInput({ onSubmit, onStop, status, placeholder, className, }: PromptInputProps): import("react/jsx-runtime").JSX.Element;
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
@@ -1 +1 @@
1
- {"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/data/chat/PromptInput.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,qEAAqE;IACrE,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;8BAE8B;AAC9B,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAA4C,EAC5C,SAAS,GACV,EAAE,gBAAgB,2CAkDlB"}
1
+ {"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/data/chat/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMrF,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IACtD,0DAA0D;IAC1D,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAClC,qEAAqE;IACrE,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;;eAGe;AACf,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAA4C,EAC5C,iBAAyB,EACzB,OAAO,EACP,SAAS,GACV,EAAE,gBAAgB,2CAqElB"}