@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.
- package/CHANGELOG.md +29 -1
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +3 -2
- package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
- package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
- package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
- package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
- package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BuHNQIbQ.js} +1 -1
- package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-DHRWoLc-.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-oY1tC3tM.js} +1 -1
- package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
- package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-Cknp3Q-P.js} +1 -1
- package/dist/web/assets/{apl-do17bnVx.js → apl-C1VSWVei.js} +1 -1
- package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
- package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
- package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
- package/dist/web/assets/{astro-ClY0e0OA.js → astro-RX-rChsZ.js} +1 -1
- package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-CCd1jdje.js} +1 -1
- package/dist/web/assets/{blade-BXSdXDGq.js → blade-CZ6bxMbK.js} +1 -1
- package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
- package/dist/web/assets/{pencil-CHKLOgLs.js → brain-KDa9jFRq.js} +1 -1
- package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-BhgCXLWk.js} +1 -1
- package/dist/web/assets/{button-CrwgGd1y.js → button-BZtEJcUv.js} +1 -1
- package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-BrwygPkn.js} +1 -1
- package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
- package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
- package/dist/web/assets/check-DN75Ucvz.js +1 -0
- package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
- package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
- package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
- package/dist/web/assets/circle-BCy0AUCo.js +1 -0
- package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
- package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
- package/dist/web/assets/{cobol-phPOE89d.js → cobol-C5TGW1Ir.js} +1 -1
- package/dist/web/assets/code-DJgYrFzm.js +1 -0
- package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
- package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-CFgKlmgI.js} +1 -1
- package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-BoRPwL3L.js} +1 -1
- package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
- package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
- package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-BhG5q3-d.js} +1 -1
- package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
- package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-DvuI7_tY.js} +1 -1
- package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BpoJqZQi.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
- package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-CdAg-4W1.js} +1 -1
- package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
- package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
- package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
- package/dist/web/assets/{dist-C2_dKAa_.js → dist-DonmFWG8.js} +1 -1
- package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-CWIDVNZn.js} +1 -1
- package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
- package/dist/web/assets/{edge-Db4ze6bw.js → edge-BSIEsmlI.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-DeHb85DN.js} +1 -1
- package/dist/web/assets/{elixir-RZPZ0472.js → elixir-BF-lrGXM.js} +1 -1
- package/dist/web/assets/{elm-DNZcQt_7.js → elm-8mCsJOeN.js} +1 -1
- package/dist/web/assets/{erb-DRONsZXs.js → erb-Dw5PUVKa.js} +1 -1
- package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-BvjBIA6O.js} +1 -1
- package/dist/web/assets/{esm-CZy-sSoK.js → esm-Bt9OwSnU.js} +1 -1
- package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
- package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
- package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
- package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-Diz2OzVq.js} +1 -1
- package/dist/web/assets/eye-gWwaF4Lg.js +1 -0
- package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
- package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
- package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
- package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
- package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-fQRoG_rV.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
- package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-CjUIHIHp.js} +1 -1
- package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-x_NUQFrZ.js} +1 -1
- package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
- package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-BOpxSbns.js} +1 -1
- package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
- package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-BW4xlK7k.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
- package/dist/web/assets/glide-data-grid-pKQuyYFj.js +141 -0
- package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-CT9jrmFj.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-CjQT1lxK.js} +1 -1
- package/dist/web/assets/{glsl-BX2--oI_.js → glsl-DsFJvtg5.js} +1 -1
- package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-Dhlnmkn_.js} +1 -1
- package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
- package/dist/web/assets/{hack-CTWk0k1m.js → hack-BKHbsJ7u.js} +1 -1
- package/dist/web/assets/{haml-DGh27hXc.js → haml-C1Ih25Y2.js} +1 -1
- package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
- package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-CaFitvmC.js} +1 -1
- package/dist/web/assets/{html-DALyDNqU.js → html-BwpKIE6V.js} +1 -1
- package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-uuTEquZU.js} +1 -1
- package/dist/web/assets/{http-DfMo7nNu.js → http-Cv4Pg5cR.js} +1 -1
- package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-DubkI6TG.js} +1 -1
- package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-B9OVZFAB.js} +1 -1
- package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-DRbZ0FA2.js} +1 -1
- package/dist/web/assets/index--KWVW-Ga.js +52 -0
- package/dist/web/assets/index-CvGx7wBQ.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
- package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-BHb1jyl6.js} +1 -1
- package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
- package/dist/web/assets/{jison-Dxut9QwA.js → jison-CtpfXDzp.js} +1 -1
- package/dist/web/assets/{julia-Cu85tTPo.js → julia-TYjgpazU.js} +1 -1
- package/dist/web/assets/{just-CctM4uLa.js → just-D27bzZuW.js} +1 -1
- package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
- package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
- package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DiOYrs6C.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
- package/dist/web/assets/{latex-kpwS_CC7.js → latex-D_MzopTn.js} +1 -1
- package/dist/web/assets/{lib-Ca7ai8xa.js → lib-xrSJ4EZX.js} +1 -1
- package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-BG_FG0Pe.js} +1 -1
- package/dist/web/assets/loader-circle-DPMrnLQ2.js +1 -0
- package/dist/web/assets/{lua-C_3gixwM.js → lua-Bl29oC2L.js} +1 -1
- package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
- package/dist/web/assets/markdown-renderer-DDcIGQEd.js +3 -0
- package/dist/web/assets/{marko-B28rvNL-.js → marko-D-HI7dbm.js} +1 -1
- package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-y444JQTQ.js} +1 -1
- package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-VzecHk9g.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
- package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-C835ucWH.js} +1 -1
- package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-C5sKcldC.js} +1 -1
- package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-bXnEC3wm.js} +1 -1
- package/dist/web/assets/{nim-wqcek3WO.js → nim-pevhf4WD.js} +1 -1
- package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
- package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
- package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-B7WINIW4.js} +1 -1
- package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-mhN0qw6Q.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
- package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
- package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
- package/dist/web/assets/{perl-BH54gN9q.js → perl-B1KUtMnW.js} +1 -1
- package/dist/web/assets/{php-Br5ZrEZm.js → php-BGmVZBAJ.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
- package/dist/web/assets/portal-container-context-a399Hd9F.js +1 -0
- package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-jaR0FYJ_.js} +3 -3
- package/dist/web/assets/power-CriQkeK5.js +1 -0
- package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
- package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-D1r-Gupi.js} +1 -1
- package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
- package/dist/web/assets/{pug-8VCubZ3H.js → pug-BfN8CpEH.js} +1 -1
- package/dist/web/assets/{qml-D8AF1RZ6.js → qml-D6fRdLZt.js} +1 -1
- package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
- package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
- package/dist/web/assets/{razor-6v042IJW.js → razor-fg-9AScR.js} +1 -1
- package/dist/web/assets/{react-DHs6efqI.js → react-X4jUntEf.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
- package/dist/web/assets/{rst-mTGFo4AQ.js → rst-BZgx076b.js} +1 -1
- package/dist/web/assets/{ruby-BBaOP636.js → ruby-iUkWDcLo.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-bKRwuO2z.js} +1 -1
- package/dist/web/assets/{sas-D5QEkIH2.js → sas-CLXO0lZ6.js} +1 -1
- package/dist/web/assets/{save-Dxq-Zepc.js → save-CipGrG1k.js} +1 -1
- package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
- package/dist/web/assets/{scss-C5Mu6y2u.js → scss-DQqteGaG.js} +1 -1
- package/dist/web/assets/send-Bb0jtcR6.js +1 -0
- package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-S8LCNX7V.js} +1 -1
- package/dist/web/assets/separator-DqGg3f9c.js +1 -0
- package/dist/web/assets/settings-body-omv04jbb.js +2 -0
- package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
- package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
- package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
- package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-CeMTqagR.js} +1 -1
- package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BAkquZAV.js} +1 -1
- package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-BsG3LV8i.js} +1 -1
- package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-7jKWpJBD.js} +1 -1
- package/dist/web/assets/{soy-CGGf1G43.js → soy-Bxm9Ez55.js} +1 -1
- package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
- package/dist/web/assets/{sparql-tQHB50VD.js → sparql-C1phwdLc.js} +1 -1
- package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
- package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
- package/dist/web/assets/{stata-DVQNLMcR.js → stata-DGNMQdEb.js} +1 -1
- package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-ByVrV320.js} +1 -1
- package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-CNWjIMhl.js} +1 -1
- package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
- package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
- package/dist/web/assets/system-monitor-window-content-BOjq53uU.js +1 -0
- package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-CW_dlYlW.js} +1 -1
- package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-PZsP5ZwG.js} +1 -1
- package/dist/web/assets/{table-BXDyImqk.js → table-CRJsmfeg.js} +1 -1
- package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
- package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
- package/dist/web/assets/{templ-CPt8AS4Q.js → templ-Cf0gQL4I.js} +1 -1
- package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-HXZkicIc.js} +3 -3
- package/dist/web/assets/{tex-BrTByT7i.js → tex-BTIqPw_R.js} +1 -1
- package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-oiekm5SB.js} +1 -1
- package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
- package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags-B8dN_Yf6.js} +1 -1
- package/dist/web/assets/{twig-DW9qc1bf.js → twig-BgemonbJ.js} +1 -1
- package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
- package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Gfi69PtP.js} +1 -1
- package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
- package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-CYW3FJFE.js} +1 -1
- package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
- package/dist/web/assets/use-open-team-member-CruUTqzD.js +1 -0
- package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
- package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-rnvuooQZ.js} +1 -1
- package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-DetaY8OI.js} +1 -1
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
- package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
- package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
- package/dist/web/assets/video-preview-C0uH2228.js +1 -0
- package/dist/web/assets/{vue-CJNDL1g-.js → vue-D3D4lbXz.js} +1 -1
- package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-DYEQFAiK.js} +1 -1
- package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-D0OPNscN.js} +1 -1
- package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-BEN0QNSW.js} +1 -1
- package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
- package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
- package/dist/web/assets/x-YDu4m2Iq.js +1 -0
- package/dist/web/assets/{xml-DTOm5niz.js → xml-DHaWgpya.js} +1 -1
- package/dist/web/assets/{xsl-BD_kBC04.js → xsl-DVRN8JsA.js} +1 -1
- package/dist/web/index.html +42 -65
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +9 -5
- package/docs/lessons-learned.md +11 -0
- package/package.json +2 -2
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- package/scripts/check-ppm-dir-usage.sh +0 -0
- package/scripts/install.sh +0 -0
- package/scripts/release.sh +0 -0
- package/src/providers/claude-agent-sdk.ts +51 -43
- package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
- package/src/providers/codex-app-server/codex-history.ts +24 -0
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
- package/src/providers/codex-app-server/codex-protocol.ts +24 -0
- package/src/providers/codex-app-server/codex-provider.ts +166 -18
- package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
- package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
- package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
- package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
- package/src/server/routes/chat.ts +95 -12
- package/src/server/routes/codex-accounts.ts +9 -5
- package/src/server/ws/chat.ts +55 -5
- package/src/services/claude-usage-source.ts +40 -0
- package/src/services/claude-usage.service.ts +77 -146
- package/src/services/codex-account-login.ts +123 -55
- package/src/services/codex-account.service.ts +27 -4
- package/src/services/db.service.ts +161 -20
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-credential-path-guard.ts +35 -8
- package/src/services/fs-path-guard.service.ts +1 -0
- package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
- package/src/services/named-tunnel/network-ready.ts +63 -0
- package/src/services/postgres.service.ts +144 -72
- package/src/services/provider-usage/index.ts +63 -0
- package/src/services/provider-usage/register-usage-sources.ts +18 -0
- package/src/services/provider-usage/usage-memory-cache.ts +71 -0
- package/src/services/provider-usage/usage-registry.ts +157 -0
- package/src/services/provider-usage/usage-scheduler.ts +117 -0
- package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
- package/src/services/provider-usage/usage-source.ts +82 -0
- package/src/services/slash-discovery/builtin-commands.ts +13 -0
- package/src/services/slash-discovery/codex-skill-items.ts +57 -0
- package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
- package/src/services/slash-discovery/types.ts +19 -0
- package/src/services/supervisor.ts +98 -0
- package/src/services/transcript-images.ts +14 -7
- package/src/services/upgrade.service.ts +7 -2
- package/src/services/windows-process-tree.ts +351 -351
- package/src/shared/image-limits.ts +37 -0
- package/src/types/api.ts +3 -1
- package/src/types/chat.ts +13 -1
- package/src/types/extension-messages.ts +2 -0
- package/src/web/app.tsx +1 -7
- package/src/web/components/chat/attachment-chips.tsx +10 -0
- package/src/web/components/chat/chat-history-bar.tsx +10 -1
- package/src/web/components/chat/chat-tab.tsx +54 -14
- package/src/web/components/chat/message-input.tsx +113 -15
- package/src/web/components/chat/message-list.tsx +14 -0
- package/src/web/components/chat/session-debug-dialog.tsx +2 -1
- package/src/web/components/chat/slash-command-picker.tsx +22 -3
- package/src/web/components/chat/tool-cards.tsx +47 -4
- package/src/web/components/chat/usage-badge.tsx +43 -447
- package/src/web/components/chat/user-message-parse.ts +33 -11
- package/src/web/components/database/database-viewer.tsx +34 -11
- package/src/web/components/database/extract-query-table.ts +24 -0
- package/src/web/components/database/glide-data-grid.tsx +11 -4
- package/src/web/components/database/glide-grid-types.ts +4 -0
- package/src/web/components/database/split-sql-statements.ts +116 -0
- package/src/web/components/database/sql-completion-provider.ts +1 -26
- package/src/web/components/database/sql-query-editor.tsx +2 -1
- package/src/web/components/database/use-database.ts +16 -1
- package/src/web/components/database/use-glide-cell-content.ts +11 -6
- package/src/web/components/editor/code-editor.tsx +30 -56
- package/src/web/components/floating-window/window-content-registry.ts +2 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/layout/command-palette.tsx +2 -2
- package/src/web/components/layout/mobile-drawer.tsx +5 -4
- package/src/web/components/layout/mobile-nav.tsx +5 -1
- package/src/web/components/layout/nav-section-rail.tsx +2 -1
- package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
- package/src/web/components/layout/sidebar.tsx +0 -2
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
- package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
- package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
- package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
- package/src/web/components/settings/accounts/account-card.tsx +127 -0
- package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
- package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
- package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
- package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
- package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
- package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
- package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
- package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
- package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
- package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
- package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
- package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
- package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
- package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
- package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
- package/src/web/components/settings/ai-settings-section.tsx +14 -4
- package/src/web/components/settings/appearance-settings-section.tsx +125 -0
- package/src/web/components/settings/general-settings-section.tsx +87 -0
- package/src/web/components/settings/jira-watcher-section.tsx +34 -0
- package/src/web/components/settings/notifications-settings-section.tsx +37 -0
- package/src/web/components/settings/open-settings.ts +42 -0
- package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
- package/src/web/components/settings/settings-body.tsx +110 -0
- package/src/web/components/settings/settings-categories.ts +102 -0
- package/src/web/components/settings/settings-category-list.tsx +56 -0
- package/src/web/components/settings/settings-category-rail.tsx +62 -0
- package/src/web/components/settings/settings-section-content.tsx +43 -0
- package/src/web/components/settings/settings-tab.tsx +15 -329
- package/src/web/components/settings/settings-window-content.tsx +24 -0
- package/src/web/hooks/use-chat.ts +44 -4
- package/src/web/hooks/use-global-keybindings.ts +3 -4
- package/src/web/hooks/use-url-sync.ts +30 -0
- package/src/web/hooks/use-usage.ts +23 -11
- package/src/web/lib/attachment-marker-split.ts +44 -0
- package/src/web/lib/image-animation.ts +51 -0
- package/src/web/lib/image-resize-limits.ts +47 -0
- package/src/web/lib/image-resize.ts +133 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
- package/src/web/lib/slash-items-cache.ts +27 -7
- package/src/web/stores/panel-utils.ts +11 -0
- package/src/web/stores/settings-store.ts +4 -2
- package/src/web/stores/window-panel-actions.ts +1 -3
- package/src/web/stores/window-panel-persistence.ts +9 -4
- package/dist/web/assets/adaptive-context-menu-JfuUSqbe.js +0 -1
- package/dist/web/assets/ai-resource-editor-MfdJKVM5.js +0 -1
- package/dist/web/assets/ai-settings-section-CJE7jFsj.js +0 -1
- package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
- package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
- package/dist/web/assets/api-settings-CPg4yGvS.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
- package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
- package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
- package/dist/web/assets/circle-4g2coECH.js +0 -1
- package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
- package/dist/web/assets/clock-DbiNiwCG.js +0 -1
- package/dist/web/assets/code-DdKXqtf_.js +0 -1
- package/dist/web/assets/code-editor-COTa37oe.js +0 -10
- package/dist/web/assets/context-menu-BEAa1qhd.js +0 -1
- package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
- package/dist/web/assets/dialog-DrL3WEXd.js +0 -1
- package/dist/web/assets/diff-viewer-CT0lmDc9.js +0 -4
- package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
- package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
- package/dist/web/assets/extension-store-ClQpnxQr.js +0 -1
- package/dist/web/assets/external-link-ojDHcQxQ.js +0 -1
- package/dist/web/assets/eye-off-Rq1n2-oe.js +0 -1
- package/dist/web/assets/file-store-28e44KP3.js +0 -1
- package/dist/web/assets/git-log-panel-D1-YXZ0O.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
- package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
- package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
- package/dist/web/assets/index-BkHlMfPD.js +0 -52
- package/dist/web/assets/index-SFFv2-VM.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
- package/dist/web/assets/input-DTmI80l2.js +0 -1
- package/dist/web/assets/keybindings-store-Bi0q9x4Q.js +0 -1
- package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
- package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
- package/dist/web/assets/lock-BEtt_eTO.js +0 -1
- package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
- package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
- package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
- package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +0 -1
- package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
- package/dist/web/assets/panel-store-Dv28vz3L.js +0 -1
- package/dist/web/assets/pdf-preview-DKeIIJTv.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
- package/dist/web/assets/play-CPbVzGgq.js +0 -1
- package/dist/web/assets/plus-BxqD2FIZ.js +0 -1
- package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
- package/dist/web/assets/refresh-cw-BALzw7L3.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +0 -1
- package/dist/web/assets/rotate-ccw-CuvNBM80.js +0 -1
- package/dist/web/assets/scroll-area-B5yhPpZo.js +0 -1
- package/dist/web/assets/send-Cits8q0l.js +0 -1
- package/dist/web/assets/settings-store-Dp4i8zeA.js +0 -2
- package/dist/web/assets/settings-tab-BtMvvKAF.js +0 -3
- package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
- package/dist/web/assets/shallow-BBJ_qoEF.js +0 -1
- package/dist/web/assets/sql-query-editor-DZqnYIB7.js +0 -1
- package/dist/web/assets/sqlite-viewer-DXutos0e.js +0 -1
- package/dist/web/assets/switch-BPol3BFC.js +0 -1
- package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +0 -2
- package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
- package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
- package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
- package/dist/web/assets/team-member-window-content-BJ_XFw8-.js +0 -1
- package/dist/web/assets/tool-cards-pIu_cmEL.js +0 -8
- package/dist/web/assets/trash-2-BjShZ7if.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
- package/dist/web/assets/triangle-alert-qQ6Plydp.js +0 -1
- package/dist/web/assets/upload-7Zs6oaK5.js +0 -1
- package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +0 -1
- package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +0 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
- package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
- package/dist/web/assets/window-store-CNzkeUyO.js +0 -1
- package/dist/web/assets/x-Dzu9ecti.js +0 -1
- package/src/web/components/chat/account-dialogs.tsx +0 -384
- package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
- package/src/web/components/settings/codex-accounts-section.tsx +0 -198
- package/src/web/components/settings/telegram-settings-section.tsx +0 -113
- /package/dist/web/assets/{abap-Cjj2olMr.js → abap-h3QyH8MW.js} +0 -0
- /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-mkxkq-FN.js} +0 -0
- /package/dist/web/assets/{ada-AEWvZXuX.js → ada-DhISK_EH.js} +0 -0
- /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DnlWWesX.js} +0 -0
- /package/dist/web/assets/{apache-DjvX0RSe.js → apache-P5AK3sfA.js} +0 -0
- /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CJUPehg2.js} +0 -0
- /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-Dx6GGbay.js} +0 -0
- /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
- /package/dist/web/assets/{ara-iGt2zixk.js → ara-95Kx3qKb.js} +0 -0
- /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-Dp0RbIej.js} +0 -0
- /package/dist/web/assets/{asm-B-T0eqhE.js → asm-C82coUjU.js} +0 -0
- /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BFc3zeop.js} +0 -0
- /package/dist/web/assets/{awk-CZwfwpPi.js → awk-ChjC0icr.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-DYfUf_PB.js} +0 -0
- /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-Bh6O0plO.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-DlNhHTGq.js} +0 -0
- /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-Ceoll3Sk.js} +0 -0
- /package/dist/web/assets/{bat-DqKcJTUn.js → bat-C7QS6Lfq.js} +0 -0
- /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-5NMEAZgs.js} +0 -0
- /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBL83Vbk.js} +0 -0
- /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-L7rPkPSG.js} +0 -0
- /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-BOwHIINh.js} +0 -0
- /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-CWLsMom2.js} +0 -0
- /package/dist/web/assets/{c-AjOLBXIs.js → c-CD4Ku5Cr.js} +0 -0
- /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-CpMl-w3G.js} +0 -0
- /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-CvgF6ehN.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-DoiMCU_7.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
- /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-DXYM8-SA.js} +0 -0
- /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-DzamsL69.js} +0 -0
- /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-CR6RP_T_.js} +0 -0
- /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-D1s6-IyY.js} +0 -0
- /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-DAqNWMkS.js} +0 -0
- /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-8e-KtitB.js} +0 -0
- /package/dist/web/assets/{coq-EAW-1nw0.js → coq-UEq0WUcO.js} +0 -0
- /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-wxJ0TuCm.js} +0 -0
- /package/dist/web/assets/{css-Cr27CfPY.js → css-Cerub-yB.js} +0 -0
- /package/dist/web/assets/{csv-B5VtaPtH.js → csv-Bh9_WD_h.js} +0 -0
- /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
- /package/dist/web/assets/{cue-jm6lYNrx.js → cue-rzM1ztqU.js} +0 -0
- /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-DGaTQ8m0.js} +0 -0
- /package/dist/web/assets/{d-CgG1_yzd.js → d-DbkZGuhB.js} +0 -0
- /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-BO2bzZcA.js} +0 -0
- /package/dist/web/assets/{dart-BEVoLlWg.js → dart-BDhCoqVg.js} +0 -0
- /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-Bh03stWh.js} +0 -0
- /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-BOdr_fwq.js} +0 -0
- /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Bb_gTsS2.js} +0 -0
- /package/dist/web/assets/{dist-CJJKpgR7.js → dist-BUMDu0av.js} +0 -0
- /package/dist/web/assets/{docker-COBitpld.js → docker-BDsTF5ce.js} +0 -0
- /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-CbCWp9Ti.js} +0 -0
- /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-Cd1fxvoX.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-B7eXkfXG.js} +0 -0
- /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-XlzuS21R.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-CXV1Ki_7.js} +0 -0
- /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-DuqR60Dg.js} +0 -0
- /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-q4BPnenD.js} +0 -0
- /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-B-e0TX2Q.js} +0 -0
- /package/dist/web/assets/{fish-B04oqgKk.js → fish-Bzb6-TtF.js} +0 -0
- /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-B-szH2-f.js} +0 -0
- /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-OS6SGYC0.js} +0 -0
- /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-FYq4OP2d.js} +0 -0
- /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-BpG6P8rw.js} +0 -0
- /package/dist/web/assets/{genie-BSIGRHu7.js → genie-BS9RLAna.js} +0 -0
- /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-B_9FdtGs.js} +0 -0
- /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-cfaIlnBV.js} +0 -0
- /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-B2Wv65He.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-CtM9X76s.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
- /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-DWEI10iG.js} +0 -0
- /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-DcJkK1YN.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
- /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-DVpiM6b2.js} +0 -0
- /package/dist/web/assets/{gn-BG5NvwAf.js → gn-B80DNc0i.js} +0 -0
- /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-D9i3MaT-.js} +0 -0
- /package/dist/web/assets/{go-DXvjjq4q.js → go-CwCBf_Pt.js} +0 -0
- /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-D7mEfwmW.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
- /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-CsXSYXRG.js} +0 -0
- /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-B1uyiI9t.js} +0 -0
- /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DlYKn5q9.js} +0 -0
- /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-C_0VMGJJ.js} +0 -0
- /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-zt2PEzbg.js} +0 -0
- /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-6wQfyOK0.js} +0 -0
- /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-CqI2qTim.js} +0 -0
- /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BljpHDoG.js} +0 -0
- /package/dist/web/assets/{hy-BALeMn3i.js → hy-XzuL55dw.js} +0 -0
- /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
- /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CqhhjBrP.js} +0 -0
- /package/dist/web/assets/{ini-DG7imsqe.js → ini-CiFDi6cY.js} +0 -0
- /package/dist/web/assets/{java-D5GTT474.js → java-OsQgTL5-.js} +0 -0
- /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-qWAOScxl.js} +0 -0
- /package/dist/web/assets/{json-B9FhwAgR.js → json-RiAKsaD1.js} +0 -0
- /package/dist/web/assets/{json5--Abk7MUy.js → json5-BeCnS3H2.js} +0 -0
- /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-CbTW1Mka.js} +0 -0
- /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-BLSi_669.js} +0 -0
- /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-C3xAggou.js} +0 -0
- /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-CLdiagC9.js} +0 -0
- /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-lYE1lb50.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
- /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-BfDV0825.js} +0 -0
- /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-DONxR4wD.js} +0 -0
- /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-BDP63Fh5.js} +0 -0
- /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-BeBrWApL.js} +0 -0
- /package/dist/web/assets/{lean-Bgt24V1D.js → lean-DOZc0N1A.js} +0 -0
- /package/dist/web/assets/{less-Bk_UaqsM.js → less-C3H9NlDv.js} +0 -0
- /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
- /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-C_kTL608.js} +0 -0
- /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-Dsp27U3P.js} +0 -0
- /package/dist/web/assets/{log-CKm0CxB1.js → log-BywQaTCy.js} +0 -0
- /package/dist/web/assets/{logo-DFP1_5U6.js → logo-BirJgciO.js} +0 -0
- /package/dist/web/assets/{luau-BcZnoyh3.js → luau-Btrz5kJB.js} +0 -0
- /package/dist/web/assets/{make-CpNSwgwT.js → make-Kl7nFQrS.js} +0 -0
- /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-qsC4P8S-.js} +0 -0
- /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-CC__ui3p.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-D4FxG2FZ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-zGqiWcxn.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CZIdTTB1.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-CEIs0GcK.js} +0 -0
- /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-Cf0aUjN9.js} +0 -0
- /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-CIsXjH86.js} +0 -0
- /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-B06WO9Qc.js} +0 -0
- /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-z8fyp0FT.js} +0 -0
- /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-Dr-qesbW.js} +0 -0
- /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-CWHzYLcE.js} +0 -0
- /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D9zXrKmv.js} +0 -0
- /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-4b-AKOpX.js} +0 -0
- /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-Dl4dhOZJ.js} +0 -0
- /package/dist/web/assets/{move-DItn61bH.js → move-DTYKJmXy.js} +0 -0
- /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-DnvXOS-o.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-Di8QnEXg.js} +0 -0
- /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-Czh0yq-H.js} +0 -0
- /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-D_OpZqOR.js} +0 -0
- /package/dist/web/assets/{nix-sLgl9zGF.js → nix-2MvcjpK3.js} +0 -0
- /package/dist/web/assets/{nord-TWkJPI6M.js → nord-CG_r4cyk.js} +0 -0
- /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-DINskMcU.js} +0 -0
- /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-Dv7BIicR.js} +0 -0
- /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-CpqsCxPb.js} +0 -0
- /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-DwTsYSmW.js} +0 -0
- /package/dist/web/assets/{odin-BJjlKpkM.js → odin-G-4O4k4t.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-13Pv5SjI.js} +0 -0
- /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-Bi2e86mS.js} +0 -0
- /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-D0WsOTij.js} +0 -0
- /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-DmDStuXr.js} +0 -0
- /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-Bl6Qjtuo.js} +0 -0
- /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-UeSVSjTI.js} +0 -0
- /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-DreFC689.js} +0 -0
- /package/dist/web/assets/{po-_kXpd_0n.js → po-ZIjccxsX.js} +0 -0
- /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CvQHJPrI.js} +0 -0
- /package/dist/web/assets/{polar-DV_1L04z.js → polar-BrMB0Kgh.js} +0 -0
- /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-pgUsil1T.js} +0 -0
- /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CTsZkCg_.js} +0 -0
- /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CcwVuBAp.js} +0 -0
- /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-spjnAI5W.js} +0 -0
- /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-ChCkZpDO.js} +0 -0
- /package/dist/web/assets/{proto-B5meVsrA.js → proto-wVGqK4gB.js} +0 -0
- /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-CED7iwPQ.js} +0 -0
- /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-u9lGiQKV.js} +0 -0
- /package/dist/web/assets/{python-DeTIYd5V.js → python-DeqTx6ef.js} +0 -0
- /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-BH1uoPrX.js} +0 -0
- /package/dist/web/assets/{qss-YRbeNtr0.js → qss-CUVKA_13.js} +0 -0
- /package/dist/web/assets/{r-DHwHK0Jh.js → r-BqdPzqYH.js} +0 -0
- /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-C2eTgPN3.js} +0 -0
- /package/dist/web/assets/{raku-DWTmAj3A.js → raku-uAay1sWx.js} +0 -0
- /package/dist/web/assets/{red-BFuHRZFS.js → red-DTZP1ZfC.js} +0 -0
- /package/dist/web/assets/{reg-DYoOF5tH.js → reg-Cdk3YsxY.js} +0 -0
- /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-Bpa2aS7K.js} +0 -0
- /package/dist/web/assets/{rel-Z_tTruCU.js → rel-bNsb1-3L.js} +0 -0
- /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
- /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-DSaubByW.js} +0 -0
- /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-DbQzYM6i.js} +0 -0
- /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-C7RsprTu.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-VP976YzL.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-XHZNhOqd.js} +0 -0
- /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-BVQlx0Lj.js} +0 -0
- /package/dist/web/assets/{rust-Dx86yfCi.js → rust-VHaQ3rh3.js} +0 -0
- /package/dist/web/assets/{sass-BwwVhdT9.js → sass-CO-kMkwD.js} +0 -0
- /package/dist/web/assets/{scala-WJqNEN-w.js → scala-cY0GXkiz.js} +0 -0
- /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-D4LNZE0X.js} +0 -0
- /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-Bh2Fd2cn.js} +0 -0
- /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-KKclmCnt.js} +0 -0
- /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-D6YQ-fqf.js} +0 -0
- /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-CDf66CDj.js} +0 -0
- /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-D2OH_QIl.js} +0 -0
- /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-CQeF8G1K.js} +0 -0
- /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-LTPf1iLZ.js} +0 -0
- /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-Ose1n88k.js} +0 -0
- /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-lFuOMHki.js} +0 -0
- /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Bov-sB5S.js} +0 -0
- /package/dist/web/assets/{sql-DIwJJDU-.js → sql-BKyZ6wmk.js} +0 -0
- /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-CqJoMGMb.js} +0 -0
- /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Bf5pWRLI.js} +0 -0
- /package/dist/web/assets/{swift-X8oWpSeU.js → swift-D5d1hn_u.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-B4c6YAgO.js} +0 -0
- /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-D7fXyDsI.js} +0 -0
- /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-oNagzlBK.js} +0 -0
- /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-C6Nqm9Dm.js} +0 -0
- /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-CaWy-cBX.js} +0 -0
- /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-C435P5Mq.js} +0 -0
- /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CVDxqdPf.js} +0 -0
- /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-Uhs17tQ3.js} +0 -0
- /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-j6fPOJ6P.js} +0 -0
- /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-h78O6xrL.js} +0 -0
- /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-Bol-vCrQ.js} +0 -0
- /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Dw8eFf04.js} +0 -0
- /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-Dtz7DPci.js} +0 -0
- /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-BIOQ7dMP.js} +0 -0
- /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-CoaGMjzu.js} +0 -0
- /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-Bf2hKAMu.js → v-BoqFdpJ7.js} +0 -0
- /package/dist/web/assets/{vala-Cbete-UE.js → vala-COBpNnCC.js} +0 -0
- /package/dist/web/assets/{vb-CA-xWAcX.js → vb-DxI86znh.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
- /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-DcTAamuQ.js} +0 -0
- /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-DO298JSv.js} +0 -0
- /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-Dq-bDVSN.js} +0 -0
- /package/dist/web/assets/{viml-BePrIAs8.js → viml-zHTRWuQh.js} +0 -0
- /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-jlYMJCBf.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-DS0sUlSZ.js} +0 -0
- /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-HSeZ2YhG.js} +0 -0
- /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-BQfKVO3M.js} +0 -0
- /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
- /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-B6mCuI8h.js} +0 -0
- /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-CfpS05sP.js} +0 -0
- /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-14GDsWRD.js} +0 -0
- /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-Ccbq5EVX.js} +0 -0
- /package/dist/web/assets/{wit-C9yyX6-U.js → wit-C19hb3R5.js} +0 -0
- /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-B-ZSwGwP.js} +0 -0
- /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-mw_JUeio.js} +0 -0
- /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-WbzDRjvF.js} +0 -0
- /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
|
-
/**
|
|
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
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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:
|
|
115
|
+
return { files: [singleMatch[1]!], text: rest.slice(singleMatch[0].length) };
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
const multiMatch =
|
|
118
|
+
const multiMatch = rest.match(/^\[Attached files:\n([\s\S]+?)\]\n*/);
|
|
97
119
|
if (multiMatch) {
|
|
98
|
-
const
|
|
99
|
-
return { files, text:
|
|
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:
|
|
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}
|
|
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
|
-
/**
|
|
266
|
-
|
|
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
|
|
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
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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}
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
-
|
|
572
|
-
|
|
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";
|