@vibe-forge/client 0.8.4 → 0.10.0

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 (301) hide show
  1. package/AGENTS.md +75 -0
  2. package/dist/assets/abap-DLDM7-KI.js +1 -0
  3. package/dist/assets/apex-DNDY2TF8.js +1 -0
  4. package/dist/assets/{arc-DCuZPvAs.js → arc-CCXV7u3V.js} +1 -1
  5. package/dist/assets/azcli-Y6nb8tq_.js +1 -0
  6. package/dist/assets/bat-BwHxbl9M.js +1 -0
  7. package/dist/assets/bicep-CFznDFnq.js +2 -0
  8. package/dist/assets/{blockDiagram-c4efeb88-C39d7-Bu.js → blockDiagram-c4efeb88-Bm52FmvT.js} +1 -1
  9. package/dist/assets/{c4Diagram-c83219d4-jWPBJdeq.js → c4Diagram-c83219d4-C8tTEpcK.js} +1 -1
  10. package/dist/assets/cameligo-Bf6VGUru.js +1 -0
  11. package/dist/assets/channel-gq_WMRvv.js +1 -0
  12. package/dist/assets/{classDiagram-beda092f-Bo7Yvv2T.js → classDiagram-beda092f-CNAIBAH1.js} +1 -1
  13. package/dist/assets/{classDiagram-v2-2358418a-DfoCP9XM.js → classDiagram-v2-2358418a-BHeZAVdc.js} +1 -1
  14. package/dist/assets/clojure-Dnu-v4kV.js +1 -0
  15. package/dist/assets/clone-XxGY7A5N.js +1 -0
  16. package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  17. package/dist/assets/coffee-Bd8akH9Z.js +1 -0
  18. package/dist/assets/cpp-BbWJElDN.js +1 -0
  19. package/dist/assets/{createText-1719965b-8_Ez5rxh.js → createText-1719965b-BS2hLG8t.js} +1 -1
  20. package/dist/assets/csharp-Co3qMtFm.js +1 -0
  21. package/dist/assets/csp-D-4FJmMZ.js +1 -0
  22. package/dist/assets/css-DdJfP1eB.js +3 -0
  23. package/dist/assets/css.worker-BvV5MPou.js +93 -0
  24. package/dist/assets/cssMode-WHcTFAOU.js +1 -0
  25. package/dist/assets/cypher-cTPe9QuQ.js +1 -0
  26. package/dist/assets/dart-BOtBlQCF.js +1 -0
  27. package/dist/assets/dockerfile-BG73LgW2.js +1 -0
  28. package/dist/assets/ecl-BEgZUVRK.js +1 -0
  29. package/dist/assets/{edges-96097737-BEegO-R-.js → edges-96097737-C07f4iWA.js} +1 -1
  30. package/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
  31. package/dist/assets/elixir-BkW5O-1t.js +1 -0
  32. package/dist/assets/{erDiagram-0228fc6a-derRgkLz.js → erDiagram-0228fc6a-BytsAWUs.js} +1 -1
  33. package/dist/assets/flow9-BeJ5waoc.js +1 -0
  34. package/dist/assets/{flowDb-c6c81e3f-ChtOxgsJ.js → flowDb-c6c81e3f-CQJkOpAs.js} +1 -1
  35. package/dist/assets/{flowDiagram-50d868cf-Bm4ufmmA.js → flowDiagram-50d868cf-CD5Tng2S.js} +1 -1
  36. package/dist/assets/flowDiagram-v2-4f6560a1-DIBOANLV.js +1 -0
  37. package/dist/assets/{flowchart-elk-definition-6af322e1-BRYLO-BL.js → flowchart-elk-definition-6af322e1-ylso-GWH.js} +1 -1
  38. package/dist/assets/freemarker2-U_9Jyyr3.js +3 -0
  39. package/dist/assets/fsharp-PahG7c26.js +1 -0
  40. package/dist/assets/{ganttDiagram-a2739b55-CbP6dzRO.js → ganttDiagram-a2739b55-Cg98bJx5.js} +1 -1
  41. package/dist/assets/{gitGraphDiagram-82fe8481-Du44v02s.js → gitGraphDiagram-82fe8481-7Yp4hz0N.js} +1 -1
  42. package/dist/assets/go-acbASCJo.js +1 -0
  43. package/dist/assets/{graph-0_VzJX6O.js → graph-Ig3nvzvL.js} +1 -1
  44. package/dist/assets/graphql-BxJiqAUM.js +1 -0
  45. package/dist/assets/handlebars-DQyCBwHe.js +1 -0
  46. package/dist/assets/hcl-DtV1sZF8.js +1 -0
  47. package/dist/assets/html-CNC2AT5k.js +1 -0
  48. package/dist/assets/html.worker-BLJhxQJQ.js +470 -0
  49. package/dist/assets/htmlMode-DlATk4xW.js +1 -0
  50. package/dist/assets/{index-5325376f-DLP7F7of.js → index-5325376f-C4zed9sb.js} +1 -1
  51. package/dist/assets/index-DRLsOoqb.css +32 -0
  52. package/dist/assets/index-Dbx0JG0p.js +1511 -0
  53. package/dist/assets/{infoDiagram-8eee0895-DY19rRl6.js → infoDiagram-8eee0895-C8oSBaFs.js} +1 -1
  54. package/dist/assets/ini-Kd9XrMLS.js +1 -0
  55. package/dist/assets/java-CXBNlu9o.js +1 -0
  56. package/dist/assets/javascript-9wv9uKW4.js +1 -0
  57. package/dist/assets/{journeyDiagram-c64418c1-4Asnwc86.js → journeyDiagram-c64418c1-D5kJldvy.js} +1 -1
  58. package/dist/assets/json.worker-usMZ-FED.js +58 -0
  59. package/dist/assets/jsonMode-45dv39mU.js +7 -0
  60. package/dist/assets/julia-cl7-CwDS.js +1 -0
  61. package/dist/assets/kotlin-s7OhZKlX.js +1 -0
  62. package/dist/assets/{layout-BILp7GjD.js → layout-DYNFLnIl.js} +1 -1
  63. package/dist/assets/less-9HpZscsL.js +2 -0
  64. package/dist/assets/lexon-OrD6JF1K.js +1 -0
  65. package/dist/assets/{line-D0Xqr8mi.js → line-1gvOYQYZ.js} +1 -1
  66. package/dist/assets/{linear-LpL8RZsq.js → linear-DHGm6Zdw.js} +1 -1
  67. package/dist/assets/liquid-BGoxrdXO.js +1 -0
  68. package/dist/assets/lspLanguageFeatures-CpCCXhrd.js +4 -0
  69. package/dist/assets/lua-Cyyb5UIc.js +1 -0
  70. package/dist/assets/m3-B8OfTtLu.js +1 -0
  71. package/dist/assets/markdown-BFxVWTOG.js +1 -0
  72. package/dist/assets/mdx-Dc2iMbEw.js +1 -0
  73. package/dist/assets/{mermaid.core-Bk0Y_0sz.js → mermaid.core-Cq2bBFF1.js} +6 -6
  74. package/dist/assets/{mindmap-definition-8da855dc-eCAgn5kY.js → mindmap-definition-8da855dc-y5l6GRVh.js} +1 -1
  75. package/dist/assets/mips-CiqrrVzr.js +1 -0
  76. package/dist/assets/msdax-DmeGPVcC.js +1 -0
  77. package/dist/assets/mysql-C_tMU-Nz.js +1 -0
  78. package/dist/assets/objective-c-BDtDVThU.js +1 -0
  79. package/dist/assets/pascal-vHIfCaH5.js +1 -0
  80. package/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
  81. package/dist/assets/perl-Ub6l9XKa.js +1 -0
  82. package/dist/assets/pgsql-BlNEE0v7.js +1 -0
  83. package/dist/assets/php-BBUBE1dy.js +1 -0
  84. package/dist/assets/{pieDiagram-a8764435-BeGFFS1p.js → pieDiagram-a8764435-PNROcv9y.js} +1 -1
  85. package/dist/assets/pla-DSh2-awV.js +1 -0
  86. package/dist/assets/postiats-CocnycG-.js +1 -0
  87. package/dist/assets/powerquery-tScXyioY.js +1 -0
  88. package/dist/assets/powershell-COWaemsV.js +1 -0
  89. package/dist/assets/protobuf-Brw8urJB.js +2 -0
  90. package/dist/assets/pug-8SOpv6rk.js +1 -0
  91. package/dist/assets/python-DjYAge7h.js +1 -0
  92. package/dist/assets/qsharp-Bw9ernYp.js +1 -0
  93. package/dist/assets/{quadrantDiagram-1e28029f-tLqbYOHC.js → quadrantDiagram-1e28029f-CFJ3VPpp.js} +1 -1
  94. package/dist/assets/r-j7ic8hl3.js +1 -0
  95. package/dist/assets/razor-OIY8fx_i.js +1 -0
  96. package/dist/assets/redis-Bu5POkcn.js +1 -0
  97. package/dist/assets/redshift-Bs9aos_-.js +1 -0
  98. package/dist/assets/{requirementDiagram-08caed73-DCverr_g.js → requirementDiagram-08caed73-BpzDIINS.js} +1 -1
  99. package/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
  100. package/dist/assets/ruby-zBfavPgS.js +1 -0
  101. package/dist/assets/rust-BzKRNQWT.js +1 -0
  102. package/dist/assets/{sankeyDiagram-a04cb91d-DidhF5PL.js → sankeyDiagram-a04cb91d-CH69-iIn.js} +1 -1
  103. package/dist/assets/sb-BBc9UKZt.js +1 -0
  104. package/dist/assets/scala-D9hQfWCl.js +1 -0
  105. package/dist/assets/scheme-BPhDTwHR.js +1 -0
  106. package/dist/assets/scss-CBJaRo0y.js +3 -0
  107. package/dist/assets/{sequenceDiagram-c5b8d532-CEKaiwTo.js → sequenceDiagram-c5b8d532-yBBEeVFU.js} +1 -1
  108. package/dist/assets/shell-DiJ1NA_G.js +1 -0
  109. package/dist/assets/solidity-Db0IVjzk.js +1 -0
  110. package/dist/assets/sophia-CnS9iZB_.js +1 -0
  111. package/dist/assets/sparql-CJmd_6j2.js +1 -0
  112. package/dist/assets/sql-ClhHkBeG.js +1 -0
  113. package/dist/assets/st-CHwy0fLd.js +1 -0
  114. package/dist/assets/{stateDiagram-1ecb1508-Cj9ZY7RH.js → stateDiagram-1ecb1508-BvF4sign.js} +1 -1
  115. package/dist/assets/{stateDiagram-v2-c2b004d7-Gsmps-dk.js → stateDiagram-v2-c2b004d7-BeyT7Ghx.js} +1 -1
  116. package/dist/assets/{styles-b4e223ce-D7E8Th0j.js → styles-b4e223ce-C58zxmK6.js} +1 -1
  117. package/dist/assets/{styles-ca3715f6-tTC26Jsm.js → styles-ca3715f6-DCE5sFi5.js} +1 -1
  118. package/dist/assets/{styles-d45a18b0-Bf6oqNdR.js → styles-d45a18b0-CG-C1aM8.js} +1 -1
  119. package/dist/assets/{svgDrawCommon-b86b1483-BxKi01m2.js → svgDrawCommon-b86b1483-F-K8GeDd.js} +1 -1
  120. package/dist/assets/swift-Bqt4WxQ4.js +3 -0
  121. package/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
  122. package/dist/assets/tcl-Dm6ycUr_.js +1 -0
  123. package/dist/assets/{timeline-definition-faaaa080-D6PePEip.js → timeline-definition-faaaa080-DPv4uqVX.js} +1 -1
  124. package/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
  125. package/dist/assets/tsMode-BtU8ZELV.js +11 -0
  126. package/dist/assets/twig-Csy3S7wG.js +1 -0
  127. package/dist/assets/typescript-CJHgISWo.js +1 -0
  128. package/dist/assets/typespec-Btyra-wh.js +1 -0
  129. package/dist/assets/vb-Db0cS2oM.js +1 -0
  130. package/dist/assets/wgsl-BTesnYfV.js +298 -0
  131. package/dist/assets/xml-C_TJw4Bi.js +1 -0
  132. package/dist/assets/{xychartDiagram-f5964ef8-BuP4qfXm.js → xychartDiagram-f5964ef8-BmXlhBzX.js} +1 -1
  133. package/dist/assets/yaml-BujeJOJ6.js +1 -0
  134. package/dist/favicon.svg +3 -3
  135. package/dist/index.html +2 -2
  136. package/package.json +14 -10
  137. package/public/favicon.svg +3 -3
  138. package/src/api/sessions.ts +36 -0
  139. package/src/api/workspace.ts +19 -0
  140. package/src/api.ts +4 -0
  141. package/src/components/NavRail.scss +9 -10
  142. package/src/components/ShortcutDisplay.scss +38 -0
  143. package/src/components/ShortcutDisplay.tsx +37 -0
  144. package/src/components/ShortcutTooltip.scss +36 -0
  145. package/src/components/ShortcutTooltip.tsx +84 -0
  146. package/src/components/Sidebar.scss +55 -13
  147. package/src/components/Sidebar.tsx +141 -52
  148. package/src/components/chat/AGENTS.md +163 -0
  149. package/src/components/chat/ChatHeader.scss +308 -49
  150. package/src/components/chat/ChatHeader.tsx +394 -80
  151. package/src/components/chat/ChatHistoryView.tsx +284 -69
  152. package/src/components/chat/ChatSettingsView.tsx +5 -3
  153. package/src/components/chat/ChatTimelineView.scss +3 -2
  154. package/src/components/chat/{sender/ThinkingStatus.tsx → ThinkingStatus.tsx} +1 -1
  155. package/src/components/chat/messages/MessageContextMenu.scss +145 -0
  156. package/src/components/chat/messages/MessageContextMenu.tsx +108 -0
  157. package/src/components/chat/messages/MessageContextMenuContent.tsx +87 -0
  158. package/src/components/chat/messages/MessageFooter.tsx +44 -24
  159. package/src/components/chat/messages/MessageItem.scss +147 -10
  160. package/src/components/chat/messages/MessageItem.tsx +378 -13
  161. package/src/components/chat/messages/build-message-context-menu-entries.ts +166 -0
  162. package/src/components/chat/messages/message-content-utils.ts +121 -0
  163. package/src/components/chat/messages/message-turns.ts +88 -0
  164. package/src/components/chat/messages/message-utils.ts +19 -1
  165. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.scss +86 -0
  166. package/src/components/chat/sender/@components/adapter-select/AdapterSelectControl.tsx +54 -0
  167. package/src/components/chat/sender/@components/adapter-select/AdapterSelectDropdown.scss +42 -0
  168. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.scss +68 -0
  169. package/src/components/chat/sender/@components/effort-select/EffortSelectControl.tsx +137 -0
  170. package/src/components/chat/sender/@components/effort-select/EffortSelectDropdown.scss +96 -0
  171. package/src/components/chat/sender/@components/model-select/ModelSelectControl.scss +82 -0
  172. package/src/components/chat/sender/@components/model-select/ModelSelectControl.tsx +171 -0
  173. package/src/components/chat/sender/@components/model-select/ModelSelectMenu.scss +95 -0
  174. package/src/components/chat/sender/@components/model-select/ModelSelectMenuLabels.scss +144 -0
  175. package/src/components/chat/sender/@components/model-select/ModelSelectOptionLabel.tsx +109 -0
  176. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.scss +106 -0
  177. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsControl.tsx +156 -0
  178. package/src/components/chat/sender/@components/reference-actions/ReferenceActionsOption.scss +34 -0
  179. package/src/components/chat/sender/@components/reference-actions/ReferencePermissionActionsPopover.tsx +111 -0
  180. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.scss +103 -0
  181. package/src/components/chat/sender/@components/sender-attachments/SenderAttachments.tsx +47 -0
  182. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +137 -0
  183. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.scss +178 -0
  184. package/src/components/chat/sender/@components/sender-interaction-panel/SenderInteractionPanel.tsx +145 -0
  185. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.scss +47 -0
  186. package/src/components/chat/sender/@components/sender-monaco-editor/SenderMonacoEditor.tsx +121 -0
  187. package/src/components/chat/sender/@components/sender-monaco-editor/monaco-runtime.ts +99 -0
  188. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-editor-handle.ts +48 -0
  189. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-editor.ts +209 -0
  190. package/src/components/chat/sender/@components/sender-monaco-editor/use-sender-monaco-theme.ts +24 -0
  191. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.scss +54 -0
  192. package/src/components/chat/sender/@components/sender-submit-action/SenderSubmitAction.tsx +80 -0
  193. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectBase.scss +71 -0
  194. package/src/components/chat/sender/@components/sender-toolbar/SenderSelectShared.scss +118 -0
  195. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.scss +99 -0
  196. package/src/components/chat/sender/@components/sender-toolbar/SenderToolbar.tsx +87 -0
  197. package/src/components/chat/sender/@core/build-sender-controller-result.ts +119 -0
  198. package/src/components/chat/sender/@core/build-sender-toolbar.ts +122 -0
  199. package/src/components/chat/sender/@core/content-attachments.ts +76 -0
  200. package/src/components/chat/sender/@core/create-sender-toolbar-handlers.ts +115 -0
  201. package/src/components/chat/sender/@core/get-sender-interaction-state.ts +18 -0
  202. package/src/components/chat/sender/@core/get-sender-runtime-state.ts +14 -0
  203. package/src/components/chat/sender/@core/interaction-request.ts +5 -0
  204. package/src/components/chat/sender/@core/sender-toolbar-bindings.ts +155 -0
  205. package/src/components/chat/sender/@hooks/use-model-select-browser.tsx +189 -0
  206. package/src/components/chat/sender/@hooks/use-sender-attachments.ts +143 -0
  207. package/src/components/chat/sender/@hooks/use-sender-autofocus.ts +34 -0
  208. package/src/components/chat/sender/@hooks/use-sender-completion.ts +62 -0
  209. package/src/components/chat/sender/@hooks/use-sender-composer-state.ts +34 -0
  210. package/src/components/chat/sender/@hooks/use-sender-controller.ts +193 -0
  211. package/src/components/chat/sender/@hooks/use-sender-focus-restore.ts +72 -0
  212. package/src/components/chat/sender/@hooks/use-sender-history.ts +79 -0
  213. package/src/components/chat/sender/@hooks/use-sender-keydown.ts +113 -0
  214. package/src/components/chat/sender/@hooks/use-sender-reference-actions.ts +191 -0
  215. package/src/components/chat/sender/@hooks/use-sender-reference-focus-restore.ts +21 -0
  216. package/src/components/chat/sender/@hooks/use-sender-refs.ts +19 -0
  217. package/src/components/chat/sender/@hooks/use-sender-select-overlays.ts +83 -0
  218. package/src/components/chat/sender/@hooks/use-sender-shortcuts.ts +78 -0
  219. package/src/components/chat/sender/@hooks/use-sender-submit.ts +81 -0
  220. package/src/components/chat/sender/@types/sender-composer.ts +19 -0
  221. package/src/components/chat/sender/@types/sender-editor.ts +12 -0
  222. package/src/components/chat/sender/@types/sender-props.ts +50 -0
  223. package/src/components/chat/sender/@types/sender-toolbar-types.ts +83 -0
  224. package/src/components/chat/sender/@types/sender-types.ts +21 -0
  225. package/src/components/chat/sender/@utils/sender-completion.ts +164 -0
  226. package/src/components/chat/sender/@utils/sender-constants.ts +18 -0
  227. package/src/components/chat/sender/@utils/sender-utils.ts +45 -0
  228. package/src/components/chat/sender/Sender.scss +8 -605
  229. package/src/components/chat/sender/Sender.tsx +54 -880
  230. package/src/components/chat/session-metadata.ts +55 -0
  231. package/src/components/chat/terminal/@hooks/use-terminal-instance.ts +152 -0
  232. package/src/components/chat/terminal/@hooks/use-terminal-session.ts +196 -0
  233. package/src/components/chat/terminal/ChatTerminalView.scss +62 -0
  234. package/src/components/chat/terminal/ChatTerminalView.tsx +114 -0
  235. package/src/components/chat/tools/core/ToolGroup.scss +7 -0
  236. package/src/components/chat/tools/core/ToolGroup.tsx +97 -56
  237. package/src/components/config/ConfigSectionForm.tsx +8 -1
  238. package/src/components/config/ConfigShortcutInput.tsx +9 -2
  239. package/src/components/config/configSchema.ts +12 -2
  240. package/src/components/config/record-editors/ModelServicesRecordEditor.tsx +0 -14
  241. package/src/components/dock-panel/DockPanel.scss +152 -0
  242. package/src/components/dock-panel/DockPanel.tsx +195 -0
  243. package/src/components/layout/AppShell.scss +40 -2
  244. package/src/components/layout/AppShell.tsx +25 -10
  245. package/src/components/sidebar/SessionContextMenu.scss +143 -0
  246. package/src/components/sidebar/SessionContextMenu.tsx +196 -0
  247. package/src/components/sidebar/SessionContextMenuContent.tsx +89 -0
  248. package/src/components/sidebar/SessionItem.scss +150 -67
  249. package/src/components/sidebar/SessionItem.tsx +183 -134
  250. package/src/components/sidebar/SessionList.scss +47 -17
  251. package/src/components/sidebar/SessionList.tsx +31 -16
  252. package/src/components/sidebar/SidebarHeader.scss +329 -49
  253. package/src/components/sidebar/SidebarHeader.tsx +108 -86
  254. package/src/components/sidebar/SidebarHeaderBatchActions.tsx +81 -0
  255. package/src/components/sidebar/SidebarHeaderSearchActions.tsx +176 -0
  256. package/src/components/sidebar/SidebarHeaderSelectField.tsx +24 -0
  257. package/src/components/sidebar/filter-utils.ts +23 -0
  258. package/src/components/workspace/ContextFilePicker.scss +64 -0
  259. package/src/components/workspace/ContextFilePicker.tsx +171 -0
  260. package/src/connectionManager.ts +4 -2
  261. package/src/hooks/chat/interaction-state.ts +104 -0
  262. package/src/hooks/chat/model-selector-data-builders.ts +146 -0
  263. package/src/hooks/chat/model-selector-data-option-utils.ts +62 -0
  264. package/src/hooks/chat/model-selector-data-types.ts +27 -0
  265. package/src/hooks/chat/model-selector-data.ts +109 -0
  266. package/src/hooks/chat/model-selector-recommendations.ts +69 -0
  267. package/src/hooks/chat/model-selector.ts +9 -0
  268. package/src/hooks/chat/use-chat-interaction.ts +13 -8
  269. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +167 -164
  270. package/src/hooks/chat/use-chat-models.tsx +46 -23
  271. package/src/hooks/chat/use-chat-session-actions.ts +69 -23
  272. package/src/hooks/chat/use-chat-session-messages.ts +165 -60
  273. package/src/hooks/chat/use-chat-session.ts +34 -9
  274. package/src/hooks/chat/use-chat-view.ts +26 -6
  275. package/src/hooks/chat/use-composer-control-shortcuts.ts +69 -0
  276. package/src/hooks/chat/use-terminal-dock-visibility.ts +39 -0
  277. package/src/hooks/use-roving-focus-list.ts +104 -0
  278. package/src/hooks/use-sidebar-navigation.ts +9 -4
  279. package/src/hooks/use-sidebar-query-state.ts +79 -0
  280. package/src/main.tsx +6 -1
  281. package/src/resources/locales/en.json +151 -6
  282. package/src/resources/locales/zh.json +151 -6
  283. package/src/routes/ChatRoute.scss +159 -4
  284. package/src/routes/ChatRoute.tsx +43 -9
  285. package/src/runtime-config.ts +21 -0
  286. package/src/store/index.ts +1 -3
  287. package/src/styles/global.scss +12 -2
  288. package/src/utils/chat-links.ts +21 -0
  289. package/src/utils/copy.ts +18 -0
  290. package/src/utils/shortcutUtils.ts +111 -1
  291. package/src/vite-env.d.ts +1 -0
  292. package/src/ws.ts +6 -5
  293. package/vite.config.ts +71 -7
  294. package/dist/assets/channel-bLjHfx-Q.js +0 -1
  295. package/dist/assets/clone-upfY39Je.js +0 -1
  296. package/dist/assets/flowDiagram-v2-4f6560a1-F66FzZTY.js +0 -1
  297. package/dist/assets/index-C1O04Df8.js +0 -557
  298. package/dist/assets/index-sE8VA1N7.css +0 -1
  299. package/src/components/chat/sender/CompletionMenu.scss +0 -70
  300. package/src/components/chat/sender/CompletionMenu.tsx +0 -58
  301. /package/src/components/chat/{sender/ThinkingStatus.scss → ThinkingStatus.scss} +0 -0
@@ -0,0 +1,83 @@
1
+ import type { RefObject } from 'react'
2
+ import { useEffect, useState } from 'react'
3
+
4
+ import type { RefSelectProps } from 'antd'
5
+
6
+ import type { SenderInitialContent } from '#~/components/chat/sender/@types/sender-types'
7
+
8
+ export const useSenderSelectOverlays = ({
9
+ initialContent,
10
+ isInlineEdit,
11
+ isThinking,
12
+ modelUnavailable,
13
+ supportsEffort,
14
+ modelSelectRef,
15
+ effortSelectRef
16
+ }: {
17
+ initialContent: SenderInitialContent
18
+ isInlineEdit: boolean
19
+ isThinking: boolean
20
+ modelUnavailable?: boolean
21
+ supportsEffort: boolean
22
+ modelSelectRef: RefObject<RefSelectProps>
23
+ effortSelectRef: RefObject<RefSelectProps>
24
+ }) => {
25
+ const [showModelSelect, setShowModelSelect] = useState(false)
26
+ const [showEffortSelect, setShowEffortSelect] = useState(false)
27
+ const [modelSearchValue, setModelSearchValue] = useState('')
28
+
29
+ useEffect(() => {
30
+ resetSelectOverlays()
31
+ }, [initialContent])
32
+
33
+ const resetSelectOverlays = () => {
34
+ setShowModelSelect(false)
35
+ setShowEffortSelect(false)
36
+ setModelSearchValue('')
37
+ }
38
+
39
+ useEffect(() => {
40
+ if (!showModelSelect && modelSearchValue !== '') {
41
+ setModelSearchValue('')
42
+ }
43
+ }, [modelSearchValue, showModelSelect])
44
+
45
+ const focusSelect = (selectRef: RefObject<RefSelectProps>) => {
46
+ window.requestAnimationFrame(() => {
47
+ selectRef.current?.focus?.()
48
+ })
49
+ }
50
+
51
+ const openModelSelector = () => {
52
+ if (isInlineEdit || modelUnavailable || isThinking) {
53
+ return false
54
+ }
55
+ setShowEffortSelect(false)
56
+ setModelSearchValue('')
57
+ setShowModelSelect(true)
58
+ focusSelect(modelSelectRef)
59
+ return true
60
+ }
61
+
62
+ const openEffortSelector = () => {
63
+ if (isInlineEdit || modelUnavailable || isThinking || !supportsEffort) {
64
+ return false
65
+ }
66
+ setShowModelSelect(false)
67
+ setShowEffortSelect(true)
68
+ focusSelect(effortSelectRef)
69
+ return true
70
+ }
71
+
72
+ return {
73
+ showModelSelect,
74
+ setShowModelSelect,
75
+ showEffortSelect,
76
+ setShowEffortSelect,
77
+ modelSearchValue,
78
+ setModelSearchValue,
79
+ resetSelectOverlays,
80
+ openModelSelector,
81
+ openEffortSelector
82
+ }
83
+ }
@@ -0,0 +1,78 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import useSWR from 'swr'
4
+
5
+ import type { ConfigResponse } from '@vibe-forge/types'
6
+
7
+ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
8
+ import { useComposerControlShortcuts } from '#~/hooks/chat/use-composer-control-shortcuts'
9
+ import { resolveSendShortcut } from '#~/utils/shortcutUtils'
10
+
11
+ export const useSenderShortcuts = ({
12
+ enabled,
13
+ isInlineEdit,
14
+ isMac,
15
+ isThinking,
16
+ modelUnavailable,
17
+ permissionModeOptions,
18
+ referenceActions,
19
+ selectOverlays
20
+ }: {
21
+ enabled: boolean
22
+ isInlineEdit: boolean
23
+ isMac: boolean
24
+ isThinking: boolean
25
+ modelUnavailable?: boolean
26
+ permissionModeOptions: Array<{ value: PermissionMode; label: ReactNode }>
27
+ referenceActions: {
28
+ closeReferenceActions: (options?: { restoreFocus?: boolean }) => void
29
+ openPermissionShortcutMenu: () => void
30
+ }
31
+ selectOverlays: {
32
+ openModelSelector: () => boolean
33
+ openEffortSelector: () => boolean
34
+ setShowModelSelect: (nextOpen: boolean) => void
35
+ setShowEffortSelect: (nextOpen: boolean) => void
36
+ }
37
+ }) => {
38
+ const { data: configRes } = useSWR<ConfigResponse>('/api/config')
39
+ const mergedShortcuts = configRes?.sources?.merged?.shortcuts
40
+ const resolvedSendShortcut = resolveSendShortcut(mergedShortcuts?.sendMessage, isMac)
41
+
42
+ const composerControlShortcuts = useComposerControlShortcuts({
43
+ enabled,
44
+ isMac,
45
+ shortcuts: mergedShortcuts,
46
+ onSwitchModel: (event) => {
47
+ event.preventDefault()
48
+ event.stopPropagation()
49
+ if (selectOverlays.openModelSelector()) {
50
+ referenceActions.closeReferenceActions()
51
+ }
52
+ },
53
+ onSwitchEffort: (event) => {
54
+ event.preventDefault()
55
+ event.stopPropagation()
56
+ if (selectOverlays.openEffortSelector()) {
57
+ referenceActions.closeReferenceActions()
58
+ }
59
+ },
60
+ onSwitchPermissionMode: (event) => {
61
+ if (isInlineEdit || modelUnavailable || isThinking || permissionModeOptions.length === 0) {
62
+ return
63
+ }
64
+
65
+ event.preventDefault()
66
+ event.stopPropagation()
67
+ selectOverlays.setShowModelSelect(false)
68
+ selectOverlays.setShowEffortSelect(false)
69
+ referenceActions.openPermissionShortcutMenu()
70
+ }
71
+ })
72
+
73
+ return {
74
+ clearInputShortcut: mergedShortcuts?.clearInput,
75
+ composerControlShortcuts,
76
+ resolvedSendShortcut
77
+ }
78
+ }
@@ -0,0 +1,81 @@
1
+ import type { TFunction } from 'i18next'
2
+
3
+ import type { MessageInstance } from 'antd/es/message/interface'
4
+
5
+ import { buildMessageContent } from '#~/components/chat/sender/@core/content-attachments'
6
+ import type { SenderSubmitResult } from '#~/components/chat/sender/@types/sender-types'
7
+ import { saveChatHistoryEntry } from '#~/components/chat/sender/@utils/sender-utils'
8
+
9
+ export const useSenderSubmit = ({
10
+ getInput,
11
+ pendingImages,
12
+ pendingFiles,
13
+ isBusy,
14
+ isInlineEdit,
15
+ modelUnavailable,
16
+ interactionRequest,
17
+ onInteractionResponse,
18
+ onSend,
19
+ onSendContent,
20
+ message,
21
+ t,
22
+ resetComposer
23
+ }: {
24
+ getInput: () => string
25
+ pendingImages: Parameters<typeof buildMessageContent>[1]
26
+ pendingFiles: Parameters<typeof buildMessageContent>[2]
27
+ isBusy: boolean
28
+ isInlineEdit: boolean
29
+ modelUnavailable?: boolean
30
+ interactionRequest?: { id: string } | null
31
+ onInteractionResponse?: (id: string, data: string | string[]) => void
32
+ onSend: (text: string) => SenderSubmitResult | Promise<SenderSubmitResult>
33
+ onSendContent: (content: ReturnType<typeof buildMessageContent>) => SenderSubmitResult | Promise<SenderSubmitResult>
34
+ message: MessageInstance
35
+ t: TFunction
36
+ resetComposer: () => void
37
+ }) => {
38
+ return async () => {
39
+ const input = getInput()
40
+
41
+ if (isBusy || (input.trim() === '' && pendingImages.length === 0 && pendingFiles.length === 0)) {
42
+ return
43
+ }
44
+ if (!isInlineEdit && modelUnavailable) {
45
+ void message.warning(t('chat.modelConfigRequired'))
46
+ return
47
+ }
48
+ if (!isInlineEdit && interactionRequest != null && onInteractionResponse != null) {
49
+ if (pendingImages.length > 0 || pendingFiles.length > 0) {
50
+ void message.warning(
51
+ pendingImages.length > 0 ? t('chat.imageNotSupportedInInteraction') : t('chat.fileNotSupportedInInteraction')
52
+ )
53
+ return
54
+ }
55
+ onInteractionResponse(interactionRequest.id, input.trim())
56
+ resetComposer()
57
+ return
58
+ }
59
+
60
+ let didSubmit = true
61
+ if (pendingImages.length > 0 || pendingFiles.length > 0) {
62
+ const content = buildMessageContent(input, pendingImages, pendingFiles)
63
+ if (isInlineEdit) {
64
+ didSubmit = (await onSendContent(content)) !== false
65
+ } else {
66
+ void onSendContent(content)
67
+ }
68
+ } else if (isInlineEdit) {
69
+ didSubmit = (await onSend(input)) !== false
70
+ } else {
71
+ void onSend(input)
72
+ }
73
+
74
+ if (!didSubmit) {
75
+ return
76
+ }
77
+
78
+ saveChatHistoryEntry(input)
79
+ resetComposer()
80
+ }
81
+ }
@@ -0,0 +1,19 @@
1
+ export interface PendingImage {
2
+ id: string
3
+ url: string
4
+ name?: string
5
+ size?: number
6
+ mimeType?: string
7
+ }
8
+
9
+ export interface PendingContextFile {
10
+ path: string
11
+ name?: string
12
+ size?: number
13
+ }
14
+
15
+ export interface SenderComposerState {
16
+ input: string
17
+ pendingImages: PendingImage[]
18
+ pendingFiles: PendingContextFile[]
19
+ }
@@ -0,0 +1,12 @@
1
+ export interface SenderEditorSelection {
2
+ start: number
3
+ end: number
4
+ }
5
+
6
+ export interface SenderEditorHandle {
7
+ focus: () => void
8
+ setSelection: (selection: SenderEditorSelection) => void
9
+ getSelection: () => SenderEditorSelection | null
10
+ getValue: () => string
11
+ isDisabled: () => boolean
12
+ }
@@ -0,0 +1,50 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import type { AskUserQuestionParams, ChatMessageContent, SessionStatus } from '@vibe-forge/core'
4
+ import type { SessionInfo } from '@vibe-forge/types'
5
+
6
+ import type { ChatErrorBannerState } from '#~/hooks/chat/interaction-state'
7
+ import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
8
+ import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
9
+ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
10
+
11
+ import type { SenderInitialContent, SenderSubmitResult, SenderVariant } from './sender-types'
12
+
13
+ export interface SenderProps {
14
+ onSend: (text: string) => SenderSubmitResult | Promise<SenderSubmitResult>
15
+ onSendContent: (content: ChatMessageContent[]) => SenderSubmitResult | Promise<SenderSubmitResult>
16
+ variant?: SenderVariant
17
+ adapterLocked?: boolean
18
+ sessionStatus?: SessionStatus
19
+ onInterrupt: () => void
20
+ onClear?: () => void
21
+ sessionInfo?: SessionInfo | null
22
+ errorBanner?: ChatErrorBannerState | null
23
+ onRetryConnection?: () => void
24
+ interactionRequest?: { id: string; payload: AskUserQuestionParams } | null
25
+ onInteractionResponse?: (id: string, data: string | string[]) => void
26
+ placeholder?: string
27
+ initialContent?: SenderInitialContent
28
+ onCancel?: () => void
29
+ submitLabel?: string
30
+ submitLoading?: boolean
31
+ autoFocus?: boolean
32
+ modelMenuGroups?: ModelSelectMenuGroup[]
33
+ modelSearchOptions?: ModelSelectOption[]
34
+ recommendedModelOptions?: ModelSelectOption[]
35
+ servicePreviewModelOptions?: ModelSelectOption[]
36
+ onToggleRecommendedModel?: (option: ModelSelectOption) => void | Promise<void>
37
+ updatingRecommendedModelValue?: string
38
+ selectedModel?: string
39
+ onModelChange?: (model: string) => void
40
+ effort?: ChatEffort
41
+ effortOptions?: Array<{ value: ChatEffort; label: ReactNode }>
42
+ onEffortChange?: (effort: ChatEffort) => void
43
+ permissionMode?: PermissionMode
44
+ permissionModeOptions?: Array<{ value: PermissionMode; label: ReactNode }>
45
+ onPermissionModeChange?: (mode: PermissionMode) => void
46
+ selectedAdapter?: string
47
+ adapterOptions?: Array<{ value: string; label: ReactNode }>
48
+ onAdapterChange?: (adapter: string) => void
49
+ modelUnavailable?: boolean
50
+ }
@@ -0,0 +1,83 @@
1
+ import type { ChangeEvent, KeyboardEvent, ReactNode, RefObject } from 'react'
2
+
3
+ import type { RefSelectProps } from 'antd'
4
+
5
+ import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
6
+ import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
7
+ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
8
+ import type { ReferenceMenuKey, RovingFocusNavigation } from './sender-types'
9
+
10
+ export interface SenderToolbarShortcuts {
11
+ switchModel: string
12
+ switchEffort: string
13
+ switchPermissionMode: string
14
+ }
15
+
16
+ export interface SenderToolbarState {
17
+ isInlineEdit: boolean
18
+ isThinking: boolean
19
+ modelUnavailable: boolean
20
+ adapterLocked: boolean
21
+ submitLoading: boolean
22
+ supportsEffort: boolean
23
+ canOpenReferenceActions: boolean
24
+ showModelSelect: boolean
25
+ showEffortSelect: boolean
26
+ showReferenceActions: boolean
27
+ showPermissionActions: boolean
28
+ modelSearchValue: string
29
+ selectedModel?: string
30
+ effort: ChatEffort
31
+ permissionMode: PermissionMode
32
+ selectedAdapter?: string
33
+ isMac: boolean
34
+ resolvedSendShortcut: string
35
+ hasComposerContent: boolean
36
+ hasSendText: boolean
37
+ }
38
+
39
+ export interface SenderToolbarData {
40
+ modelMenuGroups?: ModelSelectMenuGroup[]
41
+ modelSearchOptions?: ModelSelectOption[]
42
+ recommendedModelOptions?: ModelSelectOption[]
43
+ servicePreviewModelOptions?: ModelSelectOption[]
44
+ updatingRecommendedModelValue?: string
45
+ effortOptions: Array<{ value: ChatEffort; label: ReactNode }>
46
+ permissionModeOptions: Array<{ value: PermissionMode; label: ReactNode }>
47
+ adapterOptions?: Array<{ value: string; label: ReactNode }>
48
+ composerControlShortcuts: SenderToolbarShortcuts
49
+ submitLabel?: string
50
+ }
51
+
52
+ export interface SenderToolbarRefs {
53
+ fileInputRef: RefObject<HTMLInputElement>
54
+ modelSelectRef: RefObject<RefSelectProps>
55
+ effortSelectRef: RefObject<RefSelectProps>
56
+ referenceMenuNavigation: RovingFocusNavigation<ReferenceMenuKey>
57
+ permissionMenuNavigation: RovingFocusNavigation<PermissionMode>
58
+ }
59
+
60
+ export interface SenderToolbarHandlers {
61
+ onImageFileChange: (event: ChangeEvent<HTMLInputElement>) => void
62
+ onReferenceOpenChange: (nextOpen: boolean) => void
63
+ onShowModelSelectChange: (nextOpen: boolean) => void
64
+ onShowEffortSelectChange: (nextOpen: boolean) => void
65
+ onShowPermissionActionsChange: (nextOpen: boolean) => void
66
+ onModelSearchValueChange: (value: string) => void
67
+ onOpenContextPicker: () => void
68
+ onReferenceImageSelect: () => void
69
+ onSelectPermissionMode: (mode: PermissionMode) => void
70
+ onReferenceMenuKeyDown: (event: KeyboardEvent<HTMLButtonElement>, key: ReferenceMenuKey) => void
71
+ onPermissionMenuKeyDown: (event: KeyboardEvent<HTMLButtonElement>, key: PermissionMode) => void
72
+ onOpenModelSelector: () => void
73
+ onOpenEffortSelector: () => void
74
+ onQueueTextareaFocusRestore: () => void
75
+ onCloseReferenceActions: () => void
76
+ onModelChange?: (model: string) => void
77
+ onToggleRecommendedModel?: (option: ModelSelectOption) => void | Promise<void>
78
+ onEffortChange?: (effort: ChatEffort) => void
79
+ onAdapterChange?: (adapter: string) => void
80
+ onSend: () => void
81
+ onInterrupt: () => void
82
+ onCancel?: () => void
83
+ }
@@ -0,0 +1,21 @@
1
+ import type { ChatMessageContent } from '@vibe-forge/core'
2
+
3
+ export type SenderVariant = 'default' | 'inline-edit'
4
+
5
+ export type SenderInitialContent = string | ChatMessageContent[] | undefined
6
+
7
+ export type SenderSubmitResult = boolean | void
8
+
9
+ export type ReferenceMenuKey = 'image' | 'file' | 'permission'
10
+
11
+ export type MenuFocusTarget = 'reference' | 'permission' | null
12
+
13
+ export interface RovingFocusNavigation<T extends string> {
14
+ activeKey: T | null
15
+ setActiveKey: (key: T | null) => void
16
+ registerItem: (key: T) => (node: HTMLElement | null) => void
17
+ focusKey: (key: T | null) => void
18
+ moveFocus: (delta: number, fromKey?: T | null) => T | null
19
+ focusFirst: () => T | null
20
+ focusLast: () => T | null
21
+ }
@@ -0,0 +1,164 @@
1
+ import type { SessionInfo } from '@vibe-forge/types'
2
+
3
+ export type SenderCompletionTrigger = '/' | '@' | '#'
4
+
5
+ export type SenderCompletionKind = 'command' | 'agent' | 'tool'
6
+
7
+ export interface SenderCompletionItem {
8
+ kind: SenderCompletionKind
9
+ label: string
10
+ value: string
11
+ }
12
+
13
+ export interface SenderCompletionMatch {
14
+ trigger: SenderCompletionTrigger
15
+ kind: SenderCompletionKind
16
+ query: string
17
+ replaceStart: number
18
+ cursorOffset: number
19
+ items: SenderCompletionItem[]
20
+ }
21
+
22
+ export interface SenderTokenDecoration {
23
+ start: number
24
+ end: number
25
+ className: string
26
+ }
27
+
28
+ interface CompletionSource {
29
+ trigger: SenderCompletionTrigger
30
+ kind: SenderCompletionKind
31
+ values: string[]
32
+ }
33
+
34
+ const COMPLETION_MATCHER = /(^|\s)([@/#])([^\s@/#]*)$/
35
+
36
+ const TOKEN_MATCHER = /(^|\s)([@/#])([^\s@/#]+)/g
37
+
38
+ const DEMO_COMPLETION_SOURCES: CompletionSource[] = [
39
+ {
40
+ trigger: '/',
41
+ kind: 'command',
42
+ values: ['plan', 'summarize', 'review', 'fix', 'explain']
43
+ },
44
+ {
45
+ trigger: '@',
46
+ kind: 'agent',
47
+ values: ['solution-analyst', 'dev-planner', 'dev-implementer', 'dev-reviewer']
48
+ },
49
+ {
50
+ trigger: '#',
51
+ kind: 'tool',
52
+ values: ['ChromeDevtools', 'Bash', 'Read', 'Edit', 'Search']
53
+ }
54
+ ]
55
+
56
+ const getCompletionSources = (sessionInfo?: SessionInfo | null): CompletionSource[] => {
57
+ if (sessionInfo?.type !== 'init') {
58
+ return DEMO_COMPLETION_SOURCES
59
+ }
60
+
61
+ const runtimeSources = [
62
+ { trigger: '/', kind: 'command', values: sessionInfo.slashCommands ?? [] },
63
+ { trigger: '@', kind: 'agent', values: sessionInfo.agents ?? [] },
64
+ { trigger: '#', kind: 'tool', values: sessionInfo.tools ?? [] }
65
+ ] satisfies CompletionSource[]
66
+
67
+ const hasRuntimeValues = runtimeSources.some(source => source.values.length > 0)
68
+
69
+ return hasRuntimeValues ? runtimeSources : DEMO_COMPLETION_SOURCES
70
+ }
71
+
72
+ const getDecorationClassName = (trigger: SenderCompletionTrigger) => {
73
+ if (trigger === '@') {
74
+ return 'chat-input-monaco__token--agent'
75
+ }
76
+
77
+ if (trigger === '#') {
78
+ return 'chat-input-monaco__token--tool'
79
+ }
80
+
81
+ return 'chat-input-monaco__token--command'
82
+ }
83
+
84
+ const sortCompletionItems = (items: SenderCompletionItem[], query: string) => {
85
+ const normalizedQuery = query.toLowerCase()
86
+
87
+ return items.toSorted((left, right) => {
88
+ const leftStartsWith = left.value.toLowerCase().startsWith(normalizedQuery)
89
+ const rightStartsWith = right.value.toLowerCase().startsWith(normalizedQuery)
90
+
91
+ if (leftStartsWith !== rightStartsWith) {
92
+ return leftStartsWith ? -1 : 1
93
+ }
94
+
95
+ return left.value.localeCompare(right.value)
96
+ })
97
+ }
98
+
99
+ export const resolveSenderCompletionMatch = (
100
+ value: string,
101
+ cursorOffset: number | null,
102
+ sessionInfo?: SessionInfo | null
103
+ ): SenderCompletionMatch | null => {
104
+ const resolvedCursorOffset = cursorOffset ?? value.length
105
+ const textBeforeCursor = value.slice(0, resolvedCursorOffset)
106
+ const match = COMPLETION_MATCHER.exec(textBeforeCursor)
107
+
108
+ if (match == null) {
109
+ return null
110
+ }
111
+
112
+ const trigger = match[2] as SenderCompletionTrigger
113
+ const query = match[3] ?? ''
114
+ const replaceStart = resolvedCursorOffset - query.length
115
+ const normalizedQuery = query.toLowerCase()
116
+ const source = getCompletionSources(sessionInfo).find(item => item.trigger === trigger)
117
+
118
+ if (source == null) {
119
+ return null
120
+ }
121
+
122
+ const items = source.values
123
+ .filter(item => normalizedQuery === '' || item.toLowerCase().includes(normalizedQuery))
124
+ .map(item => ({
125
+ kind: source.kind,
126
+ label: `${trigger}${item}`,
127
+ value: item
128
+ }))
129
+
130
+ return {
131
+ trigger,
132
+ kind: source.kind,
133
+ query,
134
+ replaceStart,
135
+ cursorOffset: resolvedCursorOffset,
136
+ items: sortCompletionItems(items, query)
137
+ }
138
+ }
139
+
140
+ export const resolveSenderTokenDecorations = (value: string): SenderTokenDecoration[] => {
141
+ const decorations: SenderTokenDecoration[] = []
142
+
143
+ for (const match of value.matchAll(TOKEN_MATCHER)) {
144
+ const leadingWhitespace = match[1] ?? ''
145
+ const trigger = match[2] as SenderCompletionTrigger | undefined
146
+ const token = match[3] ?? ''
147
+ const matchIndex = match.index
148
+
149
+ if (trigger == null || matchIndex == null || token === '') {
150
+ continue
151
+ }
152
+
153
+ const start = matchIndex + leadingWhitespace.length
154
+ const end = start + 1 + token.length
155
+
156
+ decorations.push({
157
+ start,
158
+ end,
159
+ className: getDecorationClassName(trigger)
160
+ })
161
+ }
162
+
163
+ return decorations
164
+ }
@@ -0,0 +1,18 @@
1
+ import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
2
+ import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
3
+
4
+ export const effortIconMap: Record<ChatEffort, string> = {
5
+ default: 'auto_awesome',
6
+ low: 'signal_cellular_alt_1_bar',
7
+ medium: 'signal_cellular_alt_2_bar',
8
+ high: 'signal_cellular_alt',
9
+ max: 'bolt'
10
+ }
11
+
12
+ export const permissionModeIconMap: Record<PermissionMode, string> = {
13
+ default: 'tune',
14
+ acceptEdits: 'edit_note',
15
+ plan: 'checklist',
16
+ dontAsk: 'verified_user',
17
+ bypassPermissions: 'shield_lock'
18
+ }
@@ -0,0 +1,45 @@
1
+ const CHAT_HISTORY_STORAGE_KEY = 'vf_chat_history'
2
+
3
+ const reportHistoryError = (action: 'read' | 'write', error: unknown) => {
4
+ console.error(`Failed to ${action} chat history`, error)
5
+ }
6
+
7
+ export const isActivationKey = (key: string) => key === 'Enter' || key === ' '
8
+
9
+ export const readFileAsDataUrl = (file: File) => {
10
+ return new Promise<string>((resolve, reject) => {
11
+ const reader = new FileReader()
12
+ reader.onload = () => {
13
+ resolve(typeof reader.result === 'string' ? reader.result : '')
14
+ }
15
+ reader.onerror = () => {
16
+ reject(new Error('read_failed'))
17
+ }
18
+ reader.readAsDataURL(file)
19
+ })
20
+ }
21
+
22
+ export const loadChatHistory = () => {
23
+ try {
24
+ const rawHistory = localStorage.getItem(CHAT_HISTORY_STORAGE_KEY)
25
+ const parsedHistory = rawHistory != null ? JSON.parse(rawHistory) : []
26
+ if (!Array.isArray(parsedHistory)) {
27
+ return []
28
+ }
29
+
30
+ return parsedHistory.filter((item): item is string => typeof item === 'string')
31
+ } catch (error) {
32
+ reportHistoryError('read', error)
33
+ return []
34
+ }
35
+ }
36
+
37
+ export const saveChatHistoryEntry = (input: string) => {
38
+ try {
39
+ const history = loadChatHistory()
40
+ const nextHistory = [input, ...history.filter(entry => entry !== input)].slice(0, 50)
41
+ localStorage.setItem(CHAT_HISTORY_STORAGE_KEY, JSON.stringify(nextHistory))
42
+ } catch (error) {
43
+ reportHistoryError('write', error)
44
+ }
45
+ }