@hienlh/ppm 0.19.0 → 0.19.2

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 (508) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +1 -1
  5. package/dist/web/assets/{adaptive-context-menu-JfuUSqbe.js → adaptive-context-menu-BbuRVTLn.js} +1 -1
  6. package/dist/web/assets/{ai-resource-editor-MfdJKVM5.js → ai-resource-editor-BCCOUzox.js} +1 -1
  7. package/dist/web/assets/{ai-settings-section-CJE7jFsj.js → ai-settings-section-BEM1z7kp.js} +1 -1
  8. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BlcrLjUx.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-BUjtceAJ.js} +1 -1
  10. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-BzWM7XPR.js} +1 -1
  11. package/dist/web/assets/api-client-D1CePgQ4.js +1 -0
  12. package/dist/web/assets/api-settings-CNtKkHqW.js +1 -0
  13. package/dist/web/assets/{api-settings-CPg4yGvS.js → api-settings-Cubuqjsu.js} +1 -1
  14. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-YsZgf6Xj.js} +1 -1
  15. package/dist/web/assets/{apl-do17bnVx.js → apl-B3g0v1pk.js} +1 -1
  16. package/dist/web/assets/{astro-ClY0e0OA.js → astro-FRnyn7cH.js} +1 -1
  17. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-DWTRm4HC.js} +1 -1
  18. package/dist/web/assets/{blade-BXSdXDGq.js → blade-NLdOqUKw.js} +1 -1
  19. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  20. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-CenHZfaU.js} +1 -1
  21. package/dist/web/assets/{button-CrwgGd1y.js → button-Cq8yixjf.js} +1 -1
  22. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-B_WR0aiQ.js} +1 -1
  23. package/dist/web/assets/chat-tab-CmXGsJGc.js +15 -0
  24. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  25. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  26. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  27. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  28. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  29. package/dist/web/assets/{cobol-phPOE89d.js → cobol-DOynhWYj.js} +1 -1
  30. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  31. package/dist/web/assets/code-editor-DtHXzNZ1.js +7 -0
  32. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-Bn6dbw4P.js} +1 -1
  33. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-Cgb-z12D.js} +1 -1
  34. package/dist/web/assets/{context-menu-BEAa1qhd.js → context-menu-DfWZAxQy.js} +1 -1
  35. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  36. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-CoZR9t8r.js} +1 -1
  37. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  38. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-BSheONSg.js} +1 -1
  39. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BZMHfFH3.js} +1 -1
  40. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-gwPoVSCU.js} +1 -1
  41. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-Dq_H1IPa.js} +1 -1
  42. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  43. package/dist/web/assets/database-viewer-GVaQVMdb.js +1 -0
  44. package/dist/web/assets/{dialog-DrL3WEXd.js → dialog-BSfuiTkJ.js} +1 -1
  45. package/dist/web/assets/{diff-viewer-CT0lmDc9.js → diff-viewer-BG5_fH1E.js} +1 -1
  46. package/dist/web/assets/{dist-C2_dKAa_.js → dist-aDSxTR_v.js} +1 -1
  47. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-DqVDOMmv.js} +1 -1
  48. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  49. package/dist/web/assets/{edge-Db4ze6bw.js → edge-DTtoslQu.js} +1 -1
  50. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-CXSbSUBX.js} +1 -1
  51. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-CIBCZ3Zi.js} +1 -1
  52. package/dist/web/assets/{elm-DNZcQt_7.js → elm-BLmb7K1H.js} +1 -1
  53. package/dist/web/assets/{erb-DRONsZXs.js → erb-BBY8ApRd.js} +1 -1
  54. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-D5Zb5_P0.js} +1 -1
  55. package/dist/web/assets/{esm-CZy-sSoK.js → esm-6F8v1jLL.js} +1 -1
  56. package/dist/web/assets/explorer-body-BPFnjxYj.js +2 -0
  57. package/dist/web/assets/explorer-window-content-Da1T9uM4.js +1 -0
  58. package/dist/web/assets/{extension-store-ClQpnxQr.js → extension-store-Bhu--fPX.js} +1 -1
  59. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-BSh6KGd7.js} +1 -1
  60. package/dist/web/assets/{external-link-ojDHcQxQ.js → external-link-B6Hl0U4X.js} +1 -1
  61. package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
  62. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  63. package/dist/web/assets/{file-store-28e44KP3.js → file-store-6HW2Lfl4.js} +1 -1
  64. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-Dx1hOk8X.js} +1 -1
  65. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CBdwkEUJ.js} +1 -1
  66. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-AT4XdVYH.js} +1 -1
  67. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-D852fWBR.js} +1 -1
  68. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-DWfWAyxy.js} +1 -1
  69. package/dist/web/assets/{git-log-panel-D1-YXZ0O.js → git-log-panel-DNC9fV0M.js} +1 -1
  70. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-C6opk8gr.js} +1 -1
  71. package/dist/web/assets/glide-data-grid-CICFjJon.js +141 -0
  72. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-DLi5mWYO.js} +1 -1
  73. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-D-scFX30.js} +1 -1
  74. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-dbQzF4iA.js} +1 -1
  75. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-D1m5Wen2.js} +1 -1
  76. package/dist/web/assets/group-chat-tab-DbUkcOaJ.js +1 -0
  77. package/dist/web/assets/{hack-CTWk0k1m.js → hack-CqYmpO7u.js} +1 -1
  78. package/dist/web/assets/{haml-DGh27hXc.js → haml-BXzKWEag.js} +1 -1
  79. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  80. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-DXnrtHtq.js} +1 -1
  81. package/dist/web/assets/{html-DALyDNqU.js → html-DhgQJ-xL.js} +1 -1
  82. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-xZRft-YE.js} +1 -1
  83. package/dist/web/assets/{http-DfMo7nNu.js → http-B53mSj14.js} +1 -1
  84. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-CsomVy04.js} +1 -1
  85. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-LffjWwKA.js} +1 -1
  86. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-CNA6XMNS.js} +1 -1
  87. package/dist/web/assets/index-DTqbMiVc.js +52 -0
  88. package/dist/web/assets/{input-DTmI80l2.js → input-D0ol5jW0.js} +1 -1
  89. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-CCHURl7V.js} +1 -1
  90. package/dist/web/assets/{jison-Dxut9QwA.js → jison-7qBd8dnR.js} +1 -1
  91. package/dist/web/assets/{julia-Cu85tTPo.js → julia-BY9pnTcy.js} +1 -1
  92. package/dist/web/assets/{just-CctM4uLa.js → just-D7wRSNRr.js} +1 -1
  93. package/dist/web/assets/{keybindings-store-Bi0q9x4Q.js → keybindings-store-BGkB72gM.js} +1 -1
  94. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +1 -0
  95. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DBBaoQSu.js} +1 -1
  96. package/dist/web/assets/{latex-kpwS_CC7.js → latex-RgsCae55.js} +1 -1
  97. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-v9x_qXgI.js} +1 -1
  98. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-CnDFzvOZ.js} +1 -1
  99. package/dist/web/assets/loader-circle-DouKgIXy.js +1 -0
  100. package/dist/web/assets/{lock-BEtt_eTO.js → lock-CNxUGpTk.js} +1 -1
  101. package/dist/web/assets/{lua-C_3gixwM.js → lua-0lj5W8sm.js} +1 -1
  102. package/dist/web/assets/markdown-renderer-DEAVpo0C.js +1 -0
  103. package/dist/web/assets/markdown-renderer-dbLIvU2M.js +3 -0
  104. package/dist/web/assets/{marko-B28rvNL-.js → marko-CQgqAhbV.js} +1 -1
  105. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-BkIcTiIy.js} +1 -1
  106. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-DDciwvLU.js} +1 -1
  107. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-pjcHKorA.js} +1 -1
  108. package/dist/web/assets/mobile-explorer-sheet-CD_kl-jA.js +1 -0
  109. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-CxPV_fQI.js} +1 -1
  110. package/dist/web/assets/{monitor-smartphone-C1wUPOSg.js → monitor-smartphone-DLZ6Q05s.js} +1 -1
  111. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-DvIW68DS.js} +1 -1
  112. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-O-hft0gG.js} +1 -1
  113. package/dist/web/assets/{nim-wqcek3WO.js → nim-BxrR4HwQ.js} +1 -1
  114. package/dist/web/assets/notification-store-Dgbsw9p9.js +1 -0
  115. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-BoLRYuZT.js} +1 -1
  116. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-l3g1gT5e.js} +1 -1
  117. package/dist/web/assets/{panel-store-Dv28vz3L.js → panel-store-UOV8wrfR.js} +1 -1
  118. package/dist/web/assets/{pdf-preview-DKeIIJTv.js → pdf-preview-B2CJ_EKv.js} +1 -1
  119. package/dist/web/assets/{pencil-CHKLOgLs.js → pencil-BlIIg6RH.js} +1 -1
  120. package/dist/web/assets/{perl-BH54gN9q.js → perl-Da-uNqmL.js} +1 -1
  121. package/dist/web/assets/{php-Br5ZrEZm.js → php-JMu45mEx.js} +1 -1
  122. package/dist/web/assets/play-DpEEBJTT.js +1 -0
  123. package/dist/web/assets/{plus-BxqD2FIZ.js → plus-BR0qewJI.js} +1 -1
  124. package/dist/web/assets/portal-container-context-D-495m5C.js +1 -0
  125. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-DstZwUai.js} +3 -3
  126. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-kmQ2-8qJ.js} +1 -1
  127. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-DTUCS65m.js} +1 -1
  128. package/dist/web/assets/{pug-8VCubZ3H.js → pug-CLzyQhia.js} +1 -1
  129. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-BHXfGB1l.js} +1 -1
  130. package/dist/web/assets/{razor-6v042IJW.js → razor-BqetJLN3.js} +1 -1
  131. package/dist/web/assets/{react-DHs6efqI.js → react-B549sK_T.js} +1 -1
  132. package/dist/web/assets/{refresh-cw-BALzw7L3.js → refresh-cw-BQU5g2CY.js} +1 -1
  133. package/dist/web/assets/remote-desktop-mobile-sheet-KKTRNjOL.js +2 -0
  134. package/dist/web/assets/{remote-desktop-mobile-view-DaoB49k4.js → remote-desktop-mobile-view-C1ST4-Fm.js} +1 -1
  135. package/dist/web/assets/{remote-desktop-readiness-gate--LJgsuoj.js → remote-desktop-readiness-gate-BH3BE5wA.js} +1 -1
  136. package/dist/web/assets/{remote-desktop-window-content-Cihx4PNZ.js → remote-desktop-window-content-BaZJQt_N.js} +1 -1
  137. package/dist/web/assets/{rotate-ccw-CuvNBM80.js → rotate-ccw-CpjRo90J.js} +1 -1
  138. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-iQdJZ0BP.js} +1 -1
  139. package/dist/web/assets/{ruby-BBaOP636.js → ruby-CNwzZGdH.js} +1 -1
  140. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-CBXmVGWa.js} +1 -1
  141. package/dist/web/assets/{sas-D5QEkIH2.js → sas-BpBIT2eR.js} +1 -1
  142. package/dist/web/assets/{save-Dxq-Zepc.js → save-DYLiVzU8.js} +1 -1
  143. package/dist/web/assets/{scroll-area-B5yhPpZo.js → scroll-area-lc0ZHORr.js} +1 -1
  144. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-D1MotuEp.js} +1 -1
  145. package/dist/web/assets/{send-Cits8q0l.js → send-C_sLFt56.js} +1 -1
  146. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-DJAdlvJ8.js} +1 -1
  147. package/dist/web/assets/{settings-store-Dp4i8zeA.js → settings-store-zbPkG7o-.js} +2 -2
  148. package/dist/web/assets/settings-tab-Cn-AqUP_.js +1 -0
  149. package/dist/web/assets/{settings-tab-BtMvvKAF.js → settings-tab-xyRpt2gq.js} +1 -1
  150. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-BNqmcPQi.js} +1 -1
  151. package/dist/web/assets/{shallow-BBJ_qoEF.js → shallow-CmPMAPte.js} +1 -1
  152. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BBLOVOO-.js} +1 -1
  153. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-CUI36aSc.js} +1 -1
  154. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-GdkkVhb3.js} +1 -1
  155. package/dist/web/assets/{soy-CGGf1G43.js → soy-BpTGo9uy.js} +1 -1
  156. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  157. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-D7A3yKob.js} +1 -1
  158. package/dist/web/assets/{sql-query-editor-DZqnYIB7.js → sql-query-editor-B86NiZHI.js} +1 -1
  159. package/dist/web/assets/{sqlite-viewer-DXutos0e.js → sqlite-viewer-DB775F6C.js} +1 -1
  160. package/dist/web/assets/{stata-DVQNLMcR.js → stata-B7N56ptS.js} +1 -1
  161. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-CTmnD-lv.js} +1 -1
  162. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-BppwY9TR.js} +1 -1
  163. package/dist/web/assets/{switch-BPol3BFC.js → switch-BmjKoIpR.js} +1 -1
  164. package/dist/web/assets/{system-monitor-body-Ct8_GX_f.js → system-monitor-body-BDxHQvy9.js} +2 -2
  165. package/dist/web/assets/system-monitor-tab-BoTHLx6B.js +1 -0
  166. package/dist/web/assets/system-monitor-window-content-B9Y98UW6.js +1 -0
  167. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-C1j8zr68.js} +1 -1
  168. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-DzYSsw5a.js} +1 -1
  169. package/dist/web/assets/{table-BXDyImqk.js → table-D5Q4ap4W.js} +1 -1
  170. package/dist/web/assets/team-member-sheet-Cxz6h5Ne.js +2 -0
  171. package/dist/web/assets/{team-member-window-content-BJ_XFw8-.js → team-member-window-content-CUSuRHCB.js} +1 -1
  172. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-CvTMKKra.js} +1 -1
  173. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-B9I_HlES.js} +3 -3
  174. package/dist/web/assets/{tex-BrTByT7i.js → tex--c4POEUE.js} +1 -1
  175. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-DBvQD3kc.js} +1 -1
  176. package/dist/web/assets/{tool-cards-pIu_cmEL.js → tool-cards-D3KSzd2K.js} +4 -4
  177. package/dist/web/assets/{trash-2-BjShZ7if.js → trash-2-By4VMbC4.js} +1 -1
  178. package/dist/web/assets/{triangle-alert-qQ6Plydp.js → triangle-alert-CUTURFbG.js} +1 -1
  179. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags--kx7D-Hw.js} +1 -1
  180. package/dist/web/assets/{twig-DW9qc1bf.js → twig-UG9uMHGd.js} +1 -1
  181. package/dist/web/assets/{upload-7Zs6oaK5.js → upload-BNlX8D07.js} +1 -1
  182. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Bdxopwwc.js} +1 -1
  183. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  184. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-B9UZNwSm.js} +1 -1
  185. package/dist/web/assets/{use-monaco-theme-DaxAk3gB.js → use-monaco-theme-CVCye2kq.js} +1 -1
  186. package/dist/web/assets/use-open-team-member-D9EOB8-i.js +1 -0
  187. package/dist/web/assets/{use-remote-desktop-display-choice-BUA_1eaH.js → use-remote-desktop-display-choice-neUqyE3p.js} +1 -1
  188. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-Sfb6CZ80.js} +1 -1
  189. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-xOOwBdwP.js} +1 -1
  190. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  191. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  192. package/dist/web/assets/video-preview-DvpgBDhM.js +1 -0
  193. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-8KEW6-I9.js} +1 -1
  194. package/dist/web/assets/{vue-CJNDL1g-.js → vue-jjTomlbt.js} +1 -1
  195. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-C5tohCkU.js} +1 -1
  196. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-FdZdGF7h.js} +1 -1
  197. package/dist/web/assets/{window-store-CNzkeUyO.js → window-store-D0x_fxr0.js} +1 -1
  198. package/dist/web/assets/x-DiOUR_mp.js +1 -0
  199. package/dist/web/assets/{xml-DTOm5niz.js → xml-ByFFosqL.js} +1 -1
  200. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-CZUwiLkj.js} +1 -1
  201. package/dist/web/index.html +59 -59
  202. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  203. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  204. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  205. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  206. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  207. package/dist/web/sw.js +1 -1
  208. package/package.json +1 -1
  209. package/scripts/check-ppm-dir-usage.sh +0 -0
  210. package/scripts/install.sh +0 -0
  211. package/scripts/release.sh +0 -0
  212. package/src/providers/claude-agent-sdk.ts +51 -43
  213. package/src/providers/codex-app-server/codex-provider.ts +7 -1
  214. package/src/server/routes/chat.ts +28 -3
  215. package/src/server/ws/chat.ts +9 -4
  216. package/src/server/ws/terminal.ts +9 -3
  217. package/src/services/media-transcode/ffmpeg-capabilities.ts +48 -7
  218. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  219. package/src/services/named-tunnel/network-ready.ts +63 -0
  220. package/src/services/supervisor.ts +98 -0
  221. package/src/services/transcript-images.ts +14 -7
  222. package/src/services/upgrade.service.ts +7 -2
  223. package/src/shared/image-limits.ts +37 -0
  224. package/src/types/api.ts +3 -1
  225. package/src/types/chat.ts +6 -0
  226. package/src/types/extension-messages.ts +2 -0
  227. package/src/web/components/chat/attachment-chips.tsx +10 -0
  228. package/src/web/components/chat/chat-tab.tsx +37 -13
  229. package/src/web/components/chat/message-input.tsx +102 -11
  230. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  231. package/src/web/components/chat/user-message-parse.ts +33 -11
  232. package/src/web/components/database/database-viewer.tsx +34 -11
  233. package/src/web/components/database/extract-query-table.ts +24 -0
  234. package/src/web/components/database/glide-data-grid.tsx +11 -4
  235. package/src/web/components/database/glide-grid-types.ts +4 -0
  236. package/src/web/components/database/split-sql-statements.ts +116 -0
  237. package/src/web/components/database/sql-completion-provider.ts +1 -26
  238. package/src/web/components/database/sql-query-editor.tsx +2 -1
  239. package/src/web/components/database/use-database.ts +16 -1
  240. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  241. package/src/web/components/editor/code-editor.tsx +30 -56
  242. package/src/web/hooks/use-chat.ts +6 -3
  243. package/src/web/lib/attachment-marker-split.ts +44 -0
  244. package/src/web/lib/image-animation.ts +51 -0
  245. package/src/web/lib/image-resize-limits.ts +47 -0
  246. package/src/web/lib/image-resize.ts +133 -0
  247. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  248. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  249. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  250. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  251. package/dist/web/assets/circle-4g2coECH.js +0 -1
  252. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  253. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  254. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  255. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  256. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  257. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  258. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  259. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  260. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  261. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  262. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  263. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  264. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  265. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  266. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  267. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  268. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  269. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  270. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  271. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  272. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  273. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  274. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  275. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  276. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  277. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  278. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-C5DFF0s3.js} +0 -0
  279. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-B0qyRNbc.js} +0 -0
  280. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-bAJzplug.js} +0 -0
  281. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DUCpISy6.js} +0 -0
  282. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-BhAr84Z_.js} +0 -0
  283. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CRv8YnNq.js} +0 -0
  284. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-CD-Cw8bt.js} +0 -0
  285. /package/dist/web/assets/{ara-iGt2zixk.js → ara-BUzeQYoG.js} +0 -0
  286. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-CT2hTg-v.js} +0 -0
  287. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-CAu35JeY.js} +0 -0
  288. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BeFJoIA1.js} +0 -0
  289. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-Vau-DrGN.js} +0 -0
  290. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-CLe-OTsQ.js} +0 -0
  291. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-CJbQfu4M.js} +0 -0
  292. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-GWaKwdrx.js} +0 -0
  293. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-DWu0USI5.js} +0 -0
  294. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-_07Huw7E.js} +0 -0
  295. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-EcvogrcF.js} +0 -0
  296. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBB87SY7.js} +0 -0
  297. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-DbueS87A.js} +0 -0
  298. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-tcmXnUo4.js} +0 -0
  299. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-B2qwilIR.js} +0 -0
  300. /package/dist/web/assets/{c-AjOLBXIs.js → c-Z58oivSt.js} +0 -0
  301. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-U40cGT9p.js} +0 -0
  302. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-BFcD-LyM.js} +0 -0
  303. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-RkC9AX6J.js} +0 -0
  304. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-Bsi4l-nO.js} +0 -0
  305. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-B8JIdTW-.js} +0 -0
  306. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-oH8wwrDj.js} +0 -0
  307. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-C3WNeRCR.js} +0 -0
  308. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-CFoAz7u9.js} +0 -0
  309. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-Bcsqp9yR.js} +0 -0
  310. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-DC2Z7heP.js} +0 -0
  311. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-BfG68Al5.js} +0 -0
  312. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-Z5P-lKQe.js} +0 -0
  313. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-BJslqrnv.js} +0 -0
  314. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-DhhZTOTs.js} +0 -0
  315. /package/dist/web/assets/{css-Cr27CfPY.js → css-DiFIpMfE.js} +0 -0
  316. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-DViLbHxI.js} +0 -0
  317. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-CoG3mg2I.js} +0 -0
  318. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-jTxC8NAB.js} +0 -0
  319. /package/dist/web/assets/{d-CgG1_yzd.js → d-6hKIUAbU.js} +0 -0
  320. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-D_-np2Sr.js} +0 -0
  321. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-CVpxLpVn.js} +0 -0
  322. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-BPqJ8qIY.js} +0 -0
  323. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-u_y5_Hr5.js} +0 -0
  324. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Duj1XMrd.js} +0 -0
  325. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-CEZ4plgH.js} +0 -0
  326. /package/dist/web/assets/{docker-COBitpld.js → docker-8UGSCc7N.js} +0 -0
  327. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-DVnj8kyR.js} +0 -0
  328. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-D0cEmSg7.js} +0 -0
  329. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-DfGS5Yfr.js} +0 -0
  330. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-qG8kMGTF.js} +0 -0
  331. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-iVqyMLyT.js} +0 -0
  332. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-Cruygd5U.js} +0 -0
  333. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-BiJa8RHL.js} +0 -0
  334. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-H6XSEIoz.js} +0 -0
  335. /package/dist/web/assets/{fish-B04oqgKk.js → fish-ho_7CxX6.js} +0 -0
  336. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-1l5ssJTF.js} +0 -0
  337. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-Bo1x233A.js} +0 -0
  338. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-DffFMp_k.js} +0 -0
  339. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-CvnpAiAF.js} +0 -0
  340. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-2eOvAEzW.js} +0 -0
  341. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-DL-5OUlV.js} +0 -0
  342. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-DrTZprgG.js} +0 -0
  343. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-_mDH8IMP.js} +0 -0
  344. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-C48hJuLn.js} +0 -0
  345. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-BQ-zGGNe.js} +0 -0
  346. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-BYc90Yx8.js} +0 -0
  347. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-Daecj4J5.js} +0 -0
  348. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-DoafCbsU.js} +0 -0
  349. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-hUWWjmnp.js} +0 -0
  350. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-7PJ3R70o.js} +0 -0
  351. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-V-Pxck7b.js} +0 -0
  352. /package/dist/web/assets/{go-DXvjjq4q.js → go-ARF6DprD.js} +0 -0
  353. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-CKGBIPOz.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-Cm9wqdI3.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-Cmsd6dG8.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-BBGlGGCz.js} +0 -0
  357. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-BCR7KmqG.js} +0 -0
  358. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-CP32bga8.js} +0 -0
  359. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-qjxk8ps6.js} +0 -0
  360. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-C5DzwGRa.js} +0 -0
  361. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-C8f_jCVv.js} +0 -0
  362. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DetJkf6E.js} +0 -0
  363. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-DeZhwIdo.js} +0 -0
  364. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-SMIDLRyX.js} +0 -0
  365. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-BMAVDxMx.js} +0 -0
  366. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-3BaV_VGD.js} +0 -0
  367. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BWBbEPvb.js} +0 -0
  368. /package/dist/web/assets/{hy-BALeMn3i.js → hy-DcsmE-Nm.js} +0 -0
  369. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CnkgnYMf.js} +0 -0
  370. /package/dist/web/assets/{ini-DG7imsqe.js → ini-BnvTdau7.js} +0 -0
  371. /package/dist/web/assets/{java-D5GTT474.js → java-BLFACXat.js} +0 -0
  372. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-BKcFWENk.js} +0 -0
  373. /package/dist/web/assets/{json-B9FhwAgR.js → json-3AJtJADf.js} +0 -0
  374. /package/dist/web/assets/{json5--Abk7MUy.js → json5-DLyDVlwe.js} +0 -0
  375. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-B5DgF1jW.js} +0 -0
  376. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-CV_O71a2.js} +0 -0
  377. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-DZfPLgYz.js} +0 -0
  378. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-Cr1zYOsY.js} +0 -0
  379. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-VN-JS2_q.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-D9CcT3xg.js} +0 -0
  381. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-DLXsB8fr.js} +0 -0
  382. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-weDs3Wjz.js} +0 -0
  383. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-0XF56jnT.js} +0 -0
  384. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-BPkH9vzk.js} +0 -0
  385. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-DMVpADb9.js} +0 -0
  386. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-Cl6T2cGP.js} +0 -0
  387. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-BvwzQGl1.js} +0 -0
  388. /package/dist/web/assets/{less-Bk_UaqsM.js → less-DI_03xkC.js} +0 -0
  389. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-hIiIsQN_.js} +0 -0
  390. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-B5hjjSWc.js} +0 -0
  391. /package/dist/web/assets/{log-CKm0CxB1.js → log-D28raQiE.js} +0 -0
  392. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-6Cdd4ASb.js} +0 -0
  393. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-CK9nKpOX.js} +0 -0
  394. /package/dist/web/assets/{make-CpNSwgwT.js → make-CVnxKCB6.js} +0 -0
  395. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-B9mMQzD5.js} +0 -0
  396. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-BZSs2o0j.js} +0 -0
  397. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-BeAQBzQ_.js} +0 -0
  398. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-BAe0IXcs.js} +0 -0
  399. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CiPIf0Np.js} +0 -0
  400. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-DJBb4_XN.js} +0 -0
  401. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-CY0dbp_s.js} +0 -0
  402. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-DU9zbF7h.js} +0 -0
  403. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-UmmjGwdh.js} +0 -0
  404. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-C59GkzOm.js} +0 -0
  405. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-B9QglrdQ.js} +0 -0
  406. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-Dc6Jsr_D.js} +0 -0
  407. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D5ij-nGW.js} +0 -0
  408. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-QAtE_yCP.js} +0 -0
  409. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-BMtIdiTL.js} +0 -0
  410. /package/dist/web/assets/{move-DItn61bH.js → move-BB_jMHYZ.js} +0 -0
  411. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-BvvoKGVn.js} +0 -0
  412. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-CqxtAsnF.js} +0 -0
  413. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-CLoE9kjt.js} +0 -0
  414. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-BwIFT5Xd.js} +0 -0
  415. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-BKlnVT9r.js} +0 -0
  416. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-B7R1KRAK.js} +0 -0
  417. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-CzUme-NJ.js} +0 -0
  418. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-D-UKKiOc.js} +0 -0
  419. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-BucWn4Fx.js} +0 -0
  420. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-EJCNXMGo.js} +0 -0
  421. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-CEjnRU_3.js} +0 -0
  422. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-Bv469NX5.js} +0 -0
  423. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-BlzO5RWI.js} +0 -0
  424. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-Czl6alLw.js} +0 -0
  425. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-BI9fLvG4.js} +0 -0
  426. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-ChQLsyjU.js} +0 -0
  427. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-DGL3mtdL.js} +0 -0
  428. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-BJDjqcWp.js} +0 -0
  429. /package/dist/web/assets/{po-_kXpd_0n.js → po-DK5O3OpF.js} +0 -0
  430. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CnuqZT4T.js} +0 -0
  431. /package/dist/web/assets/{polar-DV_1L04z.js → polar-DCsUxSsQ.js} +0 -0
  432. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-8UjdFmis.js} +0 -0
  433. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CAp0CGQj.js} +0 -0
  434. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CbBsKZJP.js} +0 -0
  435. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-BnNpXCJN.js} +0 -0
  436. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-CC-l9E8Y.js} +0 -0
  437. /package/dist/web/assets/{proto-B5meVsrA.js → proto-CV_1k2rL.js} +0 -0
  438. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-2D8HU2bv.js} +0 -0
  439. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-BEJAORgU.js} +0 -0
  440. /package/dist/web/assets/{python-DeTIYd5V.js → python-BBdbb7nn.js} +0 -0
  441. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-D5ZYI4Ie.js} +0 -0
  442. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-BOJVGyXE.js} +0 -0
  443. /package/dist/web/assets/{r-DHwHK0Jh.js → r-B5dlEtkj.js} +0 -0
  444. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-BUAEygJ6.js} +0 -0
  445. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-BG0Ss0ID.js} +0 -0
  446. /package/dist/web/assets/{red-BFuHRZFS.js → red-C5yyQbtG.js} +0 -0
  447. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-DHoBWtLf.js} +0 -0
  448. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-DDtJrlKv.js} +0 -0
  449. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-CR6WJDRD.js} +0 -0
  450. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-2V9tC-nx.js} +0 -0
  451. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-QLTmqGUw.js} +0 -0
  452. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-be0__X3A.js} +0 -0
  453. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-n_WQJwsW.js} +0 -0
  454. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-t2aLtzgB.js} +0 -0
  455. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-Ce1JD8vt.js} +0 -0
  456. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-5fHH-ZoW.js} +0 -0
  457. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-DPmGFiOJ.js} +0 -0
  458. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-BRMVFbZF.js} +0 -0
  459. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-BMSJWevF.js} +0 -0
  460. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-DTZ-pEHI.js} +0 -0
  461. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-6-9yg5Cz.js} +0 -0
  462. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-DKJgiR7e.js} +0 -0
  463. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-WjehVgyK.js} +0 -0
  464. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-BChf4SN1.js} +0 -0
  465. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-C1WAwOx6.js} +0 -0
  466. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-BxREZrGx.js} +0 -0
  467. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-DG29B1pR.js} +0 -0
  468. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-bDbtGwdR.js} +0 -0
  469. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Ceqx5njI.js} +0 -0
  470. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-OwMEKbz8.js} +0 -0
  471. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-WAR3lu2k.js} +0 -0
  472. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Cjmoz3d8.js} +0 -0
  473. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-S_ZSBG1q.js} +0 -0
  474. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-DiUeWMSh.js} +0 -0
  475. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-RRjqPjUV.js} +0 -0
  476. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-BD3SkXoU.js} +0 -0
  477. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-DWefISOm.js} +0 -0
  478. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-DCjSS-09.js} +0 -0
  479. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-DTYqT_66.js} +0 -0
  480. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CkbP5QnR.js} +0 -0
  481. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-qVSqO9xy.js} +0 -0
  482. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-D9F5Osqs.js} +0 -0
  483. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-C5tpHoxm.js} +0 -0
  484. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-CMWkGtGm.js} +0 -0
  485. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Cn4z3xOi.js} +0 -0
  486. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-DSyAEWsn.js} +0 -0
  487. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-B93Qap7y.js} +0 -0
  488. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-DtlOmFlX.js} +0 -0
  489. /package/dist/web/assets/{v-Bf2hKAMu.js → v-Dfzf-szS.js} +0 -0
  490. /package/dist/web/assets/{vala-Cbete-UE.js → vala-CyiQZQ9f.js} +0 -0
  491. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-C7DGSldd.js} +0 -0
  492. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-kF-hClY2.js} +0 -0
  493. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-BmKcADuz.js} +0 -0
  494. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-CDnb16KJ.js} +0 -0
  495. /package/dist/web/assets/{viml-BePrIAs8.js → viml-B8cs6Ady.js} +0 -0
  496. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-BR6o9Gto.js} +0 -0
  497. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-Dye14-KB.js} +0 -0
  498. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-BLpswIxf.js} +0 -0
  499. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-DU51Z97W.js} +0 -0
  500. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-CVqlJAR2.js} +0 -0
  501. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-DhCa3hCn.js} +0 -0
  502. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-Bj1Ec4vv.js} +0 -0
  503. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-C90DHNa0.js} +0 -0
  504. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-DUgbOD7Z.js} +0 -0
  505. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-BgwUqD1I.js} +0 -0
  506. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-DEQulj-8.js} +0 -0
  507. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-C7Jp7TgT.js} +0 -0
  508. /package/dist/web/assets/{zig-Cr80Qrru.js → zig-9x4qXYLg.js} +0 -0
@@ -2,6 +2,8 @@ import { useState, useRef, useCallback, useEffect, memo, type KeyboardEvent, typ
2
2
  import { ArrowUp, Square, Paperclip, Loader2, Mic, MicOff, Zap, ListOrdered, Clock, Bot, X } from "lucide-react";
3
3
  import { useVoiceInput } from "@/hooks/use-voice-input";
4
4
  import { api, projectUrl, getAuthToken } from "@/lib/api-client";
5
+ import { downscaleImage } from "@/lib/image-resize";
6
+ import { INLINE_IMAGE_LIMITS } from "@/lib/image-resize-limits";
5
7
  import { randomId } from "@/lib/utils";
6
8
  import { ownsGlobalShortcut } from "@/lib/owns-global-shortcut";
7
9
  import { SEND_TO_CHAT_EVENT, SEND_TO_CHAT_ACK_EVENT, type SendToChatDetail } from "@/lib/send-to-chat";
@@ -17,6 +19,29 @@ import { fetchSlashItems, clearSlashItemsCache } from "@/lib/slash-items-cache";
17
19
  import type { FileNode } from "../../../types/project";
18
20
  import { useFileStore } from "@/stores/file-store";
19
21
 
22
+ /**
23
+ * Base64 payload for an image file, or undefined when it cannot be read.
24
+ *
25
+ * Reads through FileReader rather than assembling the string by hand: a phone photo is
26
+ * millions of characters, and building it in chunks on the main thread freezes the composer
27
+ * for as long as it takes.
28
+ */
29
+ async function readImageData(file: File): Promise<{ data: string; mediaType: string } | undefined> {
30
+ try {
31
+ const dataUrl = await new Promise<string>((resolve, reject) => {
32
+ const reader = new FileReader();
33
+ reader.onload = () => resolve(String(reader.result ?? ""));
34
+ reader.onerror = () => reject(reader.error);
35
+ reader.readAsDataURL(file);
36
+ });
37
+ const comma = dataUrl.indexOf(",");
38
+ const data = comma >= 0 ? dataUrl.slice(comma + 1) : "";
39
+ return data ? { data, mediaType: file.type || "image/png" } : undefined;
40
+ } catch {
41
+ return undefined;
42
+ }
43
+ }
44
+
20
45
  export interface ChatAttachment {
21
46
  id: string;
22
47
  name: string;
@@ -27,6 +52,16 @@ export interface ChatAttachment {
27
52
  serverPath?: string;
28
53
  /** Inline text content (e.g. terminal output) — no upload needed */
29
54
  textContent?: string;
55
+ /**
56
+ * Base64 payload for an image, sent as part of the message itself.
57
+ *
58
+ * Passing the path instead would make the model spend a whole extra round trip calling Read
59
+ * to fetch it, and every round trip re-sends the entire transcript. The uploaded copy is
60
+ * still kept, so removing the payload from a transcript later stays recoverable.
61
+ */
62
+ imageData?: { data: string; mediaType: string };
63
+ /** Dimensions before and after downscaling, when one happened — surfaced in the chip. */
64
+ resized?: { from: { width: number; height: number }; to: { width: number; height: number } };
30
65
  status: "uploading" | "ready" | "error";
31
66
  }
32
67
 
@@ -436,6 +471,27 @@ export const MessageInput = memo(function MessageInput({
436
471
  onExternalPathsConsumed?.();
437
472
  }, [externalPaths]); // eslint-disable-line react-hooks/exhaustive-deps
438
473
 
474
+ /** Delete an upload the user removed before sending. Best effort — nothing depends on it. */
475
+ const discardUpload = useCallback(
476
+ async (serverPath: string) => {
477
+ if (!projectName) return;
478
+ const filename = serverPath.split(/[\\/]/).pop();
479
+ if (!filename) return;
480
+ try {
481
+ const headers: HeadersInit = {};
482
+ const token = getAuthToken();
483
+ if (token) headers["Authorization"] = `Bearer ${token}`;
484
+ await fetch(`${projectUrl(projectName)}/chat/uploads/${encodeURIComponent(filename)}`, {
485
+ method: "DELETE",
486
+ headers,
487
+ });
488
+ } catch {
489
+ // An orphaned file is not worth surfacing to the user.
490
+ }
491
+ },
492
+ [projectName],
493
+ );
494
+
439
495
  /** Upload a single file to the server, return server path */
440
496
  const uploadFile = useCallback(
441
497
  async (file: File): Promise<string | null> => {
@@ -466,32 +522,64 @@ export const MessageInput = memo(function MessageInput({
466
522
  /** Process files — always uploads to server. Path resolution only happens via @ picker. */
467
523
  const processFiles = useCallback(
468
524
  async (files: File[]) => {
469
- for (const file of files) {
525
+ for (const original of files) {
470
526
  const id = randomId();
471
- const isImg = isImageFile(file);
472
- const previewUrl = isImg ? URL.createObjectURL(file) : undefined;
527
+ const isImg = isImageFile(original);
473
528
 
474
529
  const att: ChatAttachment = {
475
530
  id,
476
- name: file.name,
477
- file,
531
+ name: original.name,
532
+ file: original,
478
533
  isImage: isImg,
479
- previewUrl,
534
+ previewUrl: isImg ? URL.createObjectURL(original) : undefined,
480
535
  status: "uploading",
481
536
  };
482
537
 
483
538
  setAttachments((prev) => [...prev, att]);
484
539
 
485
- // Upload in background
486
- uploadFile(file).then((serverPath) => {
540
+ // Shrink first, so the upload, the preview and the message all carry the same
541
+ // already-reduced image — an oversized original must not reach the transcript.
542
+ //
543
+ // Only a `scaled` or `inlineable` result is attached inline. An image that could not
544
+ // be measured or re-encoded travels by path alone: sending unverified dimensions
545
+ // inline risks a payload the API refuses, and that refusal outlives the turn, since
546
+ // the transcript replays it into every later one.
547
+ try {
548
+ const outcome = isImg ? await downscaleImage(original) : null;
549
+ const file = outcome?.file ?? original;
550
+ // Point the thumbnail at the reduced file and let the original go. It is the larger
551
+ // of the two and nothing needs it once the resize is done, but it stayed alive until
552
+ // the message was sent because the preview still referenced it.
553
+ const previewUrl = isImg && file !== original ? URL.createObjectURL(file) : undefined;
554
+ const serverPath = await uploadFile(file);
555
+ const inlineable = outcome ? outcome.kind !== "asis" : false;
556
+ const imageData = inlineable ? await readImageData(file) : undefined;
557
+ const withinPayloadCap =
558
+ !!imageData && imageData.data.length <= INLINE_IMAGE_LIMITS.maxBase64PerImage;
559
+
560
+ if (previewUrl && att.previewUrl) URL.revokeObjectURL(att.previewUrl);
487
561
  setAttachments((prev) =>
488
562
  prev.map((a) =>
489
563
  a.id === id
490
- ? { ...a, serverPath: serverPath ?? undefined, status: serverPath ? "ready" : "error" }
564
+ ? {
565
+ ...a,
566
+ file,
567
+ ...(previewUrl && { previewUrl }),
568
+ serverPath: serverPath ?? undefined,
569
+ imageData: withinPayloadCap ? imageData : undefined,
570
+ resized: outcome?.to ? { from: outcome.from!, to: outcome.to } : undefined,
571
+ // An image can still be sent inline when the upload failed; anything
572
+ // else has nothing left to send without its path.
573
+ status: serverPath || withinPayloadCap ? "ready" : "error",
574
+ }
491
575
  : a,
492
576
  ),
493
577
  );
494
- });
578
+ } catch {
579
+ // Nothing here may leave an attachment stuck on "uploading" — both the send button
580
+ // and the auto-send effect wait on that state, so a swallowed throw hangs the composer.
581
+ setAttachments((prev) => prev.map((a) => (a.id === id ? { ...a, status: "error" } : a)));
582
+ }
495
583
  }
496
584
  (mobileTextareaRef.current ?? textareaRef.current)?.focus();
497
585
  },
@@ -502,9 +590,12 @@ export const MessageInput = memo(function MessageInput({
502
590
  setAttachments((prev) => {
503
591
  const att = prev.find((a) => a.id === id);
504
592
  if (att?.previewUrl) URL.revokeObjectURL(att.previewUrl);
593
+ // Uploads are never swept, because chat history points at them indefinitely, so one
594
+ // abandoned before it was ever sent would sit there with nothing referencing it.
595
+ if (att?.serverPath) void discardUpload(att.serverPath);
505
596
  return prev.filter((a) => a.id !== id);
506
597
  });
507
- }, []);
598
+ }, [discardUpload]);
508
599
 
509
600
  /** Execute the actual send (called directly or after uploads complete) */
510
601
  const executeSend = useCallback(() => {
@@ -147,7 +147,8 @@ function TranscriptImages({ sessionId, projectName }: { sessionId: string; proje
147
147
  {audit.oversizedAttachments} of those {audit.oversizedAttachments === 1 ? "is an image you" : "are images you"}
148
148
  {" "}attached yourself, so the buttons below leave {audit.oversizedAttachments === 1 ? "it" : "them"} in place.
149
149
  While {audit.oversizedAttachments === 1 ? "it stays" : "they stay"}, every turn in this session keeps failing.
150
- The transcript holds the only copy — removing {audit.oversizedAttachments === 1 ? "it" : "them"} cannot be undone.
150
+ Removing {audit.oversizedAttachments === 1 ? "it" : "them"} cannot be undone: an attachment PPM sent keeps
151
+ a copy on disk with its path still in the message, but one from another client does not.
151
152
  </span>
152
153
  </div>
153
154
  <Button
@@ -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
  }