@flanksource/clicky-ui 0.2.21 → 0.3.1

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 (227) hide show
  1. package/dist/components/Combobox.cjs +5 -2
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts.map +1 -1
  4. package/dist/components/Combobox.js +5 -2
  5. package/dist/components/Combobox.js.map +1 -1
  6. package/dist/components/FilterBar.cjs +280 -253
  7. package/dist/components/FilterBar.cjs.map +1 -1
  8. package/dist/components/FilterBar.d.ts.map +1 -1
  9. package/dist/components/FilterBar.js +280 -253
  10. package/dist/components/FilterBar.js.map +1 -1
  11. package/dist/components/JsonSchemaForm.cjs +3 -1
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  14. package/dist/components/JsonSchemaForm.js +4 -2
  15. package/dist/components/JsonSchemaForm.js.map +1 -1
  16. package/dist/components/MultiSelect.cjs +29 -32
  17. package/dist/components/MultiSelect.cjs.map +1 -1
  18. package/dist/components/MultiSelect.d.ts.map +1 -1
  19. package/dist/components/MultiSelect.js +30 -33
  20. package/dist/components/MultiSelect.js.map +1 -1
  21. package/dist/components/TimeRange.cjs +29 -37
  22. package/dist/components/TimeRange.cjs.map +1 -1
  23. package/dist/components/TimeRange.d.ts.map +1 -1
  24. package/dist/components/TimeRange.js +30 -38
  25. package/dist/components/TimeRange.js.map +1 -1
  26. package/dist/components/TreePickerField.cjs +6 -3
  27. package/dist/components/TreePickerField.cjs.map +1 -1
  28. package/dist/components/TreePickerField.d.ts.map +1 -1
  29. package/dist/components/TreePickerField.js +6 -3
  30. package/dist/components/TreePickerField.js.map +1 -1
  31. package/dist/components/icon-menu-picker.cjs +9 -3
  32. package/dist/components/icon-menu-picker.cjs.map +1 -1
  33. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  34. package/dist/components/icon-menu-picker.js +9 -3
  35. package/dist/components/icon-menu-picker.js.map +1 -1
  36. package/dist/components/json-schema-form-fields.cjs +80 -2
  37. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  38. package/dist/components/json-schema-form-fields.d.ts +15 -0
  39. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  40. package/dist/components/json-schema-form-fields.js +81 -3
  41. package/dist/components/json-schema-form-fields.js.map +1 -1
  42. package/dist/components/json-schema-form-refs.cjs +60 -0
  43. package/dist/components/json-schema-form-refs.cjs.map +1 -0
  44. package/dist/components/json-schema-form-refs.d.ts +3 -0
  45. package/dist/components/json-schema-form-refs.d.ts.map +1 -0
  46. package/dist/components/json-schema-form-refs.js +60 -0
  47. package/dist/components/json-schema-form-refs.js.map +1 -0
  48. package/dist/components/json-schema-form-render.cjs +14 -2
  49. package/dist/components/json-schema-form-render.cjs.map +1 -1
  50. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  51. package/dist/components/json-schema-form-render.js +15 -3
  52. package/dist/components/json-schema-form-render.js.map +1 -1
  53. package/dist/components/json-schema-form-resolve.cjs +5 -0
  54. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  55. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  56. package/dist/components/json-schema-form-resolve.js +5 -0
  57. package/dist/components/json-schema-form-resolve.js.map +1 -1
  58. package/dist/components/json-schema-form-template-menu.cjs +83 -0
  59. package/dist/components/json-schema-form-template-menu.cjs.map +1 -0
  60. package/dist/components/json-schema-form-template-menu.d.ts +15 -0
  61. package/dist/components/json-schema-form-template-menu.d.ts.map +1 -0
  62. package/dist/components/json-schema-form-template-menu.js +83 -0
  63. package/dist/components/json-schema-form-template-menu.js.map +1 -0
  64. package/dist/components/json-schema-form-template.cjs +18 -0
  65. package/dist/components/json-schema-form-template.cjs.map +1 -0
  66. package/dist/components/json-schema-form-template.d.ts +13 -0
  67. package/dist/components/json-schema-form-template.d.ts.map +1 -0
  68. package/dist/components/json-schema-form-template.js +18 -0
  69. package/dist/components/json-schema-form-template.js.map +1 -0
  70. package/dist/components/json-schema-form-types.d.ts +5 -1
  71. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  72. package/dist/components.cjs +4 -0
  73. package/dist/components.cjs.map +1 -1
  74. package/dist/components.d.ts +3 -0
  75. package/dist/components.d.ts.map +1 -1
  76. package/dist/components.js +4 -0
  77. package/dist/components.js.map +1 -1
  78. package/dist/data/Clicky.cjs +6 -9
  79. package/dist/data/Clicky.cjs.map +1 -1
  80. package/dist/data/Clicky.d.ts.map +1 -1
  81. package/dist/data/Clicky.js +6 -9
  82. package/dist/data/Clicky.js.map +1 -1
  83. package/dist/data/DataTable.cjs +23 -17
  84. package/dist/data/DataTable.cjs.map +1 -1
  85. package/dist/data/DataTable.d.ts +7 -1
  86. package/dist/data/DataTable.d.ts.map +1 -1
  87. package/dist/data/DataTable.js +24 -18
  88. package/dist/data/DataTable.js.map +1 -1
  89. package/dist/data/Gauge.cjs +37 -4
  90. package/dist/data/Gauge.cjs.map +1 -1
  91. package/dist/data/Gauge.d.ts +6 -1
  92. package/dist/data/Gauge.d.ts.map +1 -1
  93. package/dist/data/Gauge.js +37 -4
  94. package/dist/data/Gauge.js.map +1 -1
  95. package/dist/data/TimeseriesCoreBars.cjs +127 -29
  96. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  97. package/dist/data/TimeseriesCoreBars.d.ts +11 -1
  98. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  99. package/dist/data/TimeseriesCoreBars.js +128 -30
  100. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  101. package/dist/data/TimeseriesGauge.cjs +105 -47
  102. package/dist/data/TimeseriesGauge.cjs.map +1 -1
  103. package/dist/data/TimeseriesGauge.d.ts +6 -1
  104. package/dist/data/TimeseriesGauge.d.ts.map +1 -1
  105. package/dist/data/TimeseriesGauge.js +105 -47
  106. package/dist/data/TimeseriesGauge.js.map +1 -1
  107. package/dist/data/WorkloadCard.cjs +371 -0
  108. package/dist/data/WorkloadCard.cjs.map +1 -0
  109. package/dist/data/WorkloadCard.d.ts +24 -0
  110. package/dist/data/WorkloadCard.d.ts.map +1 -0
  111. package/dist/data/WorkloadCard.js +371 -0
  112. package/dist/data/WorkloadCard.js.map +1 -0
  113. package/dist/data/WorkloadCard.model.cjs +51 -0
  114. package/dist/data/WorkloadCard.model.cjs.map +1 -0
  115. package/dist/data/WorkloadCard.model.d.ts +53 -0
  116. package/dist/data/WorkloadCard.model.d.ts.map +1 -0
  117. package/dist/data/WorkloadCard.model.js +51 -0
  118. package/dist/data/WorkloadCard.model.js.map +1 -0
  119. package/dist/data/ai/ChatFab.cjs +3 -1
  120. package/dist/data/ai/ChatFab.cjs.map +1 -1
  121. package/dist/data/ai/ChatFab.d.ts.map +1 -1
  122. package/dist/data/ai/ChatFab.js +3 -1
  123. package/dist/data/ai/ChatFab.js.map +1 -1
  124. package/dist/data/ai/ChatWindow.cjs +25 -7
  125. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  126. package/dist/data/ai/ChatWindow.d.ts +11 -3
  127. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  128. package/dist/data/ai/ChatWindow.js +26 -8
  129. package/dist/data/ai/ChatWindow.js.map +1 -1
  130. package/dist/data/ai/chat-window-context.cjs +2 -1
  131. package/dist/data/ai/chat-window-context.cjs.map +1 -1
  132. package/dist/data/ai/chat-window-context.d.ts +1 -1
  133. package/dist/data/ai/chat-window-context.d.ts.map +1 -1
  134. package/dist/data/ai/chat-window-context.js +2 -1
  135. package/dist/data/ai/chat-window-context.js.map +1 -1
  136. package/dist/data/ai/context.cjs.map +1 -1
  137. package/dist/data/ai/context.d.ts +3 -0
  138. package/dist/data/ai/context.d.ts.map +1 -1
  139. package/dist/data/ai/context.js.map +1 -1
  140. package/dist/data/chat/Chat.cjs +9 -1
  141. package/dist/data/chat/Chat.cjs.map +1 -1
  142. package/dist/data/chat/Chat.d.ts +4 -2
  143. package/dist/data/chat/Chat.d.ts.map +1 -1
  144. package/dist/data/chat/Chat.js +10 -2
  145. package/dist/data/chat/Chat.js.map +1 -1
  146. package/dist/data/chat/Message.cjs +13 -4
  147. package/dist/data/chat/Message.cjs.map +1 -1
  148. package/dist/data/chat/Message.d.ts +4 -2
  149. package/dist/data/chat/Message.d.ts.map +1 -1
  150. package/dist/data/chat/Message.js +13 -4
  151. package/dist/data/chat/Message.js.map +1 -1
  152. package/dist/data/chat/ToolCall.cjs +9 -4
  153. package/dist/data/chat/ToolCall.cjs.map +1 -1
  154. package/dist/data/chat/ToolCall.d.ts +3 -2
  155. package/dist/data/chat/ToolCall.d.ts.map +1 -1
  156. package/dist/data/chat/ToolCall.js +9 -4
  157. package/dist/data/chat/ToolCall.js.map +1 -1
  158. package/dist/data/chat/index.d.ts +1 -1
  159. package/dist/data/chat/index.d.ts.map +1 -1
  160. package/dist/data/chat/types.cjs.map +1 -1
  161. package/dist/data/chat/types.d.ts +7 -0
  162. package/dist/data/chat/types.d.ts.map +1 -1
  163. package/dist/data/chat/types.js.map +1 -1
  164. package/dist/data/version-info.cjs +2 -2
  165. package/dist/data/version-info.js +2 -2
  166. package/dist/data.cjs +7 -0
  167. package/dist/data.cjs.map +1 -1
  168. package/dist/data.d.ts +5 -3
  169. package/dist/data.d.ts.map +1 -1
  170. package/dist/data.js +8 -1
  171. package/dist/data.js.map +1 -1
  172. package/dist/hooks/use-sort.cjs +19 -10
  173. package/dist/hooks/use-sort.cjs.map +1 -1
  174. package/dist/hooks/use-sort.d.ts +3 -0
  175. package/dist/hooks/use-sort.d.ts.map +1 -1
  176. package/dist/hooks/use-sort.js +19 -10
  177. package/dist/hooks/use-sort.js.map +1 -1
  178. package/dist/index.cjs +11 -0
  179. package/dist/index.cjs.map +1 -1
  180. package/dist/index.js +11 -0
  181. package/dist/index.js.map +1 -1
  182. package/dist/layout/AppShell.cjs +6 -10
  183. package/dist/layout/AppShell.cjs.map +1 -1
  184. package/dist/layout/AppShell.d.ts +2 -0
  185. package/dist/layout/AppShell.d.ts.map +1 -1
  186. package/dist/layout/AppShell.js +6 -10
  187. package/dist/layout/AppShell.js.map +1 -1
  188. package/dist/overlay/DropdownMenu.cjs +50 -38
  189. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  190. package/dist/overlay/DropdownMenu.d.ts +5 -1
  191. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  192. package/dist/overlay/DropdownMenu.js +50 -38
  193. package/dist/overlay/DropdownMenu.js.map +1 -1
  194. package/dist/overlay/HoverCard.cjs +7 -2
  195. package/dist/overlay/HoverCard.cjs.map +1 -1
  196. package/dist/overlay/HoverCard.d.ts.map +1 -1
  197. package/dist/overlay/HoverCard.js +7 -2
  198. package/dist/overlay/HoverCard.js.map +1 -1
  199. package/dist/overlay/Modal.cjs +20 -17
  200. package/dist/overlay/Modal.cjs.map +1 -1
  201. package/dist/overlay/Modal.d.ts.map +1 -1
  202. package/dist/overlay/Modal.js +21 -18
  203. package/dist/overlay/Modal.js.map +1 -1
  204. package/dist/overlay/Toast.cjs +9 -1
  205. package/dist/overlay/Toast.cjs.map +1 -1
  206. package/dist/overlay/Toast.d.ts.map +1 -1
  207. package/dist/overlay/Toast.js +9 -1
  208. package/dist/overlay/Toast.js.map +1 -1
  209. package/dist/overlay/modalStack.cjs +53 -1
  210. package/dist/overlay/modalStack.cjs.map +1 -1
  211. package/dist/overlay/modalStack.d.ts +12 -0
  212. package/dist/overlay/modalStack.d.ts.map +1 -1
  213. package/dist/overlay/modalStack.js +54 -2
  214. package/dist/overlay/modalStack.js.map +1 -1
  215. package/dist/overlay/zIndex.cjs +20 -0
  216. package/dist/overlay/zIndex.cjs.map +1 -0
  217. package/dist/overlay/zIndex.d.ts +39 -0
  218. package/dist/overlay/zIndex.d.ts.map +1 -0
  219. package/dist/overlay/zIndex.js +20 -0
  220. package/dist/overlay/zIndex.js.map +1 -0
  221. package/dist/rpc/AcceptPicker.cjs +28 -26
  222. package/dist/rpc/AcceptPicker.cjs.map +1 -1
  223. package/dist/rpc/AcceptPicker.d.ts.map +1 -1
  224. package/dist/rpc/AcceptPicker.js +29 -27
  225. package/dist/rpc/AcceptPicker.js.map +1 -1
  226. package/dist/styles.css +1 -1
  227. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/data/ai/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;mCAImC;AACnC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE;;;iCAGiC;AACjC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAUjE;AAID;;4DAE4D;AAC5D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,eAAe,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAoB/F;AAGD,6DAA6D;AAC7D,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/data/ai/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;mCAImC;AACnC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;kFAC8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE;;;iCAGiC;AACjC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAUjE;AAID;;4DAE4D;AAC5D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,eAAe,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAoB/F;AAGD,6DAA6D;AAC7D,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["../../../src/data/ai/context.ts"],"sourcesContent":["import type { StaticIconComponent } from \"../Icon\";\n\n/** A piece of context the user has attached to a chat (a record, a selection,\n * a document…). The `type` is an arbitrary caller-defined string; a\n * {@link ContextTypeConfig} maps it to an icon/colour for display. `fields`\n * are flat key/value details serialized into the prompt and rendered as a\n * compact suffix on the badge. */\nexport interface ChatContextItem {\n id: string;\n type: string;\n label: string;\n fields?: Record<string, string>;\n}\n\n/** Per-type display style for context badges, keyed by `ChatContextItem.type`. */\nexport interface ContextTypeStyle {\n /** Iconify name or an imported icon component. */\n icon?: string | StaticIconComponent;\n /** Tailwind classes for the badge (text + background colour). */\n className?: string;\n}\n\nexport type ContextTypeConfig = Record<string, ContextTypeStyle>;\n\n/** Serializes attached context into a `Context:` block the backend can prepend\n * to (or read alongside) the user's message. Mirrors the format parsed by\n * {@link parseContextPrefix}: `[type] label (k: v, k: v)`. Returns \"\" when\n * there is nothing attached. */\nexport function serializeContext(items: ChatContextItem[]): string {\n if (items.length === 0) return \"\";\n const lines = items.map((item) => {\n const entries = Object.entries(item.fields ?? {})\n .filter(([, v]) => v != null && v !== \"\")\n .map(([k, v]) => `${k}: ${v}`)\n .join(\", \");\n return entries ? `[${item.type}] ${item.label} (${entries})` : `[${item.type}] ${item.label}`;\n });\n return `Context:\\n${lines.join(\"\\n\")}\\n\\n`;\n}\n\nconst CONTEXT_LINE_RE = /^\\[([^\\]]+)\\]\\s+(.+?)(?:\\s+\\((.+)\\))?$/;\n\n/** Splits a message produced with a {@link serializeContext} prefix back into\n * its attached items and the bare question. When no prefix is present the\n * whole text is returned as the question with no items. */\nexport function parseContextPrefix(text: string): { items: ChatContextItem[]; question: string } {\n if (!text.startsWith(\"Context:\\n\")) return { items: [], question: text };\n const blockEnd = text.indexOf(\"\\n\\n\");\n if (blockEnd === -1) return { items: [], question: text };\n const block = text.slice(\"Context:\\n\".length, blockEnd);\n const question = text.slice(blockEnd + 2);\n const items: ChatContextItem[] = [];\n for (const line of block.split(\"\\n\")) {\n const m = line.match(CONTEXT_LINE_RE);\n if (!m) continue;\n const fields: Record<string, string> = {};\n if (m[3]) {\n for (const pair of m[3].split(/,\\s*(?=[A-Za-z0-9_]+:)/)) {\n const idx = pair.indexOf(\":\");\n if (idx > 0) fields[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n }\n items.push({ id: `parsed-${items.length}`, type: m[1]!, label: m[2]!, fields });\n }\n return { items, question };\n}\n\nlet nextId = 0;\n/** A process-unique id for a newly attached context item. */\nexport function makeContextId(): string {\n return `ctx-${++nextId}-${Date.now()}`;\n}\n"],"names":[],"mappings":"AA4BO,SAAS,iBAAiB,OAAkC;AACjE,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,QAAQ,MAAM,IAAI,CAAC,SAAS;AAChC,UAAM,UAAU,OAAO,QAAQ,KAAK,UAAU,CAAA,CAAE,EAC7C,OAAO,CAAC,CAAA,EAAG,CAAC,MAAM,KAAK,QAAQ,MAAM,EAAE,EACvC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,EAC5B,KAAK,IAAI;AACZ,WAAO,UAAU,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,OAAO,MAAM,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK;AAAA,EAC7F,CAAC;AACD,SAAO;AAAA,EAAa,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA;AACtC;AAEA,MAAM,kBAAkB;AAKjB,SAAS,mBAAmB,MAA8D;AAC/F,MAAI,CAAC,KAAK,WAAW,YAAY,EAAG,QAAO,EAAE,OAAO,CAAA,GAAI,UAAU,KAAA;AAClE,QAAM,WAAW,KAAK,QAAQ,MAAM;AACpC,MAAI,aAAa,GAAI,QAAO,EAAE,OAAO,CAAA,GAAI,UAAU,KAAA;AACnD,QAAM,QAAQ,KAAK,MAAM,aAAa,QAAQ,QAAQ;AACtD,QAAM,WAAW,KAAK,MAAM,WAAW,CAAC;AACxC,QAAM,QAA2B,CAAA;AACjC,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,IAAI,KAAK,MAAM,eAAe;AACpC,QAAI,CAAC,EAAG;AACR,UAAM,SAAiC,CAAA;AACvC,QAAI,EAAE,CAAC,GAAG;AACR,iBAAW,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,GAAG;AACvD,cAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,YAAI,MAAM,EAAG,QAAO,KAAK,MAAM,GAAG,GAAG,EAAE,KAAA,CAAM,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAA;AAAA,MACvE;AAAA,IACF;AACA,UAAM,KAAK,EAAE,IAAI,UAAU,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,GAAI,OAAO,EAAE,CAAC,GAAI,QAAQ;AAAA,EAChF;AACA,SAAO,EAAE,OAAO,SAAA;AAClB;AAEA,IAAI,SAAS;AAEN,SAAS,gBAAwB;AACtC,SAAO,OAAO,EAAE,MAAM,IAAI,KAAK,KAAK;AACtC;"}
1
+ {"version":3,"file":"context.js","sources":["../../../src/data/ai/context.ts"],"sourcesContent":["import type { StaticIconComponent } from \"../Icon\";\n\n/** A piece of context the user has attached to a chat (a record, a selection,\n * a document…). The `type` is an arbitrary caller-defined string; a\n * {@link ContextTypeConfig} maps it to an icon/colour for display. `fields`\n * are flat key/value details serialized into the prompt and rendered as a\n * compact suffix on the badge. */\nexport interface ChatContextItem {\n id: string;\n type: string;\n label: string;\n fields?: Record<string, string>;\n /** Rich caller-owned data forwarded to the chat backend alongside the\n * serialized context summary. It is intentionally not rendered in badges. */\n payload?: unknown;\n}\n\n/** Per-type display style for context badges, keyed by `ChatContextItem.type`. */\nexport interface ContextTypeStyle {\n /** Iconify name or an imported icon component. */\n icon?: string | StaticIconComponent;\n /** Tailwind classes for the badge (text + background colour). */\n className?: string;\n}\n\nexport type ContextTypeConfig = Record<string, ContextTypeStyle>;\n\n/** Serializes attached context into a `Context:` block the backend can prepend\n * to (or read alongside) the user's message. Mirrors the format parsed by\n * {@link parseContextPrefix}: `[type] label (k: v, k: v)`. Returns \"\" when\n * there is nothing attached. */\nexport function serializeContext(items: ChatContextItem[]): string {\n if (items.length === 0) return \"\";\n const lines = items.map((item) => {\n const entries = Object.entries(item.fields ?? {})\n .filter(([, v]) => v != null && v !== \"\")\n .map(([k, v]) => `${k}: ${v}`)\n .join(\", \");\n return entries ? `[${item.type}] ${item.label} (${entries})` : `[${item.type}] ${item.label}`;\n });\n return `Context:\\n${lines.join(\"\\n\")}\\n\\n`;\n}\n\nconst CONTEXT_LINE_RE = /^\\[([^\\]]+)\\]\\s+(.+?)(?:\\s+\\((.+)\\))?$/;\n\n/** Splits a message produced with a {@link serializeContext} prefix back into\n * its attached items and the bare question. When no prefix is present the\n * whole text is returned as the question with no items. */\nexport function parseContextPrefix(text: string): { items: ChatContextItem[]; question: string } {\n if (!text.startsWith(\"Context:\\n\")) return { items: [], question: text };\n const blockEnd = text.indexOf(\"\\n\\n\");\n if (blockEnd === -1) return { items: [], question: text };\n const block = text.slice(\"Context:\\n\".length, blockEnd);\n const question = text.slice(blockEnd + 2);\n const items: ChatContextItem[] = [];\n for (const line of block.split(\"\\n\")) {\n const m = line.match(CONTEXT_LINE_RE);\n if (!m) continue;\n const fields: Record<string, string> = {};\n if (m[3]) {\n for (const pair of m[3].split(/,\\s*(?=[A-Za-z0-9_]+:)/)) {\n const idx = pair.indexOf(\":\");\n if (idx > 0) fields[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();\n }\n }\n items.push({ id: `parsed-${items.length}`, type: m[1]!, label: m[2]!, fields });\n }\n return { items, question };\n}\n\nlet nextId = 0;\n/** A process-unique id for a newly attached context item. */\nexport function makeContextId(): string {\n return `ctx-${++nextId}-${Date.now()}`;\n}\n"],"names":[],"mappings":"AA+BO,SAAS,iBAAiB,OAAkC;AACjE,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,QAAQ,MAAM,IAAI,CAAC,SAAS;AAChC,UAAM,UAAU,OAAO,QAAQ,KAAK,UAAU,CAAA,CAAE,EAC7C,OAAO,CAAC,CAAA,EAAG,CAAC,MAAM,KAAK,QAAQ,MAAM,EAAE,EACvC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,EAC5B,KAAK,IAAI;AACZ,WAAO,UAAU,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,OAAO,MAAM,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK;AAAA,EAC7F,CAAC;AACD,SAAO;AAAA,EAAa,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA;AACtC;AAEA,MAAM,kBAAkB;AAKjB,SAAS,mBAAmB,MAA8D;AAC/F,MAAI,CAAC,KAAK,WAAW,YAAY,EAAG,QAAO,EAAE,OAAO,CAAA,GAAI,UAAU,KAAA;AAClE,QAAM,WAAW,KAAK,QAAQ,MAAM;AACpC,MAAI,aAAa,GAAI,QAAO,EAAE,OAAO,CAAA,GAAI,UAAU,KAAA;AACnD,QAAM,QAAQ,KAAK,MAAM,aAAa,QAAQ,QAAQ;AACtD,QAAM,WAAW,KAAK,MAAM,WAAW,CAAC;AACxC,QAAM,QAA2B,CAAA;AACjC,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,IAAI,KAAK,MAAM,eAAe;AACpC,QAAI,CAAC,EAAG;AACR,UAAM,SAAiC,CAAA;AACvC,QAAI,EAAE,CAAC,GAAG;AACR,iBAAW,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,GAAG;AACvD,cAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,YAAI,MAAM,EAAG,QAAO,KAAK,MAAM,GAAG,GAAG,EAAE,KAAA,CAAM,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAA;AAAA,MACvE;AAAA,IACF;AACA,UAAM,KAAK,EAAE,IAAI,UAAU,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,GAAI,OAAO,EAAE,CAAC,GAAI,QAAQ;AAAA,EAChF;AACA,SAAO,EAAE,OAAO,SAAA;AAClB;AAEA,IAAI,SAAS;AAEN,SAAS,gBAAwB;AACtC,SAAO,OAAO,EAAE,MAAM,IAAI,KAAK,KAAK;AACtC;"}
@@ -25,6 +25,7 @@ function Chat({
25
25
  enableAttachments = false,
26
26
  toolApproval,
27
27
  threadId,
28
+ renderToolResult,
28
29
  body,
29
30
  transport,
30
31
  initialMessages,
@@ -36,6 +37,7 @@ function Chat({
36
37
  const [model, setModel] = react.useState(defaultModel);
37
38
  const [effort, setEffort] = react.useState(defaultReasoningEffort);
38
39
  const [usage, setUsage] = react.useState(null);
40
+ const lastDefaultModel = react.useRef(defaultModel);
39
41
  react.useEffect(() => {
40
42
  if (modelsProp || !modelsApi) return;
41
43
  let cancelled = false;
@@ -51,6 +53,11 @@ function Chat({
51
53
  cancelled = true;
52
54
  };
53
55
  }, [modelsProp, modelsApi]);
56
+ react.useEffect(() => {
57
+ if (!defaultModel || defaultModel === lastDefaultModel.current) return;
58
+ lastDefaultModel.current = defaultModel;
59
+ setModel(defaultModel);
60
+ }, [defaultModel]);
54
61
  const selectedModel = models.find((m) => m.id === model);
55
62
  const showEffort = !selectedModel || selectedModel.reasoning;
56
63
  const bodyRef = react.useRef({});
@@ -126,7 +133,8 @@ function Chat({
126
133
  onClearError: clearError,
127
134
  emptyState: empty,
128
135
  onRegenerate: (messageId) => void regenerate({ messageId }),
129
- onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })
136
+ onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved }),
137
+ ...renderToolResult ? { renderToolResult } : {}
130
138
  }
131
139
  ),
132
140
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 pt-0", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.cjs","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon } from \"./provider-icons\";\nimport { ContextUsage } from \"./ContextUsage\";\nimport type { ChatModel, ChatMessageMetadata, ChatUsageSummary, Suggestion } from \"./types\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** \"manual\" forwards a tool-approval policy hint to the backend. */\n toolApproval?: \"auto\" | \"manual\";\n /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\nconst DEFAULT_EFFORTS = [\"low\", \"medium\", \"high\"];\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, approval policy and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n reasoningEfforts = DEFAULT_EFFORTS,\n defaultReasoningEffort = \"\",\n onModelChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n toolApproval,\n threadId,\n body,\n transport,\n initialMessages,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [model, setModel] = useState<string | undefined>(defaultModel);\n const [effort, setEffort] = useState(defaultReasoningEffort);\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))))\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n setModel((m) => m ?? data.find((d) => d.configured !== false)?.id);\n })\n .catch((err) => console.warn(\"clicky-ui: failed to load chat models\", err));\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/policy selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(model ? { model } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(toolApproval ? { toolApproval } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } =\n useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages ? { messages: initialMessages as ChatUIMessage[] } : {}),\n });\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const cost = meta.threadCostUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const toolbar = models.length > 0 || showEffort || (usage && usage.maxTokens > 0) ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector efforts={reasoningEfforts} value={effort} onChange={setEffort} />\n )}\n {usage && usage.maxTokens > 0 && (\n <>\n <div className=\"flex-1\" />\n <ContextUsage\n usedTokens={usage.usedTokens}\n maxTokens={usage.maxTokens}\n {...(usage.messageCount != null ? { messageCount: usage.messageCount } : {})}\n {...(usage.cost != null ? { cost: usage.cost } : {})}\n {...(usage.modelLabel ? { modelLabel: usage.modelLabel } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\n\n const empty = (messages.length === 0 && (emptyState || suggestions?.length)) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions suggestions={suggestions} onSelect={(text) => void sendMessage({ text })} />\n )}\n </div>\n ) : undefined;\n\n return (\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={error}\n onClearError={clearError}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })\n }\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n );\n}\n"],"names":["useState","useEffect","useRef","useMemo","DefaultChatTransport","useChat","lastAssistantMessageIsCompleteWithApprovalResponses","providerIcon","jsxs","jsx","ModelSelector","EffortSelector","Fragment","ContextUsage","Suggestions","cn","Conversation","PromptInput"],"mappings":";;;;;;;;;;;;;AA4DA,MAAM,kBAAkB,CAAC,OAAO,UAAU,MAAM;AAOzC,SAAS,KAAK;AAAA,EACnB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AACZ,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA,SAAsB,cAAc,CAAA,CAAE;AAClE,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAA6B,YAAY;AACnE,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA,SAAS,sBAAsB;AAC3D,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAkC,IAAI;AAGhEC,QAAAA,UAAU,MAAM;AACd,QAAI,cAAc,CAAC,UAAW;AAC9B,QAAI,YAAY;AAChB,UAAM,SAAS,EACZ,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAA,IAAS,QAAQ,OAAO,IAAI,MAAM,UAAU,EAAE,MAAM,EAAE,CAAC,CAAE,EAC/E,KAAK,CAAC,SAAsB;AAC3B,UAAI,UAAW;AACf,gBAAU,IAAI;AACd,eAAS,CAAC;;AAAM,sBAAK,UAAK,KAAK,CAAC,MAAM,EAAE,eAAe,KAAK,MAAvC,mBAA0C;AAAA,OAAE;AAAA,IACnE,CAAC,EACA,MAAM,CAAC,QAAQ,QAAQ,KAAK,yCAAyC,GAAG,CAAC;AAC5E,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,SAAS,CAAC;AAE1B,QAAM,gBAAgB,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK;AACvD,QAAM,aAAa,CAAC,iBAAiB,cAAc;AAInD,QAAM,UAAUC,MAAAA,OAAgC,EAAE;AAClD,UAAQ,UAAU;AAAA,IAChB,GAAG;AAAA,IACH,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA;AAAA,IACxB,GAAI,SAAS,EAAE,iBAAiB,OAAA,IAAW,CAAA;AAAA,IAC3C,GAAI,eAAe,EAAE,aAAA,IAAiB,CAAA;AAAA,IACtC,GAAI,WAAW,EAAE,aAAa,CAAA;AAAA,EAAC;AAGjC,QAAM,oBAAoBC,MAAAA;AAAAA,IACxB,MAAM,aAAa,IAAIC,GAAAA,qBAAqB,EAAE,KAAK,MAAM,MAAM,QAAQ,SAAS;AAAA,IAChF,CAAC,WAAW,GAAG;AAAA,EAAA;AAGjB,QAAM,EAAE,UAAU,aAAa,YAAY,yBAAyB,QAAQ,OAAO,YAAY,KAAA,IAC7FC,gBAAuB;AAAA,IACrB,WAAW;AAAA,IACX,uBAAuBC,GAAAA;AAAAA,IACvB,GAAI,kBAAkB,EAAE,UAAU,oBAAuC,CAAA;AAAA,EAAC,CAC3E;AAKH,QAAM,aAAaJ,MAAAA,OAAO,OAAO;AACjC,aAAW,UAAU;AACrBD,QAAAA,UAAU,MAAM;;AACd,QAAI,WAAW,QAAS;AACxB,UAAM,OAAO,CAAC,GAAG,QAAQ,EAAE,QAAA,EAAU,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACvE,UAAM,OAAO,6BAAM;AACnB,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,KAAK,iBAAiB,KAAK;AACxC,UAAM,WAA6B;AAAA,MACjC,YAAY,KAAK,mBAAiB,UAAK,UAAL,mBAAY,gBAAe;AAAA,MAC7D,YAAW,+CAAe,kBAAiB;AAAA,MAC3C,cAAc,SAAS;AAAA,MACvB,GAAI,QAAQ,OAAO,EAAE,KAAA,IAAS,CAAA;AAAA,MAC9B,IAAI,+CAAe,SAAQ,EAAE,YAAY,cAAc,MAAA,IAAU,CAAA;AAAA,IAAC;AAEpE,aAAS,QAAQ;AACjB,qBAAW,YAAX,oCAAqB;AAAA,EACvB,GAAG,CAAC,UAAU,QAAQ,aAAa,CAAC;AAEpC,QAAM,gBAAgB,CAAC,OAAe;AACpC,aAAS,EAAE;AACX,mDAAgB;AAAA,EAClB;AAEA,QAAM,aAAaM,cAAAA,aAAa,+CAAe,QAAQ;AACvD,QAAM,UAAU,OAAO,SAAS,KAAK,cAAe,SAAS,MAAM,YAAY,IAC7EC,2BAAAA,KAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,IAAAC,2BAAAA,IAACC,cAAAA,eAAA,EAAc,QAAgB,OAAO,OAAO,UAAU,eAAe;AAAA,IACrE,6CACEC,cAAAA,gBAAA,EAAe,SAAS,kBAAkB,OAAO,QAAQ,UAAU,WAAW;AAAA,IAEhF,SAAS,MAAM,YAAY,KAC1BH,2BAAAA,KAAAI,WAAAA,UAAA,EACE,UAAA;AAAA,MAAAH,2BAAAA,IAAC,OAAA,EAAI,WAAU,SAAA,CAAS;AAAA,MACxBA,2BAAAA;AAAAA,QAACI,aAAAA;AAAAA,QAAA;AAAA,UACC,YAAY,MAAM;AAAA,UAClB,WAAW,MAAM;AAAA,UAChB,GAAI,MAAM,gBAAgB,OAAO,EAAE,cAAc,MAAM,aAAA,IAAiB,CAAA;AAAA,UACxE,GAAI,MAAM,QAAQ,OAAO,EAAE,MAAM,MAAM,KAAA,IAAS,CAAA;AAAA,UAChD,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAA,IAAe,CAAA;AAAA,UAC1D,GAAI,aAAa,EAAE,WAAW,eAAe,CAAA;AAAA,QAAC;AAAA,MAAA;AAAA,IACjD,EAAA,CACF;AAAA,EAAA,EAAA,CAEJ,IACE;AAEJ,QAAM,QAAS,SAAS,WAAW,MAAM,eAAc,2CAAa,WAClEL,2BAAAA,KAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,SAAS,oCAClCM,WAAAA,aAAA,EAAY,aAA0B,UAAU,CAAC,SAAS,KAAK,YAAY,EAAE,KAAA,CAAM,EAAA,CAAG;AAAA,EAAA,EAAA,CAE3F,IACE;AAEJ,yCACG,OAAA,EAAI,WAAWC,MAAAA,GAAG,wBAAwB,SAAS,GAClD,UAAA;AAAA,IAAAN,2BAAAA;AAAAA,MAACO,aAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,YAAY;AAAA,QACZ,cAAc,CAAC,cAAc,KAAK,WAAW,EAAE,WAAW;AAAA,QAC1D,WAAW,CAAC,IAAI,UAAU,WACxB,KAAK,wBAAwB,SAAS,EAAE,IAAI,UAAU,OAAA,IAAW,EAAE,IAAI,UAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGrFP,2BAAAA,IAAC,OAAA,EAAI,WAAU,YACb,UAAAA,2BAAAA;AAAAA,MAACQ,YAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA,QAAQ,MAAM,KAAK,KAAA;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,CAAC,MAAM,UAAU,KAAK,YAAY,EAAE,MAAM,MAAA,CAAO;AAAA,MAAA;AAAA,IAAA,EAC7D,CACF;AAAA,EAAA,GACF;AAEJ;;"}
1
+ {"version":3,"file":"Chat.cjs","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon } from \"./provider-icons\";\nimport { ContextUsage } from \"./ContextUsage\";\nimport type { ChatModel, ChatMessageMetadata, ChatUsageSummary, Suggestion, ToolResultRenderer } from \"./types\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** \"manual\" forwards a tool-approval policy hint to the backend. */\n toolApproval?: \"auto\" | \"manual\";\n /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\nconst DEFAULT_EFFORTS = [\"low\", \"medium\", \"high\"];\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, approval policy and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n reasoningEfforts = DEFAULT_EFFORTS,\n defaultReasoningEffort = \"\",\n onModelChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n toolApproval,\n threadId,\n renderToolResult,\n body,\n transport,\n initialMessages,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [model, setModel] = useState<string | undefined>(defaultModel);\n const [effort, setEffort] = useState(defaultReasoningEffort);\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const lastDefaultModel = useRef(defaultModel);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))))\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n setModel((m) => m ?? data.find((d) => d.configured !== false)?.id);\n })\n .catch((err) => console.warn(\"clicky-ui: failed to load chat models\", err));\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (!defaultModel || defaultModel === lastDefaultModel.current) return;\n lastDefaultModel.current = defaultModel;\n setModel(defaultModel);\n }, [defaultModel]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/policy selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(model ? { model } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(toolApproval ? { toolApproval } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } =\n useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages ? { messages: initialMessages as ChatUIMessage[] } : {}),\n });\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const cost = meta.threadCostUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const toolbar = models.length > 0 || showEffort || (usage && usage.maxTokens > 0) ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector efforts={reasoningEfforts} value={effort} onChange={setEffort} />\n )}\n {usage && usage.maxTokens > 0 && (\n <>\n <div className=\"flex-1\" />\n <ContextUsage\n usedTokens={usage.usedTokens}\n maxTokens={usage.maxTokens}\n {...(usage.messageCount != null ? { messageCount: usage.messageCount } : {})}\n {...(usage.cost != null ? { cost: usage.cost } : {})}\n {...(usage.modelLabel ? { modelLabel: usage.modelLabel } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\n\n const empty = (messages.length === 0 && (emptyState || suggestions?.length)) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions suggestions={suggestions} onSelect={(text) => void sendMessage({ text })} />\n )}\n </div>\n ) : undefined;\n\n return (\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={error}\n onClearError={clearError}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","useMemo","DefaultChatTransport","useChat","lastAssistantMessageIsCompleteWithApprovalResponses","providerIcon","jsxs","jsx","ModelSelector","EffortSelector","Fragment","ContextUsage","Suggestions","cn","Conversation","PromptInput"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { ChatTransport, UIMessage } from 'ai';
2
- import { ChatModel, ChatUsageSummary, Suggestion } from './types';
2
+ import { ChatModel, ChatUsageSummary, Suggestion, ToolResultRenderer } from './types';
3
3
  export type ChatProps = {
4
4
  /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.
5
5
  * Ignored when `transport` is supplied. Defaults to "/api/chat". */
@@ -28,6 +28,8 @@ export type ChatProps = {
28
28
  toolApproval?: "auto" | "manual";
29
29
  /** Thread id to persist this conversation under (forwarded in the body). */
30
30
  threadId?: string;
31
+ /** Optional host renderer for recognized completed tool outputs. */
32
+ renderToolResult?: ToolResultRenderer;
31
33
  /** Extra fields merged into every request body. */
32
34
  body?: Record<string, unknown>;
33
35
  /** Pre-built transport (e.g. a mock for stories/tests). */
@@ -43,5 +45,5 @@ export type ChatProps = {
43
45
  * per-message copy/regenerate, and human-in-the-loop tool approvals. The
44
46
  * backend owns model selection and tool execution; the selected model,
45
47
  * effort, approval policy and thread id are forwarded in the request body. */
46
- export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, reasoningEfforts, defaultReasoningEffort, onModelChange, onUsage, suggestions, enableAttachments, toolApproval, threadId, body, transport, initialMessages, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
48
+ export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, reasoningEfforts, defaultReasoningEffort, onModelChange, onUsage, suggestions, enableAttachments, toolApproval, threadId, renderToolResult, body, transport, initialMessages, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
47
49
  //# sourceMappingURL=Chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Chat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,IAAI,CAAC;AAQZ,OAAO,KAAK,EAAE,SAAS,EAAuB,gBAAgB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAM5F,MAAM,MAAM,SAAS,GAAG;IACtB;yEACqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB;+DAC2D;IAC3D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uDAAuD;IACvD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gDAAgD;IAChD,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,kDAAkD;IAClD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACrC,iDAAiD;IACjD,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF;;;;+EAI+E;AAC/E,wBAAgB,IAAI,CAAC,EACnB,GAAiB,EACjB,MAAM,EAAE,UAAU,EAClB,SAA8B,EAC9B,YAAY,EACZ,gBAAkC,EAClC,sBAA2B,EAC3B,aAAa,EACb,OAAO,EACP,WAAW,EACX,iBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,eAAe,EACf,WAAW,EACX,UAAU,EACV,SAAS,GACV,EAAE,SAAS,2CAqIX"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Chat.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,IAAI,CAAC;AAQZ,OAAO,KAAK,EAAE,SAAS,EAAuB,gBAAgB,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAMhH,MAAM,MAAM,SAAS,GAAG;IACtB;yEACqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB;+DAC2D;IAC3D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,uDAAuD;IACvD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gDAAgD;IAChD,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5C,kDAAkD;IAClD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACrC,iDAAiD;IACjD,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF;;;;+EAI+E;AAC/E,wBAAgB,IAAI,CAAC,EACnB,GAAiB,EACjB,MAAM,EAAE,UAAU,EAClB,SAA8B,EAC9B,YAAY,EACZ,gBAAkC,EAClC,sBAA2B,EAC3B,aAAa,EACb,OAAO,EACP,WAAW,EACX,iBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,IAAI,EACJ,SAAS,EACT,eAAe,EACf,WAAW,EACX,UAAU,EACV,SAAS,GACV,EAAE,SAAS,2CA6IX"}
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef, useMemo } from "react";
2
+ import { useState, useRef, useEffect, useMemo } from "react";
3
3
  import { useChat } from "@ai-sdk/react";
4
4
  import { DefaultChatTransport, lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
5
5
  import { cn } from "../../lib/utils.js";
@@ -23,6 +23,7 @@ function Chat({
23
23
  enableAttachments = false,
24
24
  toolApproval,
25
25
  threadId,
26
+ renderToolResult,
26
27
  body,
27
28
  transport,
28
29
  initialMessages,
@@ -34,6 +35,7 @@ function Chat({
34
35
  const [model, setModel] = useState(defaultModel);
35
36
  const [effort, setEffort] = useState(defaultReasoningEffort);
36
37
  const [usage, setUsage] = useState(null);
38
+ const lastDefaultModel = useRef(defaultModel);
37
39
  useEffect(() => {
38
40
  if (modelsProp || !modelsApi) return;
39
41
  let cancelled = false;
@@ -49,6 +51,11 @@ function Chat({
49
51
  cancelled = true;
50
52
  };
51
53
  }, [modelsProp, modelsApi]);
54
+ useEffect(() => {
55
+ if (!defaultModel || defaultModel === lastDefaultModel.current) return;
56
+ lastDefaultModel.current = defaultModel;
57
+ setModel(defaultModel);
58
+ }, [defaultModel]);
52
59
  const selectedModel = models.find((m) => m.id === model);
53
60
  const showEffort = !selectedModel || selectedModel.reasoning;
54
61
  const bodyRef = useRef({});
@@ -124,7 +131,8 @@ function Chat({
124
131
  onClearError: clearError,
125
132
  emptyState: empty,
126
133
  onRegenerate: (messageId) => void regenerate({ messageId }),
127
- onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })
134
+ onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved }),
135
+ ...renderToolResult ? { renderToolResult } : {}
128
136
  }
129
137
  ),
130
138
  /* @__PURE__ */ jsx("div", { className: "p-4 pt-0", children: /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.js","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon } from \"./provider-icons\";\nimport { ContextUsage } from \"./ContextUsage\";\nimport type { ChatModel, ChatMessageMetadata, ChatUsageSummary, Suggestion } from \"./types\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** \"manual\" forwards a tool-approval policy hint to the backend. */\n toolApproval?: \"auto\" | \"manual\";\n /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\nconst DEFAULT_EFFORTS = [\"low\", \"medium\", \"high\"];\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, approval policy and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n reasoningEfforts = DEFAULT_EFFORTS,\n defaultReasoningEffort = \"\",\n onModelChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n toolApproval,\n threadId,\n body,\n transport,\n initialMessages,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [model, setModel] = useState<string | undefined>(defaultModel);\n const [effort, setEffort] = useState(defaultReasoningEffort);\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))))\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n setModel((m) => m ?? data.find((d) => d.configured !== false)?.id);\n })\n .catch((err) => console.warn(\"clicky-ui: failed to load chat models\", err));\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/policy selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(model ? { model } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(toolApproval ? { toolApproval } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } =\n useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages ? { messages: initialMessages as ChatUIMessage[] } : {}),\n });\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const cost = meta.threadCostUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const toolbar = models.length > 0 || showEffort || (usage && usage.maxTokens > 0) ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector efforts={reasoningEfforts} value={effort} onChange={setEffort} />\n )}\n {usage && usage.maxTokens > 0 && (\n <>\n <div className=\"flex-1\" />\n <ContextUsage\n usedTokens={usage.usedTokens}\n maxTokens={usage.maxTokens}\n {...(usage.messageCount != null ? { messageCount: usage.messageCount } : {})}\n {...(usage.cost != null ? { cost: usage.cost } : {})}\n {...(usage.modelLabel ? { modelLabel: usage.modelLabel } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\n\n const empty = (messages.length === 0 && (emptyState || suggestions?.length)) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions suggestions={suggestions} onSelect={(text) => void sendMessage({ text })} />\n )}\n </div>\n ) : undefined;\n\n return (\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={error}\n onClearError={clearError}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })\n }\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Chat.js","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon } from \"./provider-icons\";\nimport { ContextUsage } from \"./ContextUsage\";\nimport type { ChatModel, ChatMessageMetadata, ChatUsageSummary, Suggestion, ToolResultRenderer } from \"./types\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** \"manual\" forwards a tool-approval policy hint to the backend. */\n toolApproval?: \"auto\" | \"manual\";\n /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\nconst DEFAULT_EFFORTS = [\"low\", \"medium\", \"high\"];\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, approval policy and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n reasoningEfforts = DEFAULT_EFFORTS,\n defaultReasoningEffort = \"\",\n onModelChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n toolApproval,\n threadId,\n renderToolResult,\n body,\n transport,\n initialMessages,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [model, setModel] = useState<string | undefined>(defaultModel);\n const [effort, setEffort] = useState(defaultReasoningEffort);\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const lastDefaultModel = useRef(defaultModel);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) => (r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))))\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n setModel((m) => m ?? data.find((d) => d.configured !== false)?.id);\n })\n .catch((err) => console.warn(\"clicky-ui: failed to load chat models\", err));\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (!defaultModel || defaultModel === lastDefaultModel.current) return;\n lastDefaultModel.current = defaultModel;\n setModel(defaultModel);\n }, [defaultModel]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/policy selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(model ? { model } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(toolApproval ? { toolApproval } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } =\n useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages ? { messages: initialMessages as ChatUIMessage[] } : {}),\n });\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const cost = meta.threadCostUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const toolbar = models.length > 0 || showEffort || (usage && usage.maxTokens > 0) ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector efforts={reasoningEfforts} value={effort} onChange={setEffort} />\n )}\n {usage && usage.maxTokens > 0 && (\n <>\n <div className=\"flex-1\" />\n <ContextUsage\n usedTokens={usage.usedTokens}\n maxTokens={usage.maxTokens}\n {...(usage.messageCount != null ? { messageCount: usage.messageCount } : {})}\n {...(usage.cost != null ? { cost: usage.cost } : {})}\n {...(usage.modelLabel ? { modelLabel: usage.modelLabel } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\n\n const empty = (messages.length === 0 && (emptyState || suggestions?.length)) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions suggestions={suggestions} onSelect={(text) => void sendMessage({ text })} />\n )}\n </div>\n ) : undefined;\n\n return (\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={error}\n onClearError={clearError}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ const MessageActions = require("./MessageActions.cjs");
9
9
  const Reasoning = require("./Reasoning.cjs");
10
10
  const types = require("./types.cjs");
11
11
  const UiFile = require("../../icons/components/UiFile.cjs");
12
- function Message({ message, className, onRegenerate, onApprove }) {
12
+ function Message({ message, className, onRegenerate, onApprove, renderToolResult }) {
13
13
  const isUser = message.role === "user";
14
14
  const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
15
15
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -33,7 +33,8 @@ function Message({ message, className, onRegenerate, onApprove }) {
33
33
  {
34
34
  part,
35
35
  isUser,
36
- onApprove
36
+ onApprove,
37
+ renderToolResult
37
38
  },
38
39
  `${message.id}-${i}`
39
40
  ))
@@ -53,7 +54,8 @@ function Message({ message, className, onRegenerate, onApprove }) {
53
54
  function MessagePart({
54
55
  part,
55
56
  isUser,
56
- onApprove
57
+ onApprove,
58
+ renderToolResult
57
59
  }) {
58
60
  if (part.type === "text") {
59
61
  if (isUser) {
@@ -68,7 +70,14 @@ function MessagePart({
68
70
  return /* @__PURE__ */ jsxRuntime.jsx(FilePart, { part });
69
71
  }
70
72
  if (types.isDynamicToolPart(part) || types.isTypedToolPart(part)) {
71
- return /* @__PURE__ */ jsxRuntime.jsx(ToolCall.ToolCall, { part, onApprove });
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ ToolCall.ToolCall,
75
+ {
76
+ part,
77
+ onApprove,
78
+ ...renderToolResult ? { renderToolResult } : {}
79
+ }
80
+ );
72
81
  }
73
82
  return null;
74
83
  }
@@ -1 +1 @@
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
+ {"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 type ToolResultRenderer,\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 /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\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, renderToolResult }: 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 renderToolResult={renderToolResult}\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 renderToolResult,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n renderToolResult: MessageActionHandlers[\"renderToolResult\"];\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 (\n <ToolCall\n part={part as AnyToolPart}\n onApprove={onApprove}\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n );\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":";;;;;;;;;;;AAmCO,SAAS,QAAQ,EAAE,SAAS,WAAW,cAAc,WAAW,oBAAkC;AACvG,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,gBACA;AAAA,cAAA;AAAA,cAJK,GAAG,QAAQ,EAAE,IAAI,CAAC;AAAA,YAAA,CAM1B;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;AAAA,EACA;AACF,GAKG;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,WACEP,2BAAAA;AAAAA,MAACQ,SAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACC,GAAI,mBAAmB,EAAE,qBAAqB,CAAA;AAAA,MAAC;AAAA,IAAA;AAAA,EAGtD;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,12 @@
1
- import { UIMessage } from './types';
1
+ import { UIMessage, ToolResultRenderer } from './types';
2
2
  /** Callbacks the conversation threads down to each message. */
3
3
  export type MessageActionHandlers = {
4
4
  /** Re-generate the assistant message with the given id. */
5
5
  onRegenerate?: ((messageId: string) => void) | undefined;
6
6
  /** Respond to a tool approval request. */
7
7
  onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;
8
+ /** Optional host renderer for recognized completed tool outputs. */
9
+ renderToolResult?: ToolResultRenderer;
8
10
  };
9
11
  export type MessageProps = MessageActionHandlers & {
10
12
  message: UIMessage;
@@ -13,5 +15,5 @@ export type MessageProps = MessageActionHandlers & {
13
15
  /** Renders one chat message. User messages are right-aligned bubbles; assistant
14
16
  * messages render text as markdown, reasoning and tool parts inline, file parts
15
17
  * 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;
18
+ export declare function Message({ message, className, onRegenerate, onApprove, renderToolResult }: MessageProps): import("react/jsx-runtime").JSX.Element;
17
19
  //# sourceMappingURL=Message.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Message.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AACrD,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,SAAS,CAAC;AAEjB,+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;IAC3F,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;CACvC,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,gBAAgB,EAAE,EAAE,YAAY,2CAwCtG"}
@@ -7,7 +7,7 @@ import { MessageActions } from "./MessageActions.js";
7
7
  import { Reasoning } from "./Reasoning.js";
8
8
  import { isReasoningPart, isFilePart, isDynamicToolPart, isTypedToolPart } from "./types.js";
9
9
  import { UiFile } from "../../icons/components/UiFile.js";
10
- function Message({ message, className, onRegenerate, onApprove }) {
10
+ function Message({ message, className, onRegenerate, onApprove, renderToolResult }) {
11
11
  const isUser = message.role === "user";
12
12
  const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
13
13
  return /* @__PURE__ */ jsxs(
@@ -31,7 +31,8 @@ function Message({ message, className, onRegenerate, onApprove }) {
31
31
  {
32
32
  part,
33
33
  isUser,
34
- onApprove
34
+ onApprove,
35
+ renderToolResult
35
36
  },
36
37
  `${message.id}-${i}`
37
38
  ))
@@ -51,7 +52,8 @@ function Message({ message, className, onRegenerate, onApprove }) {
51
52
  function MessagePart({
52
53
  part,
53
54
  isUser,
54
- onApprove
55
+ onApprove,
56
+ renderToolResult
55
57
  }) {
56
58
  if (part.type === "text") {
57
59
  if (isUser) {
@@ -66,7 +68,14 @@ function MessagePart({
66
68
  return /* @__PURE__ */ jsx(FilePart, { part });
67
69
  }
68
70
  if (isDynamicToolPart(part) || isTypedToolPart(part)) {
69
- return /* @__PURE__ */ jsx(ToolCall, { part, onApprove });
71
+ return /* @__PURE__ */ jsx(
72
+ ToolCall,
73
+ {
74
+ part,
75
+ onApprove,
76
+ ...renderToolResult ? { renderToolResult } : {}
77
+ }
78
+ );
70
79
  }
71
80
  return null;
72
81
  }
@@ -1 +1 @@
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;"}
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 type ToolResultRenderer,\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 /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\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, renderToolResult }: 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 renderToolResult={renderToolResult}\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 renderToolResult,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n renderToolResult: MessageActionHandlers[\"renderToolResult\"];\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 (\n <ToolCall\n part={part as AnyToolPart}\n onApprove={onApprove}\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n );\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":";;;;;;;;;AAmCO,SAAS,QAAQ,EAAE,SAAS,WAAW,cAAc,WAAW,oBAAkC;AACvG,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,gBACA;AAAA,cAAA;AAAA,cAJK,GAAG,QAAQ,EAAE,IAAI,CAAC;AAAA,YAAA,CAM1B;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;AAAA,EACA;AACF,GAKG;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,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACC,GAAI,mBAAmB,EAAE,qBAAqB,CAAA;AAAA,MAAC;AAAA,IAAA;AAAA,EAGtD;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;"}
@@ -31,7 +31,7 @@ const STATUS_ICON = {
31
31
  "output-denied": { icon: UiCircleX.UiCircleX, className: "text-orange-600" },
32
32
  "output-error": { icon: UiCircleX.UiCircleX, className: "text-destructive" }
33
33
  };
34
- function ToolCall({ part, defaultOpen = false, onApprove, className }) {
34
+ function ToolCall({ part, defaultOpen = false, onApprove, renderToolResult, className }) {
35
35
  const needsApproval = part.state === "approval-requested";
36
36
  const [open, setOpen] = react.useState(defaultOpen || needsApproval);
37
37
  const status = STATUS_ICON[part.state];
@@ -69,7 +69,7 @@ function ToolCall({ part, defaultOpen = false, onApprove, className }) {
69
69
  ),
70
70
  open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 pl-4 pt-0.5", children: [
71
71
  /* @__PURE__ */ jsxRuntime.jsx(ToolInput, { input: part.input }),
72
- /* @__PURE__ */ jsxRuntime.jsx(ToolOutput, { part })
72
+ /* @__PURE__ */ jsxRuntime.jsx(ToolOutput, { part, ...renderToolResult ? { renderToolResult } : {} })
73
73
  ] }),
74
74
  needsApproval && /* @__PURE__ */ jsxRuntime.jsx(ApprovalControls, { part, onApprove })
75
75
  ] });
@@ -100,15 +100,20 @@ function ToolInput({ input }) {
100
100
  }
101
101
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { language: "json", source: JSON.stringify(input, null, 2) }) });
102
102
  }
103
- function ToolOutput({ part }) {
103
+ function ToolOutput({
104
+ part,
105
+ renderToolResult
106
+ }) {
104
107
  const errorText = part.state === "output-error" ? part.errorText : void 0;
105
108
  const output = part.state === "output-available" ? part.output : void 0;
106
109
  if (output === void 0 && errorText === void 0) {
107
110
  return null;
108
111
  }
112
+ const name = types.toolPartName(part);
113
+ const custom = output !== void 0 ? renderToolResult == null ? void 0 : renderToolResult({ part, toolName: name, output }) : null;
109
114
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("overflow-x-auto text-xs", errorText ? "text-destructive" : "text-muted-foreground"), children: [
110
115
  errorText !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: errorText }),
111
- output !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { language: "json", source: typeof output === "string" ? output : JSON.stringify(output, null, 2) })
116
+ custom ?? (output !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { language: "json", source: typeof output === "string" ? output : JSON.stringify(output, null, 2) }))
112
117
  ] });
113
118
  }
114
119
  exports.ToolCall = ToolCall;
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCall.cjs","sources":["../../../src/data/chat/ToolCall.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../../components/button\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n UiCheck,\n UiChevronDown,\n UiCircleOutline,\n UiCircleX,\n UiClock,\n UiWrench,\n} from \"../../icons\";\nimport type { AnyToolPart } from \"./types\";\nimport { toolPartName } from \"./types\";\n\nexport type ToolCallProps = {\n /** The tool part from an assistant message (typed or dynamic). */\n part: AnyToolPart;\n /** Whether the call body starts expanded. Defaults to false. */\n defaultOpen?: boolean;\n /** Respond to an approval request (only used when state is\n * `approval-requested`). Receives the approval id, the decision, and an\n * optional reason. */\n onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;\n className?: string;\n};\n\ntype ToolState = AnyToolPart[\"state\"];\n\nconst STATUS_LABEL: Record<ToolState, string> = {\n \"approval-requested\": \"Awaiting approval\",\n \"approval-responded\": \"Responded\",\n \"input-streaming\": \"Pending\",\n \"input-available\": \"Running\",\n \"output-available\": \"Completed\",\n \"output-denied\": \"Denied\",\n \"output-error\": \"Error\",\n};\n\nconst STATUS_ICON: Record<ToolState, { icon: StaticIconComponent; className: string }> = {\n \"approval-requested\": { icon: UiClock, className: \"text-amber-600\" },\n \"approval-responded\": { icon: UiCheck, className: \"text-sky-600\" },\n \"input-streaming\": { icon: UiCircleOutline, className: \"text-muted-foreground\" },\n \"input-available\": { icon: UiClock, className: \"text-muted-foreground animate-pulse\" },\n \"output-available\": { icon: UiCheck, className: \"text-emerald-600\" },\n \"output-denied\": { icon: UiCircleX, className: \"text-orange-600\" },\n \"output-error\": { icon: UiCircleX, className: \"text-destructive\" },\n};\n\n/** Generic display of a single AI tool call: a collapsible header showing the\n * tool name + status, expanding to its JSON input and (once available) output\n * or error. Renders both clicky `dynamic-tool` parts and typed `tool-<name>`\n * parts, keyed off `part.state`. */\nexport function ToolCall({ part, defaultOpen = false, onApprove, className }: ToolCallProps) {\n const needsApproval = part.state === \"approval-requested\";\n const [open, setOpen] = useState(defaultOpen || needsApproval);\n const status = STATUS_ICON[part.state];\n const name = toolPartName(part);\n\n return (\n <div className={cn(\"not-prose mb-1 w-full\", className)}>\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={() => setOpen((v) => !v)}\n className=\"flex w-full items-center justify-between gap-2 py-0.5 text-muted-foreground hover:text-foreground\"\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiWrench} className=\"size-3 shrink-0\" />\n <span className=\"truncate font-mono text-xs\">{name}</span>\n <Icon\n icon={status.icon}\n title={STATUS_LABEL[part.state]}\n className={cn(\"size-3 shrink-0\", status.className)}\n />\n </span>\n <Icon\n icon={UiChevronDown}\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"space-y-1 pl-4 pt-0.5\">\n <ToolInput input={part.input} />\n <ToolOutput part={part} />\n </div>\n )}\n\n {needsApproval && <ApprovalControls part={part} onApprove={onApprove} />}\n </div>\n );\n}\n\n/** Approve/Deny controls shown while a tool call awaits human approval. The\n * approval id comes from the part's `approval` envelope (AI SDK v6). */\nfunction ApprovalControls({\n part,\n onApprove,\n}: {\n part: AnyToolPart;\n onApprove: ToolCallProps[\"onApprove\"];\n}) {\n const approval = \"approval\" in part ? part.approval : undefined;\n if (!approval || !onApprove) return null;\n return (\n <div className=\"mt-1.5 flex items-center gap-2 pl-4\">\n <Button type=\"button\" size=\"sm\" onClick={() => onApprove(approval.id, true)}>\n Approve\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => onApprove(approval.id, false)}\n >\n Deny\n </Button>\n </div>\n );\n}\n\nfunction ToolInput({ input }: { input: AnyToolPart[\"input\"] }) {\n if (input === undefined || input === null) {\n return null;\n }\n return (\n <div className=\"overflow-hidden text-xs\">\n <CodeBlock language=\"json\" source={JSON.stringify(input, null, 2)} />\n </div>\n );\n}\n\nfunction ToolOutput({ part }: { part: AnyToolPart }) {\n const errorText = part.state === \"output-error\" ? part.errorText : undefined;\n const output = part.state === \"output-available\" ? part.output : undefined;\n if (output === undefined && errorText === undefined) {\n return null;\n }\n return (\n <div className={cn(\"overflow-x-auto text-xs\", errorText ? \"text-destructive\" : \"text-muted-foreground\")}>\n {errorText !== undefined && <div>{errorText}</div>}\n {output !== undefined && (\n <CodeBlock language=\"json\" source={typeof output === \"string\" ? output : JSON.stringify(output, null, 2)} />\n )}\n </div>\n );\n}\n"],"names":["UiClock","UiCheck","UiCircleOutline","UiCircleX","useState","toolPartName","cn","jsxs","jsx","Icon","UiWrench","UiChevronDown","Button","CodeBlock"],"mappings":"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"}
1
+ {"version":3,"file":"ToolCall.cjs","sources":["../../../src/data/chat/ToolCall.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../../components/button\";\nimport { Icon, type StaticIconComponent } from \"../Icon\";\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n UiCheck,\n UiChevronDown,\n UiCircleOutline,\n UiCircleX,\n UiClock,\n UiWrench,\n} from \"../../icons\";\nimport type { AnyToolPart, ToolResultRenderer } from \"./types\";\nimport { toolPartName } from \"./types\";\n\nexport type ToolCallProps = {\n /** The tool part from an assistant message (typed or dynamic). */\n part: AnyToolPart;\n /** Whether the call body starts expanded. Defaults to false. */\n defaultOpen?: boolean;\n /** Respond to an approval request (only used when state is\n * `approval-requested`). Receives the approval id, the decision, and an\n * optional reason. */\n onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;\n renderToolResult?: ToolResultRenderer;\n className?: string;\n};\n\ntype ToolState = AnyToolPart[\"state\"];\n\nconst STATUS_LABEL: Record<ToolState, string> = {\n \"approval-requested\": \"Awaiting approval\",\n \"approval-responded\": \"Responded\",\n \"input-streaming\": \"Pending\",\n \"input-available\": \"Running\",\n \"output-available\": \"Completed\",\n \"output-denied\": \"Denied\",\n \"output-error\": \"Error\",\n};\n\nconst STATUS_ICON: Record<ToolState, { icon: StaticIconComponent; className: string }> = {\n \"approval-requested\": { icon: UiClock, className: \"text-amber-600\" },\n \"approval-responded\": { icon: UiCheck, className: \"text-sky-600\" },\n \"input-streaming\": { icon: UiCircleOutline, className: \"text-muted-foreground\" },\n \"input-available\": { icon: UiClock, className: \"text-muted-foreground animate-pulse\" },\n \"output-available\": { icon: UiCheck, className: \"text-emerald-600\" },\n \"output-denied\": { icon: UiCircleX, className: \"text-orange-600\" },\n \"output-error\": { icon: UiCircleX, className: \"text-destructive\" },\n};\n\n/** Generic display of a single AI tool call: a collapsible header showing the\n * tool name + status, expanding to its JSON input and (once available) output\n * or error. Renders both clicky `dynamic-tool` parts and typed `tool-<name>`\n * parts, keyed off `part.state`. */\nexport function ToolCall({ part, defaultOpen = false, onApprove, renderToolResult, className }: ToolCallProps) {\n const needsApproval = part.state === \"approval-requested\";\n const [open, setOpen] = useState(defaultOpen || needsApproval);\n const status = STATUS_ICON[part.state];\n const name = toolPartName(part);\n\n return (\n <div className={cn(\"not-prose mb-1 w-full\", className)}>\n <button\n type=\"button\"\n aria-expanded={open}\n onClick={() => setOpen((v) => !v)}\n className=\"flex w-full items-center justify-between gap-2 py-0.5 text-muted-foreground hover:text-foreground\"\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiWrench} className=\"size-3 shrink-0\" />\n <span className=\"truncate font-mono text-xs\">{name}</span>\n <Icon\n icon={status.icon}\n title={STATUS_LABEL[part.state]}\n className={cn(\"size-3 shrink-0\", status.className)}\n />\n </span>\n <Icon\n icon={UiChevronDown}\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"space-y-1 pl-4 pt-0.5\">\n <ToolInput input={part.input} />\n <ToolOutput part={part} {...(renderToolResult ? { renderToolResult } : {})} />\n </div>\n )}\n\n {needsApproval && <ApprovalControls part={part} onApprove={onApprove} />}\n </div>\n );\n}\n\n/** Approve/Deny controls shown while a tool call awaits human approval. The\n * approval id comes from the part's `approval` envelope (AI SDK v6). */\nfunction ApprovalControls({\n part,\n onApprove,\n}: {\n part: AnyToolPart;\n onApprove: ToolCallProps[\"onApprove\"];\n}) {\n const approval = \"approval\" in part ? part.approval : undefined;\n if (!approval || !onApprove) return null;\n return (\n <div className=\"mt-1.5 flex items-center gap-2 pl-4\">\n <Button type=\"button\" size=\"sm\" onClick={() => onApprove(approval.id, true)}>\n Approve\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => onApprove(approval.id, false)}\n >\n Deny\n </Button>\n </div>\n );\n}\n\nfunction ToolInput({ input }: { input: AnyToolPart[\"input\"] }) {\n if (input === undefined || input === null) {\n return null;\n }\n return (\n <div className=\"overflow-hidden text-xs\">\n <CodeBlock language=\"json\" source={JSON.stringify(input, null, 2)} />\n </div>\n );\n}\n\nfunction ToolOutput({\n part,\n renderToolResult,\n}: {\n part: AnyToolPart;\n renderToolResult?: ToolResultRenderer | undefined;\n}) {\n const errorText = part.state === \"output-error\" ? part.errorText : undefined;\n const output = part.state === \"output-available\" ? part.output : undefined;\n if (output === undefined && errorText === undefined) {\n return null;\n }\n const name = toolPartName(part);\n const custom = output !== undefined ? renderToolResult?.({ part, toolName: name, output }) : null;\n return (\n <div className={cn(\"overflow-x-auto text-xs\", errorText ? \"text-destructive\" : \"text-muted-foreground\")}>\n {errorText !== undefined && <div>{errorText}</div>}\n {custom ?? (output !== undefined && (\n <CodeBlock language=\"json\" source={typeof output === \"string\" ? output : JSON.stringify(output, null, 2)} />\n ))}\n </div>\n );\n}\n"],"names":["UiClock","UiCheck","UiCircleOutline","UiCircleX","useState","toolPartName","cn","jsxs","jsx","Icon","UiWrench","UiChevronDown","Button","CodeBlock"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AnyToolPart } from './types';
1
+ import { AnyToolPart, ToolResultRenderer } from './types';
2
2
  export type ToolCallProps = {
3
3
  /** The tool part from an assistant message (typed or dynamic). */
4
4
  part: AnyToolPart;
@@ -8,11 +8,12 @@ export type ToolCallProps = {
8
8
  * `approval-requested`). Receives the approval id, the decision, and an
9
9
  * optional reason. */
10
10
  onApprove?: ((approvalId: string, approved: boolean, reason?: string) => void) | undefined;
11
+ renderToolResult?: ToolResultRenderer;
11
12
  className?: string;
12
13
  };
13
14
  /** Generic display of a single AI tool call: a collapsible header showing the
14
15
  * tool name + status, expanding to its JSON input and (once available) output
15
16
  * or error. Renders both clicky `dynamic-tool` parts and typed `tool-<name>`
16
17
  * parts, keyed off `part.state`. */
17
- export declare function ToolCall({ part, defaultOpen, onApprove, className }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function ToolCall({ part, defaultOpen, onApprove, renderToolResult, className }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
18
19
  //# sourceMappingURL=ToolCall.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCall.d.ts","sourceRoot":"","sources":["../../../src/data/chat/ToolCall.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,MAAM,MAAM,aAAa,GAAG;IAC1B,kEAAkE;IAClE,IAAI,EAAE,WAAW,CAAC;IAClB,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;2BAEuB;IACvB,SAAS,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwBF;;;qCAGqC;AACrC,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,aAAa,2CAuC1F"}
1
+ {"version":3,"file":"ToolCall.d.ts","sourceRoot":"","sources":["../../../src/data/chat/ToolCall.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG;IAC1B,kEAAkE;IAClE,IAAI,EAAE,WAAW,CAAC;IAClB,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;2BAEuB;IACvB,SAAS,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3F,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwBF;;;qCAGqC;AACrC,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAmB,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,EAAE,aAAa,2CAuC5G"}