@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
@@ -3,14 +3,19 @@ import './ToolGroup.scss'
3
3
  import type { ChatMessage, ChatMessageContent } from '@vibe-forge/core'
4
4
  import React, { useState } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
+ import { useSearchParams } from 'react-router-dom'
7
+ import { MessageContextMenu } from '../../messages/MessageContextMenu'
6
8
  import { MessageFooter } from '../../messages/MessageFooter'
7
9
  import { ToolRenderer } from './ToolRenderer'
8
10
 
9
11
  interface ToolGroupProps {
12
+ anchorId: string
10
13
  items: {
11
14
  item: Extract<ChatMessageContent, { type: 'tool_use' }>
12
15
  resultItem?: Extract<ChatMessageContent, { type: 'tool_result' }>
13
16
  }[]
17
+ originalMessage: ChatMessage
18
+ sessionId?: string
14
19
  footer?: {
15
20
  model?: string
16
21
  usage?: ChatMessage['usage']
@@ -20,10 +25,15 @@ interface ToolGroupProps {
20
25
  }
21
26
 
22
27
  function ToolGroupComponent({
28
+ anchorId,
23
29
  items,
30
+ originalMessage,
31
+ sessionId,
24
32
  footer
25
33
  }: ToolGroupProps) {
26
34
  const { t } = useTranslation()
35
+ const [searchParams] = useSearchParams()
36
+ const isDebugMode = searchParams.get('debug') === 'true'
27
37
  const [expanded, setExpanded] = useState(false)
28
38
 
29
39
  if (items.length === 0) return null
@@ -31,19 +41,33 @@ function ToolGroupComponent({
31
41
  // If only one item, just render it directly (wrapped in container for footer)
32
42
  if (items.length === 1) {
33
43
  return (
34
- <div className='tool-group-container'>
35
- <div className='tool-group-wrapper single-item'>
36
- <ToolRenderer
37
- item={items[0].item}
38
- resultItem={items[0].resultItem}
39
- />
40
- </div>
41
- {footer && (
42
- <div className='tool-group-footer-wrapper'>
43
- <MessageFooter msg={footer.originalMessage} isUser={false} />
44
+ <MessageContextMenu
45
+ anchorId={anchorId}
46
+ canEdit={false}
47
+ canFork={false}
48
+ canRecall={false}
49
+ isDebugMode={isDebugMode}
50
+ isEditing={false}
51
+ message={originalMessage}
52
+ sessionId={sessionId}
53
+ onFork={() => {}}
54
+ onRecall={() => {}}
55
+ onStartEditing={() => {}}
56
+ >
57
+ <div id={anchorId} className='tool-group-container'>
58
+ <div className='tool-group-wrapper single-item'>
59
+ <ToolRenderer
60
+ item={items[0].item}
61
+ resultItem={items[0].resultItem}
62
+ />
44
63
  </div>
45
- )}
46
- </div>
64
+ {footer && isDebugMode && (
65
+ <div className='tool-group-footer-wrapper'>
66
+ <MessageFooter msg={footer.originalMessage} isUser={false} />
67
+ </div>
68
+ )}
69
+ </div>
70
+ </MessageContextMenu>
47
71
  )
48
72
  }
49
73
 
@@ -51,65 +75,82 @@ function ToolGroupComponent({
51
75
  const otherItems = items.slice(0, -1)
52
76
 
53
77
  return (
54
- <div className='tool-group-container'>
55
- <div className='tool-group-wrapper card-style'>
56
- <div
57
- className='tool-group-header'
58
- onClick={() => setExpanded(!expanded)}
59
- >
60
- <div className='header-left'>
61
- <span className='material-symbols-rounded'>dataset</span>
62
- <span>{t('chat.usedTools', { count: items.length })}</span>
78
+ <MessageContextMenu
79
+ anchorId={anchorId}
80
+ canEdit={false}
81
+ canFork={false}
82
+ canRecall={false}
83
+ isDebugMode={isDebugMode}
84
+ isEditing={false}
85
+ message={originalMessage}
86
+ sessionId={sessionId}
87
+ onFork={() => {}}
88
+ onRecall={() => {}}
89
+ onStartEditing={() => {}}
90
+ >
91
+ <div id={anchorId} className='tool-group-container'>
92
+ <div className='tool-group-wrapper card-style'>
93
+ <div
94
+ className='tool-group-header'
95
+ onClick={() => setExpanded(!expanded)}
96
+ >
97
+ <div className='header-left'>
98
+ <span className='material-symbols-rounded'>dataset</span>
99
+ <span>{t('chat.usedTools', { count: items.length })}</span>
100
+ </div>
101
+ <span className='material-symbols-rounded expand-icon'>
102
+ {expanded ? 'expand_less' : 'expand_more'}
103
+ </span>
63
104
  </div>
64
- <span className='material-symbols-rounded expand-icon'>
65
- {expanded ? 'expand_less' : 'expand_more'}
66
- </span>
67
- </div>
68
105
 
69
- {expanded && (
70
- <div className='tool-group-list'>
71
- {otherItems.map((it, idx) => (
72
- <ToolRenderer
73
- key={it.item.id || idx}
74
- item={it.item}
75
- resultItem={it.resultItem}
76
- />
77
- ))}
78
- </div>
79
- )}
106
+ {expanded && (
107
+ <div className='tool-group-list'>
108
+ {otherItems.map((it, idx) => (
109
+ <ToolRenderer
110
+ key={it.item.id || idx}
111
+ item={it.item}
112
+ resultItem={it.resultItem}
113
+ />
114
+ ))}
115
+ </div>
116
+ )}
80
117
 
81
- {
82
- /* Always show the last item, but if expanded, it's just part of the list visually.
83
- If collapsed, it appears "below" the header.
84
- Actually, to make it look like "part of the list", we should just put it in the flow.
118
+ {
119
+ /* Always show the last item, but if expanded, it's just part of the list visually.
120
+ If collapsed, it appears "below" the header.
121
+ Actually, to make it look like "part of the list", we should just put it in the flow.
85
122
 
86
- When collapsed: Header + Last Item
87
- When expanded: Header + Other Items + Last Item
88
- */
89
- }
90
- <div className='tool-group-last-item'>
91
- <ToolRenderer
92
- item={lastItem.item}
93
- resultItem={lastItem.resultItem}
94
- />
123
+ When collapsed: Header + Last Item
124
+ When expanded: Header + Other Items + Last Item
125
+ */
126
+ }
127
+ <div className='tool-group-last-item'>
128
+ <ToolRenderer
129
+ item={lastItem.item}
130
+ resultItem={lastItem.resultItem}
131
+ />
132
+ </div>
95
133
  </div>
96
- </div>
97
134
 
98
- {footer && (
99
- <div className='tool-group-footer-wrapper'>
100
- <MessageFooter msg={footer.originalMessage} isUser={false} />
101
- </div>
102
- )}
103
- </div>
135
+ {footer && isDebugMode && (
136
+ <div className='tool-group-footer-wrapper'>
137
+ <MessageFooter msg={footer.originalMessage} isUser={false} />
138
+ </div>
139
+ )}
140
+ </div>
141
+ </MessageContextMenu>
104
142
  )
105
143
  }
106
144
 
107
145
  const areToolGroupPropsEqual = (prev: ToolGroupProps, next: ToolGroupProps) => {
146
+ if (prev.anchorId !== next.anchorId) return false
108
147
  if (prev.items.length !== next.items.length) return false
109
148
  for (let i = 0; i < prev.items.length; i++) {
110
149
  if (prev.items[i].item !== next.items[i].item) return false
111
150
  if (prev.items[i].resultItem !== next.items[i].resultItem) return false
112
151
  }
152
+ if (prev.originalMessage !== next.originalMessage) return false
153
+ if (prev.sessionId !== next.sessionId) return false
113
154
  if (prev.footer == null && next.footer == null) return true
114
155
  if (prev.footer == null || next.footer == null) return false
115
156
  return prev.footer.originalMessage === next.footer.originalMessage &&
@@ -3,6 +3,7 @@ import './ConfigSectionForm.scss'
3
3
  import { Collapse, Empty, Input, InputNumber, Select, Slider, Switch } from 'antd'
4
4
  import type { ReactNode } from 'react'
5
5
 
6
+ import { normalizeSendShortcut, resolveSendShortcut } from '#~/utils/shortcutUtils'
6
7
  import { ComplexTextEditor, StringArrayEditor } from './ConfigEditors'
7
8
  import { FieldRow } from './ConfigFieldRow'
8
9
  import { ShortcutInput } from './ConfigShortcutInput'
@@ -297,11 +298,17 @@ export const SectionForm = ({
297
298
  }
298
299
  } else if (field.type === 'shortcut') {
299
300
  const isMac = navigator.platform.includes('Mac')
301
+ const rawValue = typeof valueToUse === 'string' ? valueToUse : ''
302
+ const isSendMessageShortcut = field.shortcutKind === 'sendMessage'
300
303
  control = (
301
304
  <ShortcutInput
302
- value={typeof valueToUse === 'string' ? valueToUse : ''}
305
+ value={rawValue}
306
+ displayValue={isSendMessageShortcut ? resolveSendShortcut(rawValue, isMac) : undefined}
303
307
  onChange={(next) => handleValueChange(next)}
304
308
  placeholder={t('config.editor.shortcutPlaceholder')}
309
+ normalizeShortcut={isSendMessageShortcut
310
+ ? (next) => normalizeSendShortcut(next, isMac)
311
+ : undefined}
305
312
  isMac={isMac}
306
313
  t={t}
307
314
  />
@@ -7,18 +7,23 @@ import { formatShortcutLabel, getShortcutFromEvent } from '../../utils/shortcutU
7
7
 
8
8
  export const ShortcutInput = ({
9
9
  value,
10
+ displayValue,
10
11
  onChange,
11
12
  placeholder,
13
+ normalizeShortcut,
12
14
  isMac,
13
15
  t
14
16
  }: {
15
17
  value: string
18
+ displayValue?: string
16
19
  onChange: (nextValue: string) => void
17
20
  placeholder: string
21
+ normalizeShortcut?: (nextValue: string) => string | null
18
22
  isMac: boolean
19
23
  t: (key: string) => string
20
24
  }) => {
21
- const label = value.trim() === '' ? '' : formatShortcutLabel(value, isMac)
25
+ const effectiveValue = (displayValue ?? value).trim()
26
+ const label = effectiveValue === '' ? '' : formatShortcutLabel(effectiveValue, isMac)
22
27
  return (
23
28
  <div className='config-shortcut-input'>
24
29
  <Input
@@ -33,8 +38,10 @@ export const ShortcutInput = ({
33
38
  }
34
39
  const nextShortcut = getShortcutFromEvent(event)
35
40
  if (nextShortcut == null) return
41
+ const normalizedShortcut = normalizeShortcut?.(nextShortcut) ?? nextShortcut
42
+ if (normalizedShortcut == null) return
36
43
  event.preventDefault()
37
- onChange(nextShortcut)
44
+ onChange(normalizedShortcut)
38
45
  }}
39
46
  />
40
47
  <Tooltip title={t('config.editor.clearShortcut')}>
@@ -20,6 +20,7 @@ export interface FieldSpec {
20
20
  path: string[]
21
21
  type: FieldValueType
22
22
  defaultValue: unknown
23
+ shortcutKind?: 'sendMessage'
23
24
  icon?: string
24
25
  options?: FieldOption[]
25
26
  placeholderKey?: string
@@ -312,6 +313,12 @@ export const configSchema: Record<string, FieldSpec[]> = {
312
313
  type: 'json',
313
314
  defaultValue: [],
314
315
  icon: 'extension'
316
+ },
317
+ {
318
+ path: ['marketplaces'],
319
+ type: 'json',
320
+ defaultValue: {},
321
+ icon: 'store'
315
322
  }
316
323
  ],
317
324
  mcp: [
@@ -329,7 +336,10 @@ export const configSchema: Record<string, FieldSpec[]> = {
329
336
  shortcuts: [
330
337
  { path: ['newSession'], type: 'shortcut', defaultValue: '', icon: 'add_comment' },
331
338
  { path: ['openConfig'], type: 'shortcut', defaultValue: '', icon: 'settings' },
332
- { path: ['sendMessage'], type: 'shortcut', defaultValue: '', icon: 'send' },
333
- { path: ['clearInput'], type: 'shortcut', defaultValue: '', icon: 'clear' }
339
+ { path: ['sendMessage'], type: 'shortcut', shortcutKind: 'sendMessage', defaultValue: '', icon: 'send' },
340
+ { path: ['clearInput'], type: 'shortcut', defaultValue: '', icon: 'clear' },
341
+ { path: ['switchModel'], type: 'shortcut', defaultValue: '', icon: 'model_training' },
342
+ { path: ['switchEffort'], type: 'shortcut', defaultValue: '', icon: 'psychology' },
343
+ { path: ['switchPermissionMode'], type: 'shortcut', defaultValue: '', icon: 'lock' }
334
344
  ]
335
345
  }
@@ -165,19 +165,6 @@ export const ModelServicesRecordEditor = ({
165
165
  t={t}
166
166
  />
167
167
  </FieldRow>
168
- <FieldRow
169
- title={t('config.fields.modelServices.item.modelsAlias.label')}
170
- description={t('config.fields.modelServices.item.modelsAlias.desc')}
171
- icon={getTypeIcon('object')}
172
- layout='stacked'
173
- >
174
- <ComplexTextEditor
175
- value={recordValue.modelsAlias ?? {}}
176
- onChange={(next) => {
177
- onChange({ ...value, [key]: { ...recordValue, modelsAlias: next } })
178
- }}
179
- />
180
- </FieldRow>
181
168
  <FieldRow
182
169
  title={t('config.fields.modelServices.item.timeoutMs.label')}
183
170
  description={t('config.fields.modelServices.item.timeoutMs.desc')}
@@ -262,7 +249,6 @@ export const ModelServicesRecordEditor = ({
262
249
  apiBaseUrl: '',
263
250
  apiKey: '',
264
251
  models: [],
265
- modelsAlias: {},
266
252
  timeoutMs: undefined,
267
253
  maxOutputTokens: undefined,
268
254
  extra: {}
@@ -0,0 +1,152 @@
1
+ .dock-panel {
2
+ flex: 0 0 var(--dock-panel-current-height, var(--dock-panel-height, 240px));
3
+ height: var(--dock-panel-current-height, var(--dock-panel-height, 240px));
4
+ min-height: 0;
5
+ max-height: var(--dock-panel-max-height, 520px);
6
+ position: relative;
7
+ display: flex;
8
+ flex-direction: column;
9
+ min-width: 0;
10
+ padding: var(--dock-panel-padding-top, 12px) 12px
11
+ var(--dock-panel-padding-bottom, 12px);
12
+ background-color: var(--bg-color);
13
+ border-top: 1px solid var(--dock-panel-border-color, var(--border-color));
14
+ gap: 6px;
15
+ overflow: hidden;
16
+ opacity: var(--dock-panel-opacity, 1);
17
+ transform: translateY(var(--dock-panel-translate-y, 0));
18
+ transition:
19
+ flex-basis .24s cubic-bezier(.22, 1, .36, 1),
20
+ height .24s cubic-bezier(.22, 1, .36, 1),
21
+ padding-top .24s cubic-bezier(.22, 1, .36, 1),
22
+ padding-bottom .24s cubic-bezier(.22, 1, .36, 1),
23
+ opacity .18s ease,
24
+ transform .24s cubic-bezier(.22, 1, .36, 1),
25
+ border-top-color .18s ease;
26
+
27
+ &.is-resizing {
28
+ cursor: row-resize;
29
+ }
30
+ }
31
+
32
+ .dock-panel.is-closing {
33
+ --dock-panel-current-height: 0px;
34
+ --dock-panel-padding-top: 0px;
35
+ --dock-panel-padding-bottom: 0px;
36
+ --dock-panel-border-color: transparent;
37
+ --dock-panel-opacity: 0;
38
+ --dock-panel-translate-y: 20px;
39
+ pointer-events: none;
40
+ }
41
+
42
+ .dock-panel__resize-handle {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 8px;
46
+ min-width: 0;
47
+ min-height: 24px;
48
+ margin: -12px -12px 0;
49
+ padding: 12px 12px 0;
50
+ cursor: row-resize;
51
+ flex-shrink: 0;
52
+ transition: opacity .18s ease;
53
+
54
+ &:hover {
55
+ opacity: .92;
56
+ }
57
+ }
58
+
59
+ .dock-panel__header-main {
60
+ display: inline-flex;
61
+ flex: 1 1 auto;
62
+ align-items: center;
63
+ gap: 6px;
64
+ min-width: 0;
65
+ overflow: hidden;
66
+ }
67
+
68
+ .dock-panel__title,
69
+ .dock-panel__meta {
70
+ display: inline-flex;
71
+ align-items: center;
72
+ min-width: 0;
73
+ height: 24px;
74
+ padding: 0 8px;
75
+ border-radius: 6px;
76
+ background-color: var(--sub-bg-color);
77
+ color: var(--text-color);
78
+ font-size: 12px;
79
+ line-height: 1;
80
+ white-space: nowrap;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ }
84
+
85
+ .dock-panel__title {
86
+ font-weight: 600;
87
+ flex: 0 1 auto;
88
+ max-width: 100%;
89
+ }
90
+
91
+ .dock-panel__meta {
92
+ flex: 0 1 auto;
93
+ max-width: min(180px, 40%);
94
+ color: var(--sub-text-color);
95
+ font-family:
96
+ ui-monospace, sfmono-regular, 'SF Mono', menlo, consolas, monospace;
97
+ }
98
+
99
+ .dock-panel__header-spacer {
100
+ flex: 1;
101
+ min-width: 24px;
102
+ }
103
+
104
+ .dock-panel__header-actions {
105
+ display: inline-flex;
106
+ align-items: center;
107
+ gap: 6px;
108
+ }
109
+
110
+ .dock-panel__close-btn.ant-btn.ant-btn-text {
111
+ width: 24px;
112
+ height: 24px;
113
+ padding: 0;
114
+ border: none;
115
+ border-radius: 6px;
116
+ color: var(--sub-text-color, #6b7280);
117
+ display: inline-flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ transition: background-color .2s ease, color .2s ease;
121
+
122
+ .material-symbols-rounded {
123
+ font-size: 14px;
124
+ line-height: 1;
125
+ }
126
+
127
+ &:hover {
128
+ background-color: var(--nav-hover-bg, #f3f4f6);
129
+ color: var(--text-color, #111827);
130
+ }
131
+ }
132
+
133
+ .dock-panel__body {
134
+ flex: 1;
135
+ min-height: 0;
136
+ display: flex;
137
+ flex-direction: column;
138
+ }
139
+
140
+ .dock-panel__footer {
141
+ flex-shrink: 0;
142
+ }
143
+
144
+ html.dark .dock-panel__close-btn.ant-btn.ant-btn-text {
145
+ --nav-hover-bg: #1a1a1a;
146
+ }
147
+
148
+ @media (prefers-reduced-motion: reduce) {
149
+ .dock-panel {
150
+ transition: none;
151
+ }
152
+ }
@@ -0,0 +1,195 @@
1
+ import './DockPanel.scss'
2
+
3
+ import { Button } from 'antd'
4
+ import type { CSSProperties, PointerEvent as ReactPointerEvent, ReactNode } from 'react'
5
+ import { useEffect, useMemo, useRef, useState } from 'react'
6
+
7
+ const DEFAULT_PANEL_HEIGHT = 240
8
+
9
+ const clampPanelHeight = (height: number, minHeight: number, maxHeight: number) => {
10
+ return Math.min(Math.max(height, minHeight), Math.max(minHeight, maxHeight))
11
+ }
12
+
13
+ const shouldIgnoreResizePointerDown = (target: HTMLElement | null) => {
14
+ if (target == null) {
15
+ return false
16
+ }
17
+
18
+ return target.closest(
19
+ 'button, a, input, textarea, select, option, [role="button"], [data-dock-panel-no-resize="true"]'
20
+ ) != null
21
+ }
22
+
23
+ export function DockPanel({
24
+ enterMotion = 'slide-up',
25
+ children,
26
+ className,
27
+ closeLabel,
28
+ defaultHeight = DEFAULT_PANEL_HEIGHT,
29
+ footer,
30
+ isOpen = true,
31
+ maxHeight = 520,
32
+ meta,
33
+ minHeight = 180,
34
+ onClose,
35
+ resizeLabel,
36
+ storageKey,
37
+ title,
38
+ actions
39
+ }: {
40
+ enterMotion?: 'none' | 'slide-up'
41
+ actions?: ReactNode
42
+ children: ReactNode
43
+ className?: string
44
+ closeLabel?: string
45
+ defaultHeight?: number
46
+ footer?: ReactNode
47
+ isOpen?: boolean
48
+ maxHeight?: number
49
+ meta?: ReactNode
50
+ minHeight?: number
51
+ onClose?: () => void
52
+ resizeLabel: string
53
+ storageKey: string
54
+ title: ReactNode
55
+ }) {
56
+ const panelRef = useRef<HTMLDivElement | null>(null)
57
+ const resizeSessionRef = useRef<{ maxHeight: number; startHeight: number; startY: number } | null>(null)
58
+ const [isResizing, setIsResizing] = useState(false)
59
+ const [panelHeight, setPanelHeight] = useState(() => {
60
+ const storedHeight = Number(localStorage.getItem(storageKey))
61
+ return Number.isFinite(storedHeight) && storedHeight > 0
62
+ ? clampPanelHeight(storedHeight, minHeight, maxHeight)
63
+ : clampPanelHeight(defaultHeight, minHeight, maxHeight)
64
+ })
65
+
66
+ useEffect(() => {
67
+ localStorage.setItem(storageKey, String(panelHeight))
68
+ }, [panelHeight, storageKey])
69
+
70
+ useEffect(() => {
71
+ return () => {
72
+ document.body.style.cursor = ''
73
+ document.body.style.userSelect = ''
74
+ }
75
+ }, [])
76
+
77
+ useEffect(() => {
78
+ if (!isResizing) {
79
+ return
80
+ }
81
+
82
+ const handlePointerMove = (event: PointerEvent) => {
83
+ const resizeSession = resizeSessionRef.current
84
+ if (resizeSession == null) {
85
+ return
86
+ }
87
+
88
+ const nextHeight = clampPanelHeight(
89
+ resizeSession.startHeight + resizeSession.startY - event.clientY,
90
+ minHeight,
91
+ resizeSession.maxHeight
92
+ )
93
+
94
+ setPanelHeight(nextHeight)
95
+ }
96
+
97
+ const stopResizing = () => {
98
+ resizeSessionRef.current = null
99
+ setIsResizing(false)
100
+ document.body.style.cursor = ''
101
+ document.body.style.userSelect = ''
102
+ }
103
+
104
+ window.addEventListener('pointermove', handlePointerMove)
105
+ window.addEventListener('pointerup', stopResizing)
106
+ window.addEventListener('pointercancel', stopResizing)
107
+
108
+ return () => {
109
+ window.removeEventListener('pointermove', handlePointerMove)
110
+ window.removeEventListener('pointerup', stopResizing)
111
+ window.removeEventListener('pointercancel', stopResizing)
112
+ }
113
+ }, [isResizing, minHeight])
114
+
115
+ const handleResizePointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
116
+ if (shouldIgnoreResizePointerDown(event.target as HTMLElement | null)) {
117
+ return
118
+ }
119
+
120
+ event.preventDefault()
121
+
122
+ const panel = panelRef.current
123
+ const parent = panel?.parentElement
124
+ const parentHeight = parent?.getBoundingClientRect().height
125
+
126
+ resizeSessionRef.current = {
127
+ startY: event.clientY,
128
+ startHeight: panel?.getBoundingClientRect().height ?? panelHeight,
129
+ maxHeight: parentHeight != null ? Math.min(maxHeight, parentHeight - 96) : maxHeight
130
+ }
131
+
132
+ setIsResizing(true)
133
+ document.body.style.cursor = 'row-resize'
134
+ document.body.style.userSelect = 'none'
135
+ }
136
+
137
+ const panelStyle = useMemo(
138
+ () => ({
139
+ '--dock-panel-height': `${panelHeight}px`,
140
+ '--dock-panel-max-height': `${maxHeight}px`,
141
+ '--dock-panel-min-height': `${minHeight}px`
142
+ }),
143
+ [maxHeight, minHeight, panelHeight]
144
+ )
145
+
146
+ return (
147
+ <div
148
+ ref={panelRef}
149
+ className={`dock-panel ${isOpen && enterMotion === 'slide-up' ? 'is-entering-slide-up' : ''} ${
150
+ isOpen ? 'is-open' : 'is-closing'
151
+ } ${className ?? ''} ${isResizing ? 'is-resizing' : ''}`}
152
+ style={panelStyle as CSSProperties}
153
+ >
154
+ <div
155
+ className='dock-panel__resize-handle'
156
+ title={resizeLabel}
157
+ onPointerDown={handleResizePointerDown}
158
+ >
159
+ <div className='dock-panel__header-main'>
160
+ <span className='dock-panel__title'>{title}</span>
161
+ {meta != null && (
162
+ <span className='dock-panel__meta'>{meta}</span>
163
+ )}
164
+ </div>
165
+ <div className='dock-panel__header-spacer' />
166
+ {(actions != null || onClose != null) && (
167
+ <div className='dock-panel__header-actions'>
168
+ {actions}
169
+ {onClose != null && closeLabel != null && (
170
+ <Button
171
+ type='text'
172
+ className='dock-panel__close-btn'
173
+ data-dock-panel-no-resize='true'
174
+ icon={<span className='material-symbols-rounded'>close</span>}
175
+ title={closeLabel}
176
+ aria-label={closeLabel}
177
+ onClick={onClose}
178
+ />
179
+ )}
180
+ </div>
181
+ )}
182
+ </div>
183
+
184
+ <div className='dock-panel__body'>
185
+ {children}
186
+ </div>
187
+
188
+ {footer != null && (
189
+ <div className='dock-panel__footer'>
190
+ {footer}
191
+ </div>
192
+ )}
193
+ </div>
194
+ )
195
+ }