@hienlh/ppm 0.19.1 → 0.19.3

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 (681) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +3 -2
  6. package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
  7. package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
  8. package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
  9. package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
  10. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BuHNQIbQ.js} +1 -1
  11. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-DHRWoLc-.js} +1 -1
  12. package/dist/web/assets/api-client-BogqsFna.js +1 -0
  13. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-oY1tC3tM.js} +1 -1
  14. package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
  15. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-Cknp3Q-P.js} +1 -1
  16. package/dist/web/assets/{apl-do17bnVx.js → apl-C1VSWVei.js} +1 -1
  17. package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
  18. package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
  19. package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
  20. package/dist/web/assets/{astro-ClY0e0OA.js → astro-RX-rChsZ.js} +1 -1
  21. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-CCd1jdje.js} +1 -1
  22. package/dist/web/assets/{blade-BXSdXDGq.js → blade-CZ6bxMbK.js} +1 -1
  23. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  24. package/dist/web/assets/{pencil-CHKLOgLs.js → brain-KDa9jFRq.js} +1 -1
  25. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-BhgCXLWk.js} +1 -1
  26. package/dist/web/assets/{button-CrwgGd1y.js → button-BZtEJcUv.js} +1 -1
  27. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-BrwygPkn.js} +1 -1
  28. package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
  29. package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
  30. package/dist/web/assets/check-DN75Ucvz.js +1 -0
  31. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  32. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  33. package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
  34. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  35. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  36. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  37. package/dist/web/assets/{cobol-phPOE89d.js → cobol-C5TGW1Ir.js} +1 -1
  38. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  39. package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
  40. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-CFgKlmgI.js} +1 -1
  41. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-BoRPwL3L.js} +1 -1
  42. package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
  43. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  44. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-BhG5q3-d.js} +1 -1
  45. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  46. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-DvuI7_tY.js} +1 -1
  47. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BpoJqZQi.js} +1 -1
  48. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
  49. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-CdAg-4W1.js} +1 -1
  50. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  51. package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
  52. package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
  53. package/dist/web/assets/{dist-C2_dKAa_.js → dist-DonmFWG8.js} +1 -1
  54. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-CWIDVNZn.js} +1 -1
  55. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  56. package/dist/web/assets/{edge-Db4ze6bw.js → edge-BSIEsmlI.js} +1 -1
  57. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-DeHb85DN.js} +1 -1
  58. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-BF-lrGXM.js} +1 -1
  59. package/dist/web/assets/{elm-DNZcQt_7.js → elm-8mCsJOeN.js} +1 -1
  60. package/dist/web/assets/{erb-DRONsZXs.js → erb-Dw5PUVKa.js} +1 -1
  61. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-BvjBIA6O.js} +1 -1
  62. package/dist/web/assets/{esm-CZy-sSoK.js → esm-Bt9OwSnU.js} +1 -1
  63. package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
  64. package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
  65. package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
  66. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-Diz2OzVq.js} +1 -1
  67. package/dist/web/assets/eye-gWwaF4Lg.js +1 -0
  68. package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
  69. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  70. package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
  71. package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
  72. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-fQRoG_rV.js} +1 -1
  73. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
  74. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-CjUIHIHp.js} +1 -1
  75. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-x_NUQFrZ.js} +1 -1
  76. package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
  77. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-BOpxSbns.js} +1 -1
  78. package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
  79. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-BW4xlK7k.js} +1 -1
  80. package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
  81. package/dist/web/assets/glide-data-grid-pKQuyYFj.js +141 -0
  82. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-CT9jrmFj.js} +1 -1
  83. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-CjQT1lxK.js} +1 -1
  84. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-DsFJvtg5.js} +1 -1
  85. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-Dhlnmkn_.js} +1 -1
  86. package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
  87. package/dist/web/assets/{hack-CTWk0k1m.js → hack-BKHbsJ7u.js} +1 -1
  88. package/dist/web/assets/{haml-DGh27hXc.js → haml-C1Ih25Y2.js} +1 -1
  89. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  90. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-CaFitvmC.js} +1 -1
  91. package/dist/web/assets/{html-DALyDNqU.js → html-BwpKIE6V.js} +1 -1
  92. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-uuTEquZU.js} +1 -1
  93. package/dist/web/assets/{http-DfMo7nNu.js → http-Cv4Pg5cR.js} +1 -1
  94. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-DubkI6TG.js} +1 -1
  95. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-B9OVZFAB.js} +1 -1
  96. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-DRbZ0FA2.js} +1 -1
  97. package/dist/web/assets/index--KWVW-Ga.js +52 -0
  98. package/dist/web/assets/index-CvGx7wBQ.css +2 -0
  99. package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
  100. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-BHb1jyl6.js} +1 -1
  101. package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
  102. package/dist/web/assets/{jison-Dxut9QwA.js → jison-CtpfXDzp.js} +1 -1
  103. package/dist/web/assets/{julia-Cu85tTPo.js → julia-TYjgpazU.js} +1 -1
  104. package/dist/web/assets/{just-CctM4uLa.js → just-D27bzZuW.js} +1 -1
  105. package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
  106. package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
  107. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DiOYrs6C.js} +1 -1
  108. package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
  109. package/dist/web/assets/{latex-kpwS_CC7.js → latex-D_MzopTn.js} +1 -1
  110. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-xrSJ4EZX.js} +1 -1
  111. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-BG_FG0Pe.js} +1 -1
  112. package/dist/web/assets/loader-circle-DPMrnLQ2.js +1 -0
  113. package/dist/web/assets/{lua-C_3gixwM.js → lua-Bl29oC2L.js} +1 -1
  114. package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
  115. package/dist/web/assets/markdown-renderer-DDcIGQEd.js +3 -0
  116. package/dist/web/assets/{marko-B28rvNL-.js → marko-D-HI7dbm.js} +1 -1
  117. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-y444JQTQ.js} +1 -1
  118. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-VzecHk9g.js} +1 -1
  119. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
  120. package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
  121. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-C835ucWH.js} +1 -1
  122. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-C5sKcldC.js} +1 -1
  123. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-bXnEC3wm.js} +1 -1
  124. package/dist/web/assets/{nim-wqcek3WO.js → nim-pevhf4WD.js} +1 -1
  125. package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
  126. package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
  127. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-B7WINIW4.js} +1 -1
  128. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-mhN0qw6Q.js} +1 -1
  129. package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
  130. package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
  131. package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
  132. package/dist/web/assets/{perl-BH54gN9q.js → perl-B1KUtMnW.js} +1 -1
  133. package/dist/web/assets/{php-Br5ZrEZm.js → php-BGmVZBAJ.js} +1 -1
  134. package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
  135. package/dist/web/assets/portal-container-context-a399Hd9F.js +1 -0
  136. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-jaR0FYJ_.js} +3 -3
  137. package/dist/web/assets/power-CriQkeK5.js +1 -0
  138. package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
  139. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-D1r-Gupi.js} +1 -1
  140. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-BuY7rxn_.js} +1 -1
  141. package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
  142. package/dist/web/assets/{pug-8VCubZ3H.js → pug-BfN8CpEH.js} +1 -1
  143. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-D6fRdLZt.js} +1 -1
  144. package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
  145. package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
  146. package/dist/web/assets/{razor-6v042IJW.js → razor-fg-9AScR.js} +1 -1
  147. package/dist/web/assets/{react-DHs6efqI.js → react-X4jUntEf.js} +1 -1
  148. package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
  149. package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
  150. package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
  151. package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
  152. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-BZgx076b.js} +1 -1
  153. package/dist/web/assets/{ruby-BBaOP636.js → ruby-iUkWDcLo.js} +1 -1
  154. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-bKRwuO2z.js} +1 -1
  155. package/dist/web/assets/{sas-D5QEkIH2.js → sas-CLXO0lZ6.js} +1 -1
  156. package/dist/web/assets/{save-Dxq-Zepc.js → save-CipGrG1k.js} +1 -1
  157. package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
  158. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-DQqteGaG.js} +1 -1
  159. package/dist/web/assets/send-Bb0jtcR6.js +1 -0
  160. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-S8LCNX7V.js} +1 -1
  161. package/dist/web/assets/separator-DqGg3f9c.js +1 -0
  162. package/dist/web/assets/settings-body-omv04jbb.js +2 -0
  163. package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
  164. package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
  165. package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
  166. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-CeMTqagR.js} +1 -1
  167. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BAkquZAV.js} +1 -1
  168. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-BsG3LV8i.js} +1 -1
  169. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-7jKWpJBD.js} +1 -1
  170. package/dist/web/assets/{soy-CGGf1G43.js → soy-Bxm9Ez55.js} +1 -1
  171. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  172. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-C1phwdLc.js} +1 -1
  173. package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
  174. package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
  175. package/dist/web/assets/{stata-DVQNLMcR.js → stata-DGNMQdEb.js} +1 -1
  176. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-ByVrV320.js} +1 -1
  177. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-CNWjIMhl.js} +1 -1
  178. package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
  179. package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
  180. package/dist/web/assets/system-monitor-window-content-BOjq53uU.js +1 -0
  181. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-CW_dlYlW.js} +1 -1
  182. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-PZsP5ZwG.js} +1 -1
  183. package/dist/web/assets/{table-BXDyImqk.js → table-CRJsmfeg.js} +1 -1
  184. package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
  185. package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
  186. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-Cf0gQL4I.js} +1 -1
  187. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-HXZkicIc.js} +3 -3
  188. package/dist/web/assets/{tex-BrTByT7i.js → tex-BTIqPw_R.js} +1 -1
  189. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-oiekm5SB.js} +1 -1
  190. package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
  191. package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
  192. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags-B8dN_Yf6.js} +1 -1
  193. package/dist/web/assets/{twig-DW9qc1bf.js → twig-BgemonbJ.js} +1 -1
  194. package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
  195. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Gfi69PtP.js} +1 -1
  196. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  197. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-CYW3FJFE.js} +1 -1
  198. package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
  199. package/dist/web/assets/use-open-team-member-CruUTqzD.js +1 -0
  200. package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
  201. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-rnvuooQZ.js} +1 -1
  202. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-DetaY8OI.js} +1 -1
  203. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  204. package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
  205. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  206. package/dist/web/assets/video-preview-C0uH2228.js +1 -0
  207. package/dist/web/assets/{vue-CJNDL1g-.js → vue-D3D4lbXz.js} +1 -1
  208. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-DYEQFAiK.js} +1 -1
  209. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-D0OPNscN.js} +1 -1
  210. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-BEN0QNSW.js} +1 -1
  211. package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
  212. package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
  213. package/dist/web/assets/x-YDu4m2Iq.js +1 -0
  214. package/dist/web/assets/{xml-DTOm5niz.js → xml-DHaWgpya.js} +1 -1
  215. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-DVRN8JsA.js} +1 -1
  216. package/dist/web/index.html +42 -65
  217. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  218. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  219. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  220. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  221. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  222. package/dist/web/sw.js +1 -1
  223. package/docs/codebase-summary.md +9 -5
  224. package/docs/lessons-learned.md +11 -0
  225. package/package.json +2 -2
  226. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  227. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  228. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  229. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  230. package/scripts/check-ppm-dir-usage.sh +0 -0
  231. package/scripts/install.sh +0 -0
  232. package/scripts/release.sh +0 -0
  233. package/src/providers/claude-agent-sdk.ts +51 -43
  234. package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
  235. package/src/providers/codex-app-server/codex-history.ts +24 -0
  236. package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
  237. package/src/providers/codex-app-server/codex-protocol.ts +24 -0
  238. package/src/providers/codex-app-server/codex-provider.ts +166 -18
  239. package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
  240. package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
  241. package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
  242. package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
  243. package/src/server/routes/chat.ts +95 -12
  244. package/src/server/routes/codex-accounts.ts +9 -5
  245. package/src/server/ws/chat.ts +55 -5
  246. package/src/services/claude-usage-source.ts +40 -0
  247. package/src/services/claude-usage.service.ts +77 -146
  248. package/src/services/codex-account-login.ts +123 -55
  249. package/src/services/codex-account.service.ts +27 -4
  250. package/src/services/db.service.ts +161 -20
  251. package/src/services/edge-forwarder.ts +191 -191
  252. package/src/services/fs-credential-path-guard.ts +35 -8
  253. package/src/services/fs-path-guard.service.ts +1 -0
  254. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  255. package/src/services/named-tunnel/network-ready.ts +63 -0
  256. package/src/services/postgres.service.ts +144 -72
  257. package/src/services/provider-usage/index.ts +63 -0
  258. package/src/services/provider-usage/register-usage-sources.ts +18 -0
  259. package/src/services/provider-usage/usage-memory-cache.ts +71 -0
  260. package/src/services/provider-usage/usage-registry.ts +157 -0
  261. package/src/services/provider-usage/usage-scheduler.ts +117 -0
  262. package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
  263. package/src/services/provider-usage/usage-source.ts +82 -0
  264. package/src/services/slash-discovery/builtin-commands.ts +13 -0
  265. package/src/services/slash-discovery/codex-skill-items.ts +57 -0
  266. package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
  267. package/src/services/slash-discovery/types.ts +19 -0
  268. package/src/services/supervisor.ts +98 -0
  269. package/src/services/transcript-images.ts +14 -7
  270. package/src/services/upgrade.service.ts +7 -2
  271. package/src/services/windows-process-tree.ts +351 -351
  272. package/src/shared/image-limits.ts +37 -0
  273. package/src/types/api.ts +3 -1
  274. package/src/types/chat.ts +13 -1
  275. package/src/types/extension-messages.ts +2 -0
  276. package/src/web/app.tsx +1 -7
  277. package/src/web/components/chat/attachment-chips.tsx +10 -0
  278. package/src/web/components/chat/chat-history-bar.tsx +10 -1
  279. package/src/web/components/chat/chat-tab.tsx +54 -14
  280. package/src/web/components/chat/message-input.tsx +113 -15
  281. package/src/web/components/chat/message-list.tsx +14 -0
  282. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  283. package/src/web/components/chat/slash-command-picker.tsx +22 -3
  284. package/src/web/components/chat/tool-cards.tsx +47 -4
  285. package/src/web/components/chat/usage-badge.tsx +43 -447
  286. package/src/web/components/chat/user-message-parse.ts +33 -11
  287. package/src/web/components/database/database-viewer.tsx +34 -11
  288. package/src/web/components/database/extract-query-table.ts +24 -0
  289. package/src/web/components/database/glide-data-grid.tsx +11 -4
  290. package/src/web/components/database/glide-grid-types.ts +4 -0
  291. package/src/web/components/database/split-sql-statements.ts +116 -0
  292. package/src/web/components/database/sql-completion-provider.ts +1 -26
  293. package/src/web/components/database/sql-query-editor.tsx +2 -1
  294. package/src/web/components/database/use-database.ts +16 -1
  295. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  296. package/src/web/components/editor/code-editor.tsx +30 -56
  297. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  298. package/src/web/components/floating-window/window-store-types.ts +1 -1
  299. package/src/web/components/layout/command-palette.tsx +2 -2
  300. package/src/web/components/layout/mobile-drawer.tsx +5 -4
  301. package/src/web/components/layout/mobile-nav.tsx +5 -1
  302. package/src/web/components/layout/nav-section-rail.tsx +2 -1
  303. package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
  304. package/src/web/components/layout/sidebar.tsx +0 -2
  305. package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
  306. package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
  307. package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
  308. package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
  309. package/src/web/components/settings/accounts/account-card.tsx +127 -0
  310. package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
  311. package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
  312. package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
  313. package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
  314. package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
  315. package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
  316. package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
  317. package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
  318. package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
  319. package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
  320. package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
  321. package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
  322. package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
  323. package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
  324. package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
  325. package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
  326. package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
  327. package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
  328. package/src/web/components/settings/ai-settings-section.tsx +14 -4
  329. package/src/web/components/settings/appearance-settings-section.tsx +125 -0
  330. package/src/web/components/settings/general-settings-section.tsx +87 -0
  331. package/src/web/components/settings/jira-watcher-section.tsx +34 -0
  332. package/src/web/components/settings/notifications-settings-section.tsx +37 -0
  333. package/src/web/components/settings/open-settings.ts +42 -0
  334. package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
  335. package/src/web/components/settings/settings-body.tsx +110 -0
  336. package/src/web/components/settings/settings-categories.ts +102 -0
  337. package/src/web/components/settings/settings-category-list.tsx +56 -0
  338. package/src/web/components/settings/settings-category-rail.tsx +62 -0
  339. package/src/web/components/settings/settings-section-content.tsx +43 -0
  340. package/src/web/components/settings/settings-tab.tsx +15 -329
  341. package/src/web/components/settings/settings-window-content.tsx +24 -0
  342. package/src/web/hooks/use-chat.ts +44 -4
  343. package/src/web/hooks/use-global-keybindings.ts +3 -4
  344. package/src/web/hooks/use-url-sync.ts +30 -0
  345. package/src/web/hooks/use-usage.ts +23 -11
  346. package/src/web/lib/attachment-marker-split.ts +44 -0
  347. package/src/web/lib/image-animation.ts +51 -0
  348. package/src/web/lib/image-resize-limits.ts +47 -0
  349. package/src/web/lib/image-resize.ts +133 -0
  350. package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
  351. package/src/web/lib/slash-items-cache.ts +27 -7
  352. package/src/web/stores/panel-utils.ts +11 -0
  353. package/src/web/stores/settings-store.ts +4 -2
  354. package/src/web/stores/window-panel-actions.ts +1 -3
  355. package/src/web/stores/window-panel-persistence.ts +9 -4
  356. package/dist/web/assets/adaptive-context-menu-JfuUSqbe.js +0 -1
  357. package/dist/web/assets/ai-resource-editor-MfdJKVM5.js +0 -1
  358. package/dist/web/assets/ai-settings-section-CJE7jFsj.js +0 -1
  359. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  360. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  361. package/dist/web/assets/api-settings-CPg4yGvS.js +0 -1
  362. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
  363. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  364. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  365. package/dist/web/assets/circle-4g2coECH.js +0 -1
  366. package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
  367. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  368. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  369. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  370. package/dist/web/assets/context-menu-BEAa1qhd.js +0 -1
  371. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  372. package/dist/web/assets/dialog-DrL3WEXd.js +0 -1
  373. package/dist/web/assets/diff-viewer-CT0lmDc9.js +0 -4
  374. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  375. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  376. package/dist/web/assets/extension-store-ClQpnxQr.js +0 -1
  377. package/dist/web/assets/external-link-ojDHcQxQ.js +0 -1
  378. package/dist/web/assets/eye-off-Rq1n2-oe.js +0 -1
  379. package/dist/web/assets/file-store-28e44KP3.js +0 -1
  380. package/dist/web/assets/git-log-panel-D1-YXZ0O.js +0 -1
  381. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
  382. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  383. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  384. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  385. package/dist/web/assets/index-SFFv2-VM.css +0 -2
  386. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
  387. package/dist/web/assets/input-DTmI80l2.js +0 -1
  388. package/dist/web/assets/keybindings-store-Bi0q9x4Q.js +0 -1
  389. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  390. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  391. package/dist/web/assets/lock-BEtt_eTO.js +0 -1
  392. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  393. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  394. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  395. package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +0 -1
  396. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  397. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
  398. package/dist/web/assets/panel-store-Dv28vz3L.js +0 -1
  399. package/dist/web/assets/pdf-preview-DKeIIJTv.js +0 -1
  400. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
  401. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  402. package/dist/web/assets/plus-BxqD2FIZ.js +0 -1
  403. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  404. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
  405. package/dist/web/assets/refresh-cw-BALzw7L3.js +0 -1
  406. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  407. package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +0 -1
  408. package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +0 -1
  409. package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +0 -1
  410. package/dist/web/assets/rotate-ccw-CuvNBM80.js +0 -1
  411. package/dist/web/assets/scroll-area-B5yhPpZo.js +0 -1
  412. package/dist/web/assets/send-Cits8q0l.js +0 -1
  413. package/dist/web/assets/settings-store-Dp4i8zeA.js +0 -2
  414. package/dist/web/assets/settings-tab-BtMvvKAF.js +0 -3
  415. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  416. package/dist/web/assets/shallow-BBJ_qoEF.js +0 -1
  417. package/dist/web/assets/sql-query-editor-DZqnYIB7.js +0 -1
  418. package/dist/web/assets/sqlite-viewer-DXutos0e.js +0 -1
  419. package/dist/web/assets/switch-BPol3BFC.js +0 -1
  420. package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +0 -2
  421. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  422. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  423. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  424. package/dist/web/assets/team-member-window-content-BJ_XFw8-.js +0 -1
  425. package/dist/web/assets/tool-cards-pIu_cmEL.js +0 -8
  426. package/dist/web/assets/trash-2-BjShZ7if.js +0 -1
  427. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
  428. package/dist/web/assets/triangle-alert-qQ6Plydp.js +0 -1
  429. package/dist/web/assets/upload-7Zs6oaK5.js +0 -1
  430. package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +0 -1
  431. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  432. package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +0 -1
  433. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  434. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  435. package/dist/web/assets/window-store-CNzkeUyO.js +0 -1
  436. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  437. package/src/web/components/chat/account-dialogs.tsx +0 -384
  438. package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
  439. package/src/web/components/settings/codex-accounts-section.tsx +0 -198
  440. package/src/web/components/settings/telegram-settings-section.tsx +0 -113
  441. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-h3QyH8MW.js} +0 -0
  442. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-mkxkq-FN.js} +0 -0
  443. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-DhISK_EH.js} +0 -0
  444. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DnlWWesX.js} +0 -0
  445. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-P5AK3sfA.js} +0 -0
  446. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CJUPehg2.js} +0 -0
  447. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-Dx6GGbay.js} +0 -0
  448. /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
  449. /package/dist/web/assets/{ara-iGt2zixk.js → ara-95Kx3qKb.js} +0 -0
  450. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-Dp0RbIej.js} +0 -0
  451. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-C82coUjU.js} +0 -0
  452. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BFc3zeop.js} +0 -0
  453. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-ChjC0icr.js} +0 -0
  454. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-DYfUf_PB.js} +0 -0
  455. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-Bh6O0plO.js} +0 -0
  456. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-DlNhHTGq.js} +0 -0
  457. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-Ceoll3Sk.js} +0 -0
  458. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-C7QS6Lfq.js} +0 -0
  459. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-5NMEAZgs.js} +0 -0
  460. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBL83Vbk.js} +0 -0
  461. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-L7rPkPSG.js} +0 -0
  462. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-BOwHIINh.js} +0 -0
  463. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-CWLsMom2.js} +0 -0
  464. /package/dist/web/assets/{c-AjOLBXIs.js → c-CD4Ku5Cr.js} +0 -0
  465. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-CpMl-w3G.js} +0 -0
  466. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-CvgF6ehN.js} +0 -0
  467. /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
  468. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
  469. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-DoiMCU_7.js} +0 -0
  470. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
  471. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
  472. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-DXYM8-SA.js} +0 -0
  473. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-DzamsL69.js} +0 -0
  474. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-CR6RP_T_.js} +0 -0
  475. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-D1s6-IyY.js} +0 -0
  476. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-DAqNWMkS.js} +0 -0
  477. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-8e-KtitB.js} +0 -0
  478. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-UEq0WUcO.js} +0 -0
  479. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-wxJ0TuCm.js} +0 -0
  480. /package/dist/web/assets/{css-Cr27CfPY.js → css-Cerub-yB.js} +0 -0
  481. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-Bh9_WD_h.js} +0 -0
  482. /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
  483. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-rzM1ztqU.js} +0 -0
  484. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-DGaTQ8m0.js} +0 -0
  485. /package/dist/web/assets/{d-CgG1_yzd.js → d-DbkZGuhB.js} +0 -0
  486. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-BO2bzZcA.js} +0 -0
  487. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-BDhCoqVg.js} +0 -0
  488. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-Bh03stWh.js} +0 -0
  489. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-BOdr_fwq.js} +0 -0
  490. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Bb_gTsS2.js} +0 -0
  491. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-BUMDu0av.js} +0 -0
  492. /package/dist/web/assets/{docker-COBitpld.js → docker-BDsTF5ce.js} +0 -0
  493. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-CbCWp9Ti.js} +0 -0
  494. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-Cd1fxvoX.js} +0 -0
  495. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-B7eXkfXG.js} +0 -0
  496. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-XlzuS21R.js} +0 -0
  497. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-CXV1Ki_7.js} +0 -0
  498. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-DuqR60Dg.js} +0 -0
  499. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-q4BPnenD.js} +0 -0
  500. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-B-e0TX2Q.js} +0 -0
  501. /package/dist/web/assets/{fish-B04oqgKk.js → fish-Bzb6-TtF.js} +0 -0
  502. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-B-szH2-f.js} +0 -0
  503. /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
  504. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-OS6SGYC0.js} +0 -0
  505. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-FYq4OP2d.js} +0 -0
  506. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-BpG6P8rw.js} +0 -0
  507. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-BS9RLAna.js} +0 -0
  508. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-B_9FdtGs.js} +0 -0
  509. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-cfaIlnBV.js} +0 -0
  510. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-B2Wv65He.js} +0 -0
  511. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-CtM9X76s.js} +0 -0
  512. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
  513. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-DWEI10iG.js} +0 -0
  514. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-DcJkK1YN.js} +0 -0
  515. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
  516. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-DVpiM6b2.js} +0 -0
  517. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-B80DNc0i.js} +0 -0
  518. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-D9i3MaT-.js} +0 -0
  519. /package/dist/web/assets/{go-DXvjjq4q.js → go-CwCBf_Pt.js} +0 -0
  520. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-D7mEfwmW.js} +0 -0
  521. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
  522. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
  523. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
  524. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
  525. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
  526. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
  527. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-CsXSYXRG.js} +0 -0
  528. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-B1uyiI9t.js} +0 -0
  529. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DlYKn5q9.js} +0 -0
  530. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-C_0VMGJJ.js} +0 -0
  531. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-zt2PEzbg.js} +0 -0
  532. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-6wQfyOK0.js} +0 -0
  533. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-CqI2qTim.js} +0 -0
  534. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BljpHDoG.js} +0 -0
  535. /package/dist/web/assets/{hy-BALeMn3i.js → hy-XzuL55dw.js} +0 -0
  536. /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
  537. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CqhhjBrP.js} +0 -0
  538. /package/dist/web/assets/{ini-DG7imsqe.js → ini-CiFDi6cY.js} +0 -0
  539. /package/dist/web/assets/{java-D5GTT474.js → java-OsQgTL5-.js} +0 -0
  540. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-qWAOScxl.js} +0 -0
  541. /package/dist/web/assets/{json-B9FhwAgR.js → json-RiAKsaD1.js} +0 -0
  542. /package/dist/web/assets/{json5--Abk7MUy.js → json5-BeCnS3H2.js} +0 -0
  543. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-CbTW1Mka.js} +0 -0
  544. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-BLSi_669.js} +0 -0
  545. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-C3xAggou.js} +0 -0
  546. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-CLdiagC9.js} +0 -0
  547. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-lYE1lb50.js} +0 -0
  548. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
  549. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
  550. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
  551. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-BfDV0825.js} +0 -0
  552. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-DONxR4wD.js} +0 -0
  553. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-BDP63Fh5.js} +0 -0
  554. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-BeBrWApL.js} +0 -0
  555. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-DOZc0N1A.js} +0 -0
  556. /package/dist/web/assets/{less-Bk_UaqsM.js → less-C3H9NlDv.js} +0 -0
  557. /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
  558. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-C_kTL608.js} +0 -0
  559. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-Dsp27U3P.js} +0 -0
  560. /package/dist/web/assets/{log-CKm0CxB1.js → log-BywQaTCy.js} +0 -0
  561. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-BirJgciO.js} +0 -0
  562. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-Btrz5kJB.js} +0 -0
  563. /package/dist/web/assets/{make-CpNSwgwT.js → make-Kl7nFQrS.js} +0 -0
  564. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-qsC4P8S-.js} +0 -0
  565. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-CC__ui3p.js} +0 -0
  566. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-D4FxG2FZ.js} +0 -0
  567. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-zGqiWcxn.js} +0 -0
  568. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CZIdTTB1.js} +0 -0
  569. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-CEIs0GcK.js} +0 -0
  570. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-Cf0aUjN9.js} +0 -0
  571. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-CIsXjH86.js} +0 -0
  572. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-B06WO9Qc.js} +0 -0
  573. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-z8fyp0FT.js} +0 -0
  574. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-Dr-qesbW.js} +0 -0
  575. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-CWHzYLcE.js} +0 -0
  576. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D9zXrKmv.js} +0 -0
  577. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-4b-AKOpX.js} +0 -0
  578. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-Dl4dhOZJ.js} +0 -0
  579. /package/dist/web/assets/{move-DItn61bH.js → move-DTYKJmXy.js} +0 -0
  580. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-DnvXOS-o.js} +0 -0
  581. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-Di8QnEXg.js} +0 -0
  582. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-Czh0yq-H.js} +0 -0
  583. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-D_OpZqOR.js} +0 -0
  584. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-2MvcjpK3.js} +0 -0
  585. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-CG_r4cyk.js} +0 -0
  586. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-DINskMcU.js} +0 -0
  587. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-Dv7BIicR.js} +0 -0
  588. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-CpqsCxPb.js} +0 -0
  589. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-DwTsYSmW.js} +0 -0
  590. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-G-4O4k4t.js} +0 -0
  591. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-13Pv5SjI.js} +0 -0
  592. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-Bi2e86mS.js} +0 -0
  593. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-D0WsOTij.js} +0 -0
  594. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-DmDStuXr.js} +0 -0
  595. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-Bl6Qjtuo.js} +0 -0
  596. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-UeSVSjTI.js} +0 -0
  597. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-DreFC689.js} +0 -0
  598. /package/dist/web/assets/{po-_kXpd_0n.js → po-ZIjccxsX.js} +0 -0
  599. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CvQHJPrI.js} +0 -0
  600. /package/dist/web/assets/{polar-DV_1L04z.js → polar-BrMB0Kgh.js} +0 -0
  601. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-pgUsil1T.js} +0 -0
  602. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CTsZkCg_.js} +0 -0
  603. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CcwVuBAp.js} +0 -0
  604. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-spjnAI5W.js} +0 -0
  605. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-ChCkZpDO.js} +0 -0
  606. /package/dist/web/assets/{proto-B5meVsrA.js → proto-wVGqK4gB.js} +0 -0
  607. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-CED7iwPQ.js} +0 -0
  608. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-u9lGiQKV.js} +0 -0
  609. /package/dist/web/assets/{python-DeTIYd5V.js → python-DeqTx6ef.js} +0 -0
  610. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-BH1uoPrX.js} +0 -0
  611. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-CUVKA_13.js} +0 -0
  612. /package/dist/web/assets/{r-DHwHK0Jh.js → r-BqdPzqYH.js} +0 -0
  613. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-C2eTgPN3.js} +0 -0
  614. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-uAay1sWx.js} +0 -0
  615. /package/dist/web/assets/{red-BFuHRZFS.js → red-DTZP1ZfC.js} +0 -0
  616. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-Cdk3YsxY.js} +0 -0
  617. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-Bpa2aS7K.js} +0 -0
  618. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-bNsb1-3L.js} +0 -0
  619. /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
  620. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-DSaubByW.js} +0 -0
  621. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-DbQzYM6i.js} +0 -0
  622. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-C7RsprTu.js} +0 -0
  623. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-VP976YzL.js} +0 -0
  624. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-XHZNhOqd.js} +0 -0
  625. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-BVQlx0Lj.js} +0 -0
  626. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-VHaQ3rh3.js} +0 -0
  627. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-CO-kMkwD.js} +0 -0
  628. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-cY0GXkiz.js} +0 -0
  629. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-D4LNZE0X.js} +0 -0
  630. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-Bh2Fd2cn.js} +0 -0
  631. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-KKclmCnt.js} +0 -0
  632. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-D6YQ-fqf.js} +0 -0
  633. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-CDf66CDj.js} +0 -0
  634. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-D2OH_QIl.js} +0 -0
  635. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-CQeF8G1K.js} +0 -0
  636. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-LTPf1iLZ.js} +0 -0
  637. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-Ose1n88k.js} +0 -0
  638. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-lFuOMHki.js} +0 -0
  639. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Bov-sB5S.js} +0 -0
  640. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-BKyZ6wmk.js} +0 -0
  641. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-CqJoMGMb.js} +0 -0
  642. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Bf5pWRLI.js} +0 -0
  643. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-D5d1hn_u.js} +0 -0
  644. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-B4c6YAgO.js} +0 -0
  645. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-D7fXyDsI.js} +0 -0
  646. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-oNagzlBK.js} +0 -0
  647. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-C6Nqm9Dm.js} +0 -0
  648. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-CaWy-cBX.js} +0 -0
  649. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-C435P5Mq.js} +0 -0
  650. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CVDxqdPf.js} +0 -0
  651. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-Uhs17tQ3.js} +0 -0
  652. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-j6fPOJ6P.js} +0 -0
  653. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-h78O6xrL.js} +0 -0
  654. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-Bol-vCrQ.js} +0 -0
  655. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Dw8eFf04.js} +0 -0
  656. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-Dtz7DPci.js} +0 -0
  657. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-BIOQ7dMP.js} +0 -0
  658. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-CoaGMjzu.js} +0 -0
  659. /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
  660. /package/dist/web/assets/{v-Bf2hKAMu.js → v-BoqFdpJ7.js} +0 -0
  661. /package/dist/web/assets/{vala-Cbete-UE.js → vala-COBpNnCC.js} +0 -0
  662. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-DxI86znh.js} +0 -0
  663. /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
  664. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-DcTAamuQ.js} +0 -0
  665. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-DO298JSv.js} +0 -0
  666. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-Dq-bDVSN.js} +0 -0
  667. /package/dist/web/assets/{viml-BePrIAs8.js → viml-zHTRWuQh.js} +0 -0
  668. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-jlYMJCBf.js} +0 -0
  669. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-DS0sUlSZ.js} +0 -0
  670. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-HSeZ2YhG.js} +0 -0
  671. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-BQfKVO3M.js} +0 -0
  672. /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
  673. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-B6mCuI8h.js} +0 -0
  674. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-CfpS05sP.js} +0 -0
  675. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-14GDsWRD.js} +0 -0
  676. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-Ccbq5EVX.js} +0 -0
  677. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-C19hb3R5.js} +0 -0
  678. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-B-ZSwGwP.js} +0 -0
  679. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-mw_JUeio.js} +0 -0
  680. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-WbzDRjvF.js} +0 -0
  681. /package/dist/web/assets/{zig-Cr80Qrru.js → zig-F0vvImTJ.js} +0 -0
@@ -82,24 +82,46 @@ export function parseIdeOpenedFile(text: string): { idePath: string | null; clea
82
82
  return { idePath, cleanText };
83
83
  }
84
84
 
85
- /** Parse user message content, extracting attached file paths and the actual text */
85
+ /**
86
+ * Parse user message content, extracting attached file paths and the actual text.
87
+ *
88
+ * Two markers mean the same thing to the reader and different things to the model. An image
89
+ * sent inline says so — the payload is already in the message, and a bare path would invite
90
+ * the model to spend a round trip opening what it was handed. Either way the path is what the
91
+ * chat draws a thumbnail from, so both forms have to be recognised here.
92
+ */
86
93
  export function parseUserAttachments(content: string): { files: string[]; text: string } {
87
- // Match: [Attached file: /path] or [Attached files:\n/path1\n/path2\n]
88
- // Trailing newlines are optional an attachment-only message has the marker
89
- // trimmed to the end of the string (extractTerminalBlocks trims), so the
90
- // separator newlines may be absent.
91
- const singleMatch = content.match(/^\[Attached file: (.+?)\]\n*/);
94
+ // Trailing newlines are optional an attachment-only message has the marker trimmed to the
95
+ // end of the string (extractTerminalBlocks trims), so the separator newlines may be absent.
96
+ const files: string[] = [];
97
+ let rest = content;
98
+ for (;;) {
99
+ const inline = rest.match(/^\[Attached image \(contents included in this message\): (.+?)\]\n*/);
100
+ if (inline) {
101
+ files.push(inline[1]!);
102
+ rest = rest.slice(inline[0].length);
103
+ continue;
104
+ }
105
+ break;
106
+ }
107
+ if (files.length > 0) {
108
+ // A message may pair inline images with plain file attachments.
109
+ const tail = parseUserAttachments(rest);
110
+ return { files: [...files, ...tail.files], text: tail.text };
111
+ }
112
+
113
+ const singleMatch = rest.match(/^\[Attached file: (.+?)\]\n*/);
92
114
  if (singleMatch) {
93
- return { files: [singleMatch[1]!], text: content.slice(singleMatch[0].length) };
115
+ return { files: [singleMatch[1]!], text: rest.slice(singleMatch[0].length) };
94
116
  }
95
117
 
96
- const multiMatch = content.match(/^\[Attached files:\n([\s\S]+?)\]\n*/);
118
+ const multiMatch = rest.match(/^\[Attached files:\n([\s\S]+?)\]\n*/);
97
119
  if (multiMatch) {
98
- const files = multiMatch[1]!.split("\n").map((l) => l.trim()).filter(Boolean);
99
- return { files, text: content.slice(multiMatch[0].length) };
120
+ const list = multiMatch[1]!.split("\n").map((l) => l.trim()).filter(Boolean);
121
+ return { files: list, text: rest.slice(multiMatch[0].length) };
100
122
  }
101
123
 
102
- return { files: [], text: content };
124
+ return { files: [], text: rest };
103
125
  }
104
126
 
105
127
  /** Extract leading terminal code fences from message text */
@@ -7,6 +7,7 @@ import { useConnections } from "./use-connections";
7
7
  import { SqlQueryEditor } from "./sql-query-editor";
8
8
  import { ExportButton } from "./export-button";
9
9
  import { GlideDataGrid } from "./glide-data-grid";
10
+ import { extractQueryTable } from "./extract-query-table";
10
11
  import type { SchemaInfo } from "./sql-completion-provider";
11
12
 
12
13
  /** Parse WHERE "col" ILIKE '%val%' clauses from SQL */
@@ -140,6 +141,8 @@ export function DatabaseViewer({ metadata, tabId }: Props) {
140
141
 
141
142
  // Track whether user ran a custom query (show results instead of table grid)
142
143
  const [showingQueryResult, setShowingQueryResult] = useState(!!initialSql);
144
+ // SQL behind the currently displayed query result — tells us where to write edits back to
145
+ const [executedSql, setExecutedSql] = useState<string | null>(initialSql ?? null);
143
146
  const handleExecuteQuery = useCallback((sql: string) => {
144
147
  const trimmed = sql.trim();
145
148
  // Check if query is a simple SELECT on the current table — stay in table grid mode
@@ -154,6 +157,7 @@ export function DatabaseViewer({ metadata, tabId }: Props) {
154
157
  }
155
158
  }
156
159
  setShowingQueryResult(true);
160
+ setExecutedSql(trimmed);
157
161
  db.executeQuery(sql);
158
162
  }, [db.executeQuery, db.queryAsTable, db.selectedTable]);
159
163
 
@@ -171,6 +175,18 @@ export function DatabaseViewer({ metadata, tabId }: Props) {
171
175
  db.setPage(p);
172
176
  }, [db.setPage]);
173
177
 
178
+ // Ad-hoc query results are editable only when the result maps to one table and
179
+ // the connection allows writes — otherwise the grid stays read-only so edits
180
+ // can't pile up with nowhere to save them to.
181
+ const queryEditTable = useMemo(
182
+ () => (conn && !conn.readonly && executedSql ? extractQueryTable(executedSql, db.selectedSchema) : null),
183
+ [conn, executedSql, db.selectedSchema],
184
+ );
185
+ const handleQueryCellUpdate = useCallback((pkCol: string, pkVal: unknown, col: string, val: unknown) => {
186
+ if (!queryEditTable) return;
187
+ db.updateCellIn(queryEditTable.table, queryEditTable.schema, pkCol, pkVal, col, val);
188
+ }, [queryEditTable, db.updateCellIn]);
189
+
174
190
  const qr = db.queryResult;
175
191
  const showQueryResults = showingQueryResult && !!(qr || db.queryError);
176
192
  const showTableGrid = db.selectedTable && !showQueryResults;
@@ -246,7 +262,8 @@ export function DatabaseViewer({ metadata, tabId }: Props) {
246
262
  )}
247
263
 
248
264
  {showQueryResults && (
249
- <QueryResultPanel result={qr} error={db.queryError} loading={db.queryLoading} schema={db.schema} connectionName={connectionName} />
265
+ <QueryResultPanel result={qr} error={db.queryError} loading={db.queryLoading} schema={db.schema}
266
+ connectionName={connectionName} onCellUpdate={queryEditTable ? handleQueryCellUpdate : undefined} />
250
267
  )}
251
268
 
252
269
  {showInitialLoading && (
@@ -262,27 +279,32 @@ export function DatabaseViewer({ metadata, tabId }: Props) {
262
279
 
263
280
  const NOOP = () => {};
264
281
 
265
- /** Read-only result panel for ad-hoc query results — uses DataGrid for SELECT to get checkboxes + export */
266
- function QueryResultPanel({ result, error, loading, schema, connectionName }: {
282
+ /** Result panel for ad-hoc query results — uses DataGrid for SELECT to get checkboxes + export.
283
+ * Editable only when the caller supplies onCellUpdate (single-table SELECT on a writable connection). */
284
+ function QueryResultPanel({ result, error, loading, schema, connectionName, onCellUpdate }: {
267
285
  result: { columns: string[]; rows: Record<string, unknown>[]; rowsAffected: number; changeType: "select" | "modify"; executionTimeMs?: number } | null;
268
286
  error: string | null;
269
287
  loading?: boolean;
270
288
  schema?: DbColumnInfo[];
271
289
  connectionName?: string;
290
+ onCellUpdate?: (pkCol: string, pkVal: unknown, col: string, val: unknown) => void;
272
291
  }) {
273
- // Build a read-only DataGrid-compatible tableData from query result
292
+ // Build a DataGrid-compatible tableData from query result
274
293
  const queryTableData = useMemo(() => (
275
294
  result?.changeType === "select" && result.rows.length > 0
276
295
  ? { columns: result.columns, rows: result.rows, total: result.rows.length, limit: result.rows.length }
277
296
  : null
278
297
  ), [result]);
279
298
 
280
- // Use schema if available, otherwise build minimal schema from column names
281
- const querySchema = useMemo(() => (
282
- schema?.length ? schema : (result?.columns ?? []).map((c) => ({
283
- name: c, type: "text", nullable: true, pk: false, defaultValue: null,
284
- }))
285
- ), [schema, result?.columns]);
299
+ // Use the sidebar table's schema only when it describes every result column
300
+ // it belongs to the selected table, which may not be what the query returned.
301
+ // The grid drops columns missing from its schema, so a mismatch hides data.
302
+ const querySchema = useMemo(() => {
303
+ const cols = result?.columns ?? [];
304
+ const names = new Set((schema ?? []).map((c) => c.name));
305
+ if (schema?.length && cols.every((c) => names.has(c))) return schema;
306
+ return cols.map((c) => ({ name: c, type: "text", nullable: true, pk: false, defaultValue: null }));
307
+ }, [schema, result?.columns]);
286
308
 
287
309
  return (
288
310
  <div className="flex flex-col h-full overflow-hidden text-xs">
@@ -301,7 +323,8 @@ function QueryResultPanel({ result, error, loading, schema, connectionName }: {
301
323
  columns={queryTableData.columns} rows={queryTableData.rows}
302
324
  total={queryTableData.total} limit={queryTableData.limit}
303
325
  schema={querySchema} loading={!!loading}
304
- page={1} onPageChange={NOOP} onCellUpdate={NOOP}
326
+ page={1} onPageChange={NOOP}
327
+ onCellUpdate={onCellUpdate ?? NOOP} readOnly={!onCellUpdate}
305
328
  orderBy={null} orderDir="ASC" onToggleSort={NOOP}
306
329
  connectionName={connectionName}
307
330
  />
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The single table an ad-hoc SELECT reads from, or null when its rows can't be
3
+ * mapped back to one table (JOIN, set operation, subquery, aggregate).
4
+ *
5
+ * Editing a query result writes an UPDATE against this table, so anything
6
+ * ambiguous must return null — the grid then stays read-only instead of
7
+ * collecting edits that have nowhere to go.
8
+ */
9
+ export function extractQueryTable(sql: string, defaultSchema: string): { table: string; schema: string } | null {
10
+ const stripped = sql
11
+ .replace(/--[^\r\n]*/g, " ")
12
+ .replace(/\/\*[\s\S]*?\*\//g, " ")
13
+ .trim()
14
+ .replace(/;\s*$/, "");
15
+ if (!/^select\s/i.test(stripped)) return null;
16
+ if (/\b(join|union|intersect|except|group\s+by|distinct)\b/i.test(stripped)) return null;
17
+ // More than one FROM means a subquery or derived table is involved.
18
+ if ((stripped.match(/\bfrom\b/gi) ?? []).length !== 1) return null;
19
+ const m = /\bfrom\s+(?:"([^"]+)"|(\w+))\s*(?:\.\s*(?:"([^"]+)"|(\w+)))?/i.exec(stripped);
20
+ if (!m) return null;
21
+ const first = m[1] ?? m[2]!;
22
+ const second = m[3] ?? m[4];
23
+ return second ? { schema: first, table: second } : { table: first, schema: defaultSchema };
24
+ }
@@ -16,6 +16,9 @@ import { GlideSaveBar } from "./glide-save-bar";
16
16
  import { GlideGridPagination } from "./glide-grid-pagination";
17
17
  import { GlideDataPreviewPanel } from "./glide-data-preview-panel";
18
18
 
19
+ /** Swallows edits (paste, context-menu actions) while the grid is read-only. */
20
+ const NOOP_EDIT = () => {};
21
+
19
22
  const HEADER_ICONS: Record<string, (p: { fgColor: string }) => string> = {
20
23
  sortAsc: (p) => `<svg viewBox="0 0 16 16" fill="${p.fgColor}"><path d="M8 4l4 6H4z"/></svg>`,
21
24
  sortDesc: (p) => `<svg viewBox="0 0 16 16" fill="${p.fgColor}"><path d="M8 12l4-6H4z"/></svg>`,
@@ -31,7 +34,7 @@ export function GlideDataGrid(props: GlideGridProps) {
31
34
  columns: rawColumnNames, rows, total, limit, schema, loading,
32
35
  page, onPageChange, onCellUpdate, onRowDelete, onBulkDelete, onInsertRow,
33
36
  orderBy, orderDir, onToggleSort, onClearSort, columnFilters = {}, onColumnFilter,
34
- connectionId, selectedTable, selectedSchema, connectionName,
37
+ connectionId, selectedTable, selectedSchema, connectionName, readOnly,
35
38
  } = props;
36
39
 
37
40
  const theme = useGlideTheme();
@@ -78,14 +81,18 @@ export function GlideDataGrid(props: GlideGridProps) {
78
81
  }, [pinnedCount, pkCol, searchTerm, displayRows, pinnedPks]);
79
82
 
80
83
  // Hooks
84
+ // Without a primary key there is no way to address a row in an UPDATE, so
85
+ // edits could never be saved — don't let the user make them in the first place.
86
+ const cellsReadOnly = !!readOnly || !pkCol;
87
+
81
88
  const { pendingRef, addEdit, commitAll, discardAll, hasPending, pendingCount, committedRef } = useGlidePendingEdits(pkCol, onCellUpdate, onInsertRow);
82
89
  const { columns, freezeColumns, columnOrder } = useGlideColumns(schema, columnNames, pinnedCols, colWidths, displayRows, orderBy, orderDir);
83
- const { getCellContent, onCellEdited } = useGlideCellContent(displayRows, columnOrder, schema, pkCol, addEdit, pendingRef);
90
+ const { getCellContent, onCellEdited } = useGlideCellContent(displayRows, columnOrder, schema, pkCol, addEdit, pendingRef, cellsReadOnly);
84
91
  const { gridSelection, onGridSelectionChange, selectedRowIndices, clearSelection } = useGlideSelection();
85
92
  const {
86
93
  previewData, closePreview, openRowPreview, openCellPreview, openPreviewInTab,
87
94
  handlePaste, getContextFk, isCellViewable, openFkTable,
88
- } = useGlideGridActions({ displayRows, columnOrder, schema, pkCol, connectionId, connectionName, selectedTable, selectedSchema, addEdit, gridSelection, containerRef });
95
+ } = useGlideGridActions({ displayRows, columnOrder, schema, pkCol, connectionId, connectionName, selectedTable, selectedSchema, addEdit: cellsReadOnly ? NOOP_EDIT : addEdit, gridSelection, containerRef });
89
96
 
90
97
  useEffect(() => {
91
98
  if (committedRef.current) { committedRef.current = false; discardAll(); setInsertedRows([]); }
@@ -166,7 +173,7 @@ export function GlideDataGrid(props: GlideGridProps) {
166
173
  <div className="flex-1 min-h-0">
167
174
  <DataEditor ref={gridRef} columns={columns} rows={displayRows.length}
168
175
  getCellContent={getCellContent} getCellsForSelection={true}
169
- onCellEdited={onCellEdited} onPaste={handlePaste}
176
+ onCellEdited={onCellEdited} onPaste={cellsReadOnly ? false : handlePaste}
170
177
  theme={theme} freezeColumns={freezeColumns} freezeTrailingRows={frozenTrailingRows}
171
178
  rowMarkers={pkCol ? "checkbox-visible" : "number"}
172
179
  gridSelection={gridSelection} onGridSelectionChange={onGridSelectionChange}
@@ -29,6 +29,10 @@ export interface GlideGridProps {
29
29
  onPageChange: (page: number) => void;
30
30
  /** Cell edit: (pkColumn, pkValue, editedColumn, newValue) */
31
31
  onCellUpdate: (pkCol: string, pkVal: unknown, col: string, val: unknown) => void;
32
+ /** Block all cell editing — cells stay viewable/copyable but cannot be changed.
33
+ * Set when there is nowhere to write the edit back to (ad-hoc query results
34
+ * whose source table can't be determined, readonly connections). */
35
+ readOnly?: boolean;
32
36
  onRowDelete?: (pkCol: string, pkVal: unknown) => void;
33
37
  onBulkDelete?: (pkCol: string, pkValues: unknown[]) => void;
34
38
  onInsertRow?: (values: Record<string, unknown>) => Promise<void>;
@@ -0,0 +1,116 @@
1
+ export interface SqlStatement {
2
+ /** Statement text from its first token through its terminating semicolon. */
3
+ sql: string;
4
+ /** 1-based line of the statement's first token (blank/comment lines skipped). */
5
+ startLine: number;
6
+ /** 1-based line the statement ends on. */
7
+ endLine: number;
8
+ }
9
+
10
+ const IDENT_CHAR = /[A-Za-z0-9_]/;
11
+ const DOLLAR_TAG = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/;
12
+
13
+ /**
14
+ * Split SQL into statements on top-level semicolons.
15
+ *
16
+ * A semicolon only terminates a statement when it is not inside a string, a
17
+ * quoted identifier, a comment, or a dollar-quoted block — so a trailing
18
+ * `-- comment` after the semicolon, or a `;` inside a literal, no longer glues
19
+ * two statements together.
20
+ */
21
+ export function splitSqlStatements(text: string): SqlStatement[] {
22
+ const statements: SqlStatement[] = [];
23
+ let line = 1;
24
+ let firstTokenIdx = -1;
25
+ let firstTokenLine = 1;
26
+ let i = 0;
27
+
28
+ const flush = (endIdx: number, endLine: number) => {
29
+ if (firstTokenIdx === -1) return;
30
+ const sql = text.slice(firstTokenIdx, endIdx).trim();
31
+ if (sql) statements.push({ sql, startLine: firstTokenLine, endLine });
32
+ firstTokenIdx = -1;
33
+ };
34
+
35
+ while (i < text.length) {
36
+ const ch = text[i]!;
37
+
38
+ if (ch === "\n") { line++; i++; continue; }
39
+ if (ch === " " || ch === "\t" || ch === "\r") { i++; continue; }
40
+
41
+ // Line comment — runs to end of line, never part of a statement's start
42
+ if (ch === "-" && text[i + 1] === "-") {
43
+ while (i < text.length && text[i] !== "\n") i++;
44
+ continue;
45
+ }
46
+
47
+ // Block comment — Postgres allows these to nest
48
+ if (ch === "/" && text[i + 1] === "*") {
49
+ let depth = 1;
50
+ i += 2;
51
+ while (i < text.length && depth > 0) {
52
+ if (text[i] === "/" && text[i + 1] === "*") { depth++; i += 2; continue; }
53
+ if (text[i] === "*" && text[i + 1] === "/") { depth--; i += 2; continue; }
54
+ if (text[i] === "\n") line++;
55
+ i++;
56
+ }
57
+ continue;
58
+ }
59
+
60
+ if (firstTokenIdx === -1) { firstTokenIdx = i; firstTokenLine = line; }
61
+
62
+ if (ch === ";") { flush(i + 1, line); i++; continue; }
63
+
64
+ // String literal or quoted identifier
65
+ if (ch === "'" || ch === '"') {
66
+ // E'...' honours backslash escapes; plain strings only double the quote
67
+ const prev = text[i - 1];
68
+ const prev2 = text[i - 2];
69
+ const escapes = ch === "'" && (prev === "e" || prev === "E") && !(prev2 !== undefined && IDENT_CHAR.test(prev2));
70
+ i++;
71
+ while (i < text.length) {
72
+ const c = text[i]!;
73
+ if (c === "\n") { line++; i++; continue; }
74
+ if (escapes && c === "\\") { i += 2; continue; }
75
+ if (c === ch) {
76
+ if (text[i + 1] === ch) { i += 2; continue; } // doubled quote = literal
77
+ i++;
78
+ break;
79
+ }
80
+ i++;
81
+ }
82
+ continue;
83
+ }
84
+
85
+ // Dollar-quoted block ($$ … $$ / $tag$ … $tag$) — bodies hold their own semicolons
86
+ if (ch === "$") {
87
+ const m = DOLLAR_TAG.exec(text.slice(i));
88
+ if (m) {
89
+ const tag = m[0];
90
+ const close = text.indexOf(tag, i + tag.length);
91
+ const stop = close === -1 ? text.length : close + tag.length;
92
+ for (let k = i; k < stop; k++) if (text[k] === "\n") line++;
93
+ i = stop;
94
+ continue;
95
+ }
96
+ }
97
+
98
+ i++;
99
+ }
100
+
101
+ flush(text.length, line);
102
+ return statements;
103
+ }
104
+
105
+ /**
106
+ * The statement the cursor sits in. Falls back to the next statement below the
107
+ * cursor (cursor parked on a blank line between statements), then to the last.
108
+ */
109
+ export function getStatementAtCursor(text: string, cursorLine: number): string {
110
+ const statements = splitSqlStatements(text);
111
+ if (statements.length === 0) return "";
112
+ const containing = statements.find((s) => s.startLine <= cursorLine && cursorLine <= s.endLine);
113
+ if (containing) return containing.sql;
114
+ const following = statements.find((s) => s.startLine > cursorLine);
115
+ return (following ?? statements[statements.length - 1]!).sql;
116
+ }
@@ -1,4 +1,5 @@
1
1
  import type * as MonacoType from "monaco-editor";
2
+ import { getStatementAtCursor } from "./split-sql-statements";
2
3
 
3
4
  export interface SchemaInfo {
4
5
  tables: { name: string; schema: string }[];
@@ -22,32 +23,6 @@ export const AGGREGATE_FNS = ["COUNT", "SUM", "AVG", "MIN", "MAX"];
22
23
  export const OPERATORS = ["=", "!=", "<>", ">", "<", ">=", "<=", "LIKE", "ILIKE", "IN", "NOT IN", "BETWEEN", "IS NULL", "IS NOT NULL"];
23
24
  export const SORT_DIRS = ["ASC", "DESC"];
24
25
 
25
- /** Find the SQL statement surrounding the cursor line (split by ;) */
26
- export function getStatementAtCursor(text: string, cursorLine: number): string {
27
- const lines = text.split("\n");
28
- let stmtStart = 0;
29
- for (let i = 0; i < lines.length; i++) {
30
- const trimmed = lines[i]!.trim();
31
- if (i < cursorLine - 1 && trimmed.endsWith(";")) {
32
- stmtStart = i + 1;
33
- }
34
- }
35
- let stmtEnd = lines.length - 1;
36
- for (let i = cursorLine - 1; i < lines.length; i++) {
37
- const trimmed = lines[i]!.trim();
38
- if (trimmed.endsWith(";")) {
39
- stmtEnd = i;
40
- break;
41
- }
42
- }
43
- while (stmtStart <= stmtEnd) {
44
- const t = lines[stmtStart]!.trim();
45
- if (t && !t.startsWith("--")) break;
46
- stmtStart++;
47
- }
48
- return lines.slice(stmtStart, stmtEnd + 1).join("\n").trim();
49
- }
50
-
51
26
  /** Client-side column cache to avoid redundant fetches */
52
27
  const columnCache = new Map<string, { name: string; type: string }[]>();
53
28
 
@@ -2,7 +2,8 @@ import { useState, useCallback, useRef, useEffect } from "react";
2
2
  import Editor, { type OnMount } from "@monaco-editor/react";
3
3
  import type * as MonacoType from "monaco-editor";
4
4
  import { useMonacoTheme } from "@/lib/use-monaco-theme";
5
- import { createSqlCompletionProvider, clearCompletionCache, getStatementAtCursor, type SchemaInfo } from "./sql-completion-provider";
5
+ import { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from "./sql-completion-provider";
6
+ import { getStatementAtCursor } from "./split-sql-statements";
6
7
 
7
8
  interface SqlQueryEditorProps {
8
9
  onExecute: (sql: string) => void;
@@ -232,12 +232,27 @@ export function useDatabase(connectionId: number) {
232
232
  fetchTableData();
233
233
  }, [executeQuery, queryAsTable, fetchTableData]);
234
234
 
235
+ /** Update a cell in an explicitly named table, then reload the current view.
236
+ * Used by ad-hoc query results, where the target table comes from the SQL
237
+ * rather than the sidebar selection. */
238
+ const updateCellIn = useCallback(async (
239
+ table: string, tableSchema: string,
240
+ pkColumn: string, pkValue: unknown, column: string, value: unknown,
241
+ ) => {
242
+ try {
243
+ await api.put(`${base}/cell`, { table, schema: tableSchema, pkColumn, pkValue, column, value });
244
+ reload();
245
+ } catch (e) {
246
+ failMutation("Update cell", e);
247
+ }
248
+ }, [base, reload, failMutation]);
249
+
235
250
  return {
236
251
  selectedTable, selectedSchema, selectTable, tableData, schema,
237
252
  loading, error, page, setPage: changePage,
238
253
  orderBy, orderDir, toggleSort, clearSort,
239
254
  queryResult, queryError, queryLoading, executeQuery,
240
- updateCell, deleteRow, bulkDelete, insertRow,
255
+ updateCell, updateCellIn, deleteRow, bulkDelete, insertRow,
241
256
  refreshData: fetchTableData, queryAsTable, reload,
242
257
  };
243
258
  }
@@ -49,7 +49,12 @@ export function useGlideCellContent(
49
49
  pkCol: string | null,
50
50
  addPendingEdit: (pkVal: unknown, col: string, newVal: unknown) => void,
51
51
  pendingRef: React.RefObject<Map<string, PendingEdit>>,
52
+ readOnly = false,
52
53
  ): UseGlideCellContentResult {
54
+ // Read through a ref: getCellContent is stable (canvas render loop) and must
55
+ // still see the current mode without being re-created.
56
+ const readOnlyRef = useRef(readOnly);
57
+ readOnlyRef.current = readOnly;
53
58
  const rowsRef = useRef(rows);
54
59
  rowsRef.current = rows;
55
60
  const colOrderRef = useRef(columnOrder);
@@ -106,7 +111,7 @@ export function useGlideCellContent(
106
111
  }
107
112
  return {
108
113
  kind: GridCellKind.Text, data: "", displayData: placeholder,
109
- allowOverlay: !isPk, readonly: isPk,
114
+ allowOverlay: !isPk, readonly: isPk || readOnlyRef.current,
110
115
  themeOverride: hasPending ? PENDING_THEME : (isNewRow || placeholder !== "NULL" ? { textDark: "#9ca3af" } : { textDark: "#6b7280" }),
111
116
  };
112
117
  }
@@ -115,27 +120,27 @@ export function useGlideCellContent(
115
120
  if (kind === GridCellKind.Number && typeof val === "number") {
116
121
  return {
117
122
  kind: GridCellKind.Number, data: val, displayData: String(val),
118
- allowOverlay: !isPk, readonly: isPk,
123
+ allowOverlay: !isPk, readonly: isPk || readOnlyRef.current,
119
124
  themeOverride: hasPending ? PENDING_THEME : undefined,
120
125
  };
121
126
  }
122
127
 
123
128
  // Boolean cells
124
129
  if (kind === GridCellKind.Boolean && typeof val === "boolean") {
125
- return { kind: GridCellKind.Boolean, data: val, readonly: isPk, allowOverlay: false };
130
+ return { kind: GridCellKind.Boolean, data: val, readonly: isPk || readOnlyRef.current, allowOverlay: false };
126
131
  }
127
132
 
128
133
  // Text cell
129
134
  const strVal = formatCellValue(val);
130
135
  return {
131
136
  kind: GridCellKind.Text, data: strVal, displayData: truncateDisplay(strVal),
132
- allowOverlay: !isPk, readonly: isPk,
137
+ allowOverlay: !isPk, readonly: isPk || readOnlyRef.current,
133
138
  themeOverride: hasPending ? PENDING_THEME : undefined,
134
139
  };
135
140
  }, []); // stable — reads from refs
136
141
 
137
142
  const onCellEdited = useCallback(([colIdx, rowIdx]: Item, newValue: EditableGridCell) => {
138
- if (!pkCol) return;
143
+ if (!pkCol || readOnly) return;
139
144
  const colName = colOrderRef.current[colIdx];
140
145
  const row = rowsRef.current[rowIdx];
141
146
  if (!colName || !row) return;
@@ -153,7 +158,7 @@ export function useGlideCellContent(
153
158
  }
154
159
 
155
160
  addPendingEdit(pkVal, colName, parsed);
156
- }, [pkCol, addPendingEdit]);
161
+ }, [pkCol, readOnly, addPendingEdit]);
157
162
 
158
163
  return { getCellContent, onCellEdited };
159
164
  }
@@ -14,7 +14,8 @@ import { EditorToolbar } from "./editor-toolbar";
14
14
  import { EditorLanguagePicker } from "./editor-language-picker";
15
15
  import { SaveAsDialog } from "./save-as-dialog";
16
16
  import { EditorMobileToolbar } from "./editor-mobile-toolbar";
17
- import { createSqlCompletionProvider, clearCompletionCache, getStatementAtCursor, type SchemaInfo } from "../database/sql-completion-provider";
17
+ import { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from "../database/sql-completion-provider";
18
+ import { getStatementAtCursor, splitSqlStatements } from "../database/split-sql-statements";
18
19
  import { useConnections, type Connection } from "../database/use-connections";
19
20
  import { GlideDataGrid } from "../database/glide-data-grid";
20
21
  import type { GridColumnSchema } from "../database/glide-grid-types";
@@ -509,76 +510,49 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
509
510
  if (model !== thisModel) return { lenses: [], dispose: () => {} };
510
511
 
511
512
  const lenses: MonacoType.languages.CodeLens[] = [];
512
- const text = model.getValue();
513
- const lines = text.split("\n");
514
- let stmtStartLine = -1;
515
- let stmtLines: string[] = [];
516
- let dollarBlock = false; // Track DO $$ ... $$ blocks
517
-
518
- // Transaction block tracking: group BEGIN...COMMIT into single Run Transaction
519
- const txPattern = /^(BEGIN|COMMIT|ROLLBACK|END)(;|\s|$)/i;
520
- let txBlockStartLine = -1;
521
- let txBlockStmts: string[] = [];
522
513
 
523
514
  const addLens = (line: number, stmt: string, title = "\u25B7 Run") => {
524
515
  const trimmed = stmt.trim();
525
- if (!trimmed || trimmed.startsWith("--")) return;
516
+ if (!trimmed) return;
526
517
  lenses.push({
527
518
  range: { startLineNumber: line, startColumn: 1, endLineNumber: line, endColumn: 1 },
528
519
  command: { id: cmdId, title, arguments: [trimmed] },
529
520
  });
530
521
  };
531
522
 
532
- for (let i = 0; i < lines.length; i++) {
533
- const trimmed = lines[i]!.trim();
534
- if (stmtStartLine === -1) {
535
- if (!trimmed || trimmed.startsWith("--")) continue;
536
- stmtStartLine = i + 1;
537
- stmtLines = [];
538
- }
539
- stmtLines.push(lines[i]!);
540
-
541
- // Detect $$ dollar-quoted block start/end
542
- const dollarMatches = (trimmed.match(/\$\$/g) || []).length;
543
- if (dollarMatches % 2 === 1) dollarBlock = !dollarBlock;
544
-
545
- // Only split on ; when NOT inside a $$ block
546
- if (!dollarBlock && trimmed.endsWith(";")) {
547
- const stmt = stmtLines.join("\n").trim();
548
- const isTxStart = /^BEGIN(;|\s|$)/i.test(stmt);
549
- const isTxEnd = /^(COMMIT|ROLLBACK|END)(;|\s|$)/i.test(stmt);
550
-
551
- if (txBlockStartLine === -1 && isTxStart) {
552
- // Start collecting transaction block
553
- txBlockStartLine = stmtStartLine;
554
- txBlockStmts = [stmt];
555
- } else if (txBlockStartLine > -1) {
556
- txBlockStmts.push(stmt);
557
- // Individual Run for non-tx-control statements inside block
558
- if (!isTxEnd && !txPattern.test(stmt)) {
559
- addLens(stmtStartLine, stmt);
560
- }
561
- if (isTxEnd) {
562
- // Complete block — add Run Transaction at BEGIN line
563
- addLens(txBlockStartLine, txBlockStmts.join("\n"), "\u25B7 Run Transaction");
564
- txBlockStartLine = -1;
565
- txBlockStmts = [];
566
- }
567
- } else {
568
- addLens(stmtStartLine, stmt);
569
- }
523
+ // Transaction block tracking: group BEGIN...COMMIT into single Run Transaction
524
+ const txPattern = /^(BEGIN|COMMIT|ROLLBACK|END)(;|\s|$)/i;
525
+ let txBlockStartLine = -1;
526
+ let txBlockStmts: string[] = [];
570
527
 
571
- stmtStartLine = -1;
572
- stmtLines = [];
528
+ for (const { sql, startLine } of splitSqlStatements(model.getValue())) {
529
+ const isTxStart = /^BEGIN(;|\s|$)/i.test(sql);
530
+ const isTxEnd = /^(COMMIT|ROLLBACK|END)(;|\s|$)/i.test(sql);
531
+
532
+ if (txBlockStartLine === -1 && isTxStart) {
533
+ // Start collecting transaction block
534
+ txBlockStartLine = startLine;
535
+ txBlockStmts = [sql];
536
+ } else if (txBlockStartLine > -1) {
537
+ txBlockStmts.push(sql);
538
+ // Individual Run for non-tx-control statements inside block
539
+ if (!isTxEnd && !txPattern.test(sql)) {
540
+ addLens(startLine, sql);
541
+ }
542
+ if (isTxEnd) {
543
+ // Complete block — add Run Transaction at BEGIN line
544
+ addLens(txBlockStartLine, txBlockStmts.join("\n"), "\u25B7 Run Transaction");
545
+ txBlockStartLine = -1;
546
+ txBlockStmts = [];
547
+ }
548
+ } else {
549
+ addLens(startLine, sql);
573
550
  }
574
551
  }
575
552
  // Unclosed transaction block — still offer Run Transaction
576
553
  if (txBlockStartLine > -1 && txBlockStmts.length > 1) {
577
554
  addLens(txBlockStartLine, txBlockStmts.join("\n"), "\u25B7 Run Transaction");
578
555
  }
579
- if (stmtStartLine > 0 && stmtLines.join("").trim()) {
580
- addLens(stmtStartLine, stmtLines.join("\n"));
581
- }
582
556
  return { lenses, dispose: () => {} };
583
557
  },
584
558
  });
@@ -878,7 +852,7 @@ function SqlResultPanel({ result, error, loading, connName, onClose, onOpenInTab
878
852
  <GlideDataGrid
879
853
  columns={tableData.columns} rows={tableData.rows} total={tableData.total} limit={tableData.limit}
880
854
  schema={querySchema} loading={false}
881
- page={1} onPageChange={NOOP} onCellUpdate={NOOP}
855
+ page={1} onPageChange={NOOP} onCellUpdate={NOOP} readOnly
882
856
  orderBy={null} orderDir="ASC" onToggleSort={NOOP}
883
857
  connectionName={connName}
884
858
  />
@@ -23,6 +23,7 @@ export const WINDOW_CONTENT: Record<WindowKind, LazyExoticComponent<ComponentTyp
23
23
  "system-monitor": lazy(() => import("@/components/system/system-monitor-window-content")),
24
24
  "tab-host": lazy(() => import("./tab-host-window-content")),
25
25
  "remote-desktop": lazy(() => import("@/components/remote-desktop/remote-desktop-window-content")),
26
+ settings: lazy(() => import("@/components/settings/settings-window-content")),
26
27
  };
27
28
 
28
29
  /** Titlebar text for a window. Falls back to the kind's generic name. */
@@ -35,6 +36,7 @@ export function windowTitle(kind: WindowKind, payload?: Record<string, unknown>)
35
36
  }
36
37
  if (kind === "system-monitor") return "System Monitor";
37
38
  if (kind === "remote-desktop") return "Remote Desktop";
39
+ if (kind === "settings") return "Settings";
38
40
  // A detached tab carries its title in the payload; the generic name only shows for a
39
41
  // window whose tab has not been resolved yet (restore before the layout is loaded).
40
42
  if (kind === "tab-host") return "Tab";