@hienlh/ppm 0.17.14 → 0.17.16
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 +14 -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 +1 -1
- package/dist/web/assets/{ai-resource-editor-DfCN7LjR.js → ai-resource-editor-CsossqDO.js} +1 -1
- package/dist/web/assets/{angular-html-DkR4iCth.js → angular-html-CkxY2iBQ.js} +1 -1
- package/dist/web/assets/{angular-ts-DcyL4Chx.js → angular-ts-BePeGkkC.js} +1 -1
- package/dist/web/assets/{apl-BVlrVtjt.js → apl-DBS7PWvB.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-NAS99xy0.js +1 -0
- package/dist/web/assets/{astro-MjJlozLj.js → astro-Clbk1k4h.js} +1 -1
- package/dist/web/assets/{audio-preview-DbFUkIzm.js → audio-preview-DIkokvPM.js} +1 -1
- package/dist/web/assets/{blade-DnJWi1fz.js → blade-BeBsBuGS.js} +1 -1
- package/dist/web/assets/{bsl-DCmJxSIz.js → bsl-DYigT3JZ.js} +1 -1
- package/dist/web/assets/{cairo-C8dDenub.js → cairo-BjpO78wI.js} +1 -1
- package/dist/web/assets/chat-tab-Bh6l253v.js +18 -0
- package/dist/web/assets/click-outside-container-DXZVmhaX.js +1 -0
- package/dist/web/assets/{cobol-DPx_nifl.js → cobol-CpuUx69s.js} +1 -1
- package/dist/web/assets/code-editor-B7F-d4DK.js +10 -0
- package/dist/web/assets/{coffee-DvFzqhgN.js → coffee-FoFd2Cxo.js} +1 -1
- package/dist/web/assets/{conflict-editor-BAAffNdj.js → conflict-editor-Dqm1bkyL.js} +3 -3
- package/dist/web/assets/{cpp-BLJN0umi.js → cpp-CO_nPfdr.js} +1 -1
- package/dist/web/assets/{crystal-BXR-9fGw.js → crystal-D-UygAAF.js} +1 -1
- package/dist/web/assets/{csv-preview-DzhSC0fd.js → csv-preview-CvP4xzug.js} +1 -1
- package/dist/web/assets/data-grid-overlay-editor-CUlFsD-D.js +1 -0
- package/dist/web/assets/database-viewer-5oNTPgjF.js +1 -0
- package/dist/web/assets/{diff-viewer-DvXxlk47.js → diff-viewer-BfWixe_z.js} +1 -1
- package/dist/web/assets/dist-BsziT-tR.js +11 -0
- package/dist/web/assets/{docx-preview-DfjH-dIr.js → docx-preview-CguUoTKe.js} +1 -1
- package/dist/web/assets/{edge-rWOlCmXT.js → edge-BSzxzX11.js} +1 -1
- package/dist/web/assets/{elixir-E7Y82okW.js → elixir-DH1-Mrep.js} +1 -1
- package/dist/web/assets/{elm-DTPGbc-w.js → elm-B0ln5J70.js} +1 -1
- package/dist/web/assets/{erb-DHqLHrE6.js → erb-GIz6NnlZ.js} +1 -1
- package/dist/web/assets/{erlang-Byg0nNiV.js → erlang-B8MgYhib.js} +1 -1
- package/dist/web/assets/esm-CjVeOTNQ.js +2 -0
- package/dist/web/assets/{extension-webview-wuMQ8pD7.js → extension-webview-DTpFt1eL.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CY1n0Xf6.js → fortran-fixed-form-D_-TrK6U.js} +1 -1
- package/dist/web/assets/{fsharp-_vEDSCJz.js → fsharp-C9cLCk2b.js} +1 -1
- package/dist/web/assets/{gdresource-B6XT0wAC.js → gdresource-CsZ3lXwY.js} +1 -1
- package/dist/web/assets/{git-commit-Wk9ovq59.js → git-commit-xmq0KqrA.js} +1 -1
- package/dist/web/assets/{git-log-panel-SPPYmbCr.js → git-log-panel-DUX7N5tN.js} +1 -1
- package/dist/web/assets/{git-rebase-DSarGPvK.js → git-rebase-D3O-jsSX.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-B4I3L4Sf.js +1 -0
- package/dist/web/assets/glide-data-grid-7vB1SNne.js +138 -0
- package/dist/web/assets/{glimmer-js-CkYI35_P.js → glimmer-js-g74gESrC.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BqmIZcds.js → glimmer-ts-DF4aDWHT.js} +1 -1
- package/dist/web/assets/{glsl-Be-yBCF5.js → glsl-D5LmfW7l.js} +1 -1
- package/dist/web/assets/{graphql-a5DrENHY.js → graphql-_KI4RuXL.js} +1 -1
- package/dist/web/assets/group-chat-tab-CNECY-zg.js +1 -0
- package/dist/web/assets/{hack-BLAg20aD.js → hack-B1NaTC39.js} +1 -1
- package/dist/web/assets/{haml-Dj6bGOQH.js → haml-DDFXadZ4.js} +1 -1
- package/dist/web/assets/{handlebars-dmEa36Oh.js → handlebars-Bp-Jq3K9.js} +1 -1
- package/dist/web/assets/{html-yacZYzSE.js → html-DyB9_5Tj.js} +1 -1
- package/dist/web/assets/{html-derivative-lLi3-RNT.js → html-derivative-CC60wi-3.js} +1 -1
- package/dist/web/assets/{http-1KaxcE4e.js → http-C-9qngH4.js} +1 -1
- package/dist/web/assets/{hurl-ST-dEEZR.js → hurl-BtxgpNDs.js} +1 -1
- package/dist/web/assets/{hxml-CpDtuq__.js → hxml-DOoOS9op.js} +1 -1
- package/dist/web/assets/{image-preview-D_8o6OLx.js → image-preview-CZg6FQ3w.js} +1 -1
- package/dist/web/assets/{index-C1Q59prB.js → index-D2G6kmTs.js} +7 -7
- package/dist/web/assets/index-DCmrZnq0.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-CBbbOe3C.js +1 -0
- package/dist/web/assets/{jinja-Cjw-evAn.js → jinja-cN-150ao.js} +1 -1
- package/dist/web/assets/{jison-DfATMYoY.js → jison-CqjsqPq5.js} +1 -1
- package/dist/web/assets/{julia-DDCaxiqT.js → julia-C-I5m8W4.js} +1 -1
- package/dist/web/assets/{just-CJL529u6.js → just-CgsSW9zs.js} +1 -1
- package/dist/web/assets/keybindings-store-D8Cppv4w.js +1 -0
- package/dist/web/assets/{latex-Zc4EpLpW.js → latex-BFzR9N2d.js} +1 -1
- package/dist/web/assets/{liquid-B_6KMNYS.js → liquid-BWfr-_iC.js} +1 -1
- package/dist/web/assets/{lua-BtS2idK7.js → lua-DsXVun6g.js} +1 -1
- package/dist/web/assets/{markdown-renderer-DVVx3WCU.js → markdown-renderer-CiQkvWP_.js} +3 -3
- package/dist/web/assets/{marko-Wk6LBnck.js → marko-DWBbo09n.js} +1 -1
- package/dist/web/assets/{mdc-cRaXBrzL.js → mdc-CUMe9LOG.js} +1 -1
- package/dist/web/assets/{nextflow-Bos2wvDV.js → nextflow-CoYbADOm.js} +1 -1
- package/dist/web/assets/{nginx-DMlOwzdr.js → nginx-z-JRZREw.js} +1 -1
- package/dist/web/assets/{nim-D2dZQA88.js → nim-Ci4LHyBm.js} +1 -1
- package/dist/web/assets/notification-store-DOGxkWih.js +1 -0
- package/dist/web/assets/{number-overlay-editor-Du_UgiD3.js → number-overlay-editor-lnnuUpPI.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-fr8-QSTF.js +1 -0
- package/dist/web/assets/{pdf-preview-HTC73FyK.js → pdf-preview-B6ZqANpw.js} +1 -1
- package/dist/web/assets/{perl-C5-Z6fPn.js → perl-pbDimKOO.js} +1 -1
- package/dist/web/assets/{php-BXNNhkMH.js → php-B003E5v7.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-Biu7_Pbb.js +1 -0
- package/dist/web/assets/postgres-viewer-B23xTCSs.js +3 -0
- package/dist/web/assets/{pug-C1zf1yWy.js → pug-AoueAWJo.js} +1 -1
- package/dist/web/assets/{qml-OW58jGpD.js → qml-CepxioQA.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-CMtNRpK6.js +1 -0
- package/dist/web/assets/{razor-DzjSaobM.js → razor-lIvU-Gu-.js} +1 -1
- package/dist/web/assets/{rst-RfuihOUW.js → rst-Dn-dwElE.js} +1 -1
- package/dist/web/assets/{ruby-CNO3pli1.js → ruby-DmsD1Iz5.js} +1 -1
- package/dist/web/assets/{sas-DZY5BtXx.js → sas-6IM2P4YP.js} +1 -1
- package/dist/web/assets/{scss--4g-REcN.js → scss-RgYEZQqh.js} +1 -1
- package/dist/web/assets/{settings-store-mWnY4Kt0.js → settings-store-BkSf4SPQ.js} +2 -2
- package/dist/web/assets/settings-tab-D5hAA1bY.js +1 -0
- package/dist/web/assets/{shaderlab-Cu4sBiVn.js → shaderlab-B0MNEwt4.js} +1 -1
- package/dist/web/assets/{shellsession-DWzJfEv7.js → shellsession-F8S4LrQI.js} +1 -1
- package/dist/web/assets/{soy-BnqE-5Ff.js → soy-DDlMbX7u.js} +1 -1
- package/dist/web/assets/{sparql-Ctg__NhZ.js → sparql-kthmGAAe.js} +1 -1
- package/dist/web/assets/{sql-query-editor-CJdpO95g.js → sql-query-editor-BYNM0RRl.js} +1 -1
- package/dist/web/assets/sqlite-viewer-DW9f2Hqj.js +1 -0
- package/dist/web/assets/{stata-Qk2h66XW.js → stata-Tfbew8La.js} +1 -1
- package/dist/web/assets/{surrealql-8QEX7EfH.js → surrealql-C0LIGqRx.js} +1 -1
- package/dist/web/assets/{svelte-D_mubC5-.js → svelte-CfTy8CGF.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-BA9gqJRW.js → system-monitor-tab-B5f4pgdX.js} +1 -1
- package/dist/web/assets/{templ-C1jV0fsO.js → templ-V5H1Sbfy.js} +1 -1
- package/dist/web/assets/{terminal-tab-DWncjHEA.js → terminal-tab-CK3lX-0Y.js} +2 -2
- package/dist/web/assets/{tex-D_StFXy_.js → tex-C3H3uMZf.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-ZYVFjoaN.js +1 -0
- package/dist/web/assets/{ts-tags-B5m47rEm.js → ts-tags-7AXgG5YS.js} +1 -1
- package/dist/web/assets/{twig-C_br6GIl.js → twig-BSyS38T3.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-Ch2jRiGj.js → use-monaco-theme-Jj2OxkAE.js} +1 -1
- package/dist/web/assets/{use-websocket-MvxbuTdy.js → use-websocket-DrDBgIWU.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-DP1CEcGn.js → vendor-mermaid-DGOXKrJC.js} +193 -193
- package/dist/web/assets/{video-preview-rWrNCfZX.js → video-preview-BOHxkwKG.js} +1 -1
- package/dist/web/assets/{vue-DQwCnLdr.js → vue-C4d_br9u.js} +1 -1
- package/dist/web/assets/{vue-html-T_EoGpPJ.js → vue-html-CGdoz4vW.js} +1 -1
- package/dist/web/assets/{vue-vine-DmxLqX50.js → vue-vine-CqNolQlN.js} +1 -1
- package/dist/web/assets/{xml-Bs6Bw8is.js → xml-UmCmu-Od.js} +1 -1
- package/dist/web/assets/{xsl-f5ZNhRCc.js → xsl-DJG6USGh.js} +1 -1
- package/dist/web/index.html +14 -14
- 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/package.json +1 -1
- package/schemas/ppm-config.schema.json +3 -3
- package/src/cli/commands/init.ts +8 -5
- package/src/providers/claude-agent-sdk-query-options.ts +57 -0
- package/src/providers/claude-agent-sdk.ts +21 -9
- package/src/server/ws/chat.ts +91 -10
- package/src/services/account.service.ts +13 -7
- package/src/services/db.service.ts +31 -1
- package/src/types/api.ts +4 -2
- package/src/types/chat.ts +8 -0
- package/src/types/config.ts +4 -9
- package/src/web/components/chat/chat-tab.tsx +8 -0
- package/src/web/components/chat/message-input.tsx +27 -7
- package/src/web/components/chat/message-list.tsx +48 -3
- package/src/web/components/chat/model-thinking-selector-helpers.ts +33 -0
- package/src/web/components/chat/model-thinking-selector.tsx +189 -0
- package/src/web/components/settings/ai-settings-section.tsx +2 -0
- package/src/web/components/settings/proxy-test-section.tsx +3 -1
- package/src/web/hooks/use-chat.ts +45 -0
- package/C:/Users/PC/AppData/Local/Temp/ppmbuild/index.js +0 -118043
- package/dist/web/assets/architecture-PBZL5I3N-CqTF9Xn6.js +0 -1
- package/dist/web/assets/chat-tab-C6nMDb9l.js +0 -18
- package/dist/web/assets/code-editor-C38IIs_y.js +0 -10
- package/dist/web/assets/data-grid-overlay-editor-cOzDcGkl.js +0 -1
- package/dist/web/assets/data-grid-types-CWHnFOc-.js +0 -1
- package/dist/web/assets/database-viewer-DxmL2veb.js +0 -1
- package/dist/web/assets/dist-B5ErTtYs.js +0 -11
- package/dist/web/assets/esm-DU7axMjq.js +0 -2
- package/dist/web/assets/gitGraph-HDMCJU4V-X12vfQ3i.js +0 -1
- package/dist/web/assets/glide-data-grid-BC0AHzMn.js +0 -138
- package/dist/web/assets/group-chat-tab-D20bF8q5.js +0 -1
- package/dist/web/assets/index-CMJ6whqq.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-Bcz-Qhua.js +0 -1
- package/dist/web/assets/keybindings-store-TINaziU9.js +0 -1
- package/dist/web/assets/notification-store-DN8oDV7o.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-BTwjB70W.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-Cn-lrQYZ.js +0 -1
- package/dist/web/assets/postgres-viewer-pWjwP3GD.js +0 -3
- package/dist/web/assets/radar-KQ55EAFF-Cmppm_Ia.js +0 -1
- package/dist/web/assets/settings-tab-BoRuss3O.js +0 -1
- package/dist/web/assets/sqlite-viewer-BgewX3tM.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-BLWus1wm.js +0 -1
- package/docs/journal/binary-self-upgrade-260724.md +0 -66
- package/nul/docs/claude-agent-sdk-reference.md +0 -780
- package/nul/docs/code-standards.md +0 -1080
- package/nul/docs/codebase-summary.md +0 -916
- package/nul/docs/deployment-guide.md +0 -702
- package/nul/docs/design-guidelines.md +0 -767
- package/nul/docs/extension-development-guide.md +0 -629
- package/nul/docs/journals/260414-1400-ext-git-graph-port-complete.md +0 -147
- package/nul/docs/journals/260414-1452-git-graph-faithful-port.md +0 -144
- package/nul/docs/journals/260414-1810-git-graph-ui-improvements-complete.md +0 -261
- package/nul/docs/journals/260414-2001-bundled-extensions.md +0 -219
- package/nul/docs/journals/260415-frontend-memory-optimization.md +0 -73
- package/nul/docs/lessons-learned.md +0 -70
- package/nul/docs/project-changelog.md +0 -574
- package/nul/docs/project-overview-pdr.md +0 -205
- package/nul/docs/project-roadmap.md +0 -222
- package/nul/docs/system-architecture.md +0 -1961
- 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/spike-group-channel.mjs +0 -156
- package/spike-group-chat.mjs +0 -181
- /package/dist/web/assets/{abap-tkptwpsK.js → abap-BZ3J5hF3.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Di9TMGwg.js → actionscript-3-CkyGfT-8.js} +0 -0
- /package/dist/web/assets/{ada-CB9F079D.js → ada-CxNLBoe7.js} +0 -0
- /package/dist/web/assets/{andromeeda-Dcl8jpb7.js → andromeeda-BPN5rsDz.js} +0 -0
- /package/dist/web/assets/{apache-D0Nk0qsH.js → apache-C1F5wHL6.js} +0 -0
- /package/dist/web/assets/{apex-mQIoWncD.js → apex-BaNixdO-.js} +0 -0
- /package/dist/web/assets/{api-themes-BdQCjXQk.js → api-themes-84MT-XqR.js} +0 -0
- /package/dist/web/assets/{applescript-C6ljbcsI.js → applescript-Cl_pdcch.js} +0 -0
- /package/dist/web/assets/{ara-WFqRPGPe.js → ara-B6UAE7M2.js} +0 -0
- /package/dist/web/assets/{asciidoc-bNd8w4nf.js → asciidoc-D9c9Twki.js} +0 -0
- /package/dist/web/assets/{asm-BQ1_F580.js → asm-CNmtv7Y9.js} +0 -0
- /package/dist/web/assets/{aurora-x-Ciqs_Dbu.js → aurora-x-C9z-NyzR.js} +0 -0
- /package/dist/web/assets/{awk-GCR-6KZV.js → awk-BfLHEnKa.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DxJXuj99.js → ayu-dark-Bn7trkt_.js} +0 -0
- /package/dist/web/assets/{ayu-light-jvSyJWDV.js → ayu-light-Dpya9hPf.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Cg3BPO6H.js → ayu-mirage-Cc0jfABD.js} +0 -0
- /package/dist/web/assets/{ballerina-BPz4CrX6.js → ballerina-BtiCmN7T.js} +0 -0
- /package/dist/web/assets/{bat-CSTs2hIG.js → bat-tmFM76zR.js} +0 -0
- /package/dist/web/assets/{beancount-DMateAZX.js → beancount-BjPqRrcH.js} +0 -0
- /package/dist/web/assets/{berry-9lXg0GRM.js → berry-D0SQBxN2.js} +0 -0
- /package/dist/web/assets/{bibtex-DIbqmvFg.js → bibtex-CkjRWQKr.js} +0 -0
- /package/dist/web/assets/{bicep-02WGsoJ8.js → bicep-Bhq28Cxc.js} +0 -0
- /package/dist/web/assets/{bird2-BY0tbu3k.js → bird2-DFGInYb3.js} +0 -0
- /package/dist/web/assets/{c-jMBX_9Je.js → c-CP8cB4k_.js} +0 -0
- /package/dist/web/assets/{c3-B8JVbNjW.js → c3-oRytyeWo.js} +0 -0
- /package/dist/web/assets/{cadence-DUIf1tvL.js → cadence-Inf1rGb7.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-FhUCVxex.js → catppuccin-frappe-DAzribQo.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CzfrtmgL.js → catppuccin-latte-B6Y_mCCQ.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-6xL0lOtj.js → catppuccin-macchiato-COLbYqVL.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-D39-xS3P.js → catppuccin-mocha-DKWaML1B.js} +0 -0
- /package/dist/web/assets/{clarity-D8TeGvoT.js → clarity-_ZLLrrsX.js} +0 -0
- /package/dist/web/assets/{clojure-Dx_acSX7.js → clojure-DWGVCg_P.js} +0 -0
- /package/dist/web/assets/{cmake-CyeYPq8o.js → cmake-Cwh3mug7.js} +0 -0
- /package/dist/web/assets/{codeowners-DrTI0lmI.js → codeowners-BdV6CFYi.js} +0 -0
- /package/dist/web/assets/{codeql-CPkFxVW-.js → codeql-NvupDIDf.js} +0 -0
- /package/dist/web/assets/{common-lisp-B0k-3LTj.js → common-lisp-B-vtBh5k.js} +0 -0
- /package/dist/web/assets/{coq-uygpo7to.js → coq-M8-FTfiZ.js} +0 -0
- /package/dist/web/assets/{csharp-DKUYGfSp.js → csharp-B7Ugdtd-.js} +0 -0
- /package/dist/web/assets/{css-DoOUp7v4.js → css-86k3Z-YA.js} +0 -0
- /package/dist/web/assets/{csv-B3YE13gi.js → csv-BX5XYhfH.js} +0 -0
- /package/dist/web/assets/{csv-parser-CB6bK93y.js → csv-parser-d56Fzpwc.js} +0 -0
- /package/dist/web/assets/{cue-DViO2wvn.js → cue-B7099gQa.js} +0 -0
- /package/dist/web/assets/{cypher-4BMCIW5N.js → cypher-D99mWVIu.js} +0 -0
- /package/dist/web/assets/{d-BT_sTHas.js → d-CFE8oQwh.js} +0 -0
- /package/dist/web/assets/{dark-plus-DVVXu-1N.js → dark-plus-BEeeDwdg.js} +0 -0
- /package/dist/web/assets/{dart-DHz6f6_6.js → dart-HVSWGnI5.js} +0 -0
- /package/dist/web/assets/{dax-ChYCjweK.js → dax-DjDgSJkh.js} +0 -0
- /package/dist/web/assets/{desktop-D6Lt4p04.js → desktop-B5A3i7J1.js} +0 -0
- /package/dist/web/assets/{diff-D8TDe4hP.js → diff-CpTrlaE9.js} +0 -0
- /package/dist/web/assets/{dist-D1CjMuoa.js → dist-BSCgJs-V.js} +0 -0
- /package/dist/web/assets/{docker-Bsp0YbPx.js → docker-xBhmJeHj.js} +0 -0
- /package/dist/web/assets/{dotenv-B_7giSY_.js → dotenv-DKKhLEv5.js} +0 -0
- /package/dist/web/assets/{dracula-TrQL2iB5.js → dracula-CVwQuzra.js} +0 -0
- /package/dist/web/assets/{dracula-soft-n2XOlgh-.js → dracula-soft-CG6mB-lf.js} +0 -0
- /package/dist/web/assets/{dream-maker-CdIEdhpT.js → dream-maker-SC-IdYY3.js} +0 -0
- /package/dist/web/assets/{edit-diff-preview-Cj6-iplb.js → edit-diff-preview-C50i1H63.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CSbixs6u.js → emacs-lisp-CIBFq6W8.js} +0 -0
- /package/dist/web/assets/{everforest-dark-BuZahZYp.js → everforest-dark-odRT_lec.js} +0 -0
- /package/dist/web/assets/{everforest-light-DPH53z5H.js → everforest-light-or1Voqjl.js} +0 -0
- /package/dist/web/assets/{fennel-B1-K_hlJ.js → fennel-tQd1z3iu.js} +0 -0
- /package/dist/web/assets/{fish-Cy12Dbua.js → fish-DSuZQG0k.js} +0 -0
- /package/dist/web/assets/{fluent-B_82ys8E.js → fluent-D7xI8a8e.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BRQdNzZF.js → fortran-free-form-BlAtcPd3.js} +0 -0
- /package/dist/web/assets/{gdscript-XBDmLUm9.js → gdscript-VsEQXBPm.js} +0 -0
- /package/dist/web/assets/{gdshader-DhVfB3u4.js → gdshader-iqHMOdwW.js} +0 -0
- /package/dist/web/assets/{genie-X3K29Y49.js → genie-C4XU4g1a.js} +0 -0
- /package/dist/web/assets/{gherkin-DKIx-6cX.js → gherkin-C4nfQzAB.js} +0 -0
- /package/dist/web/assets/{github-dark-Bf7zdp5s.js → github-dark-B_pbG3CC.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DqsBBV11.js → github-dark-default-kFbx7C95.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-X2gqRz6w.js → github-dark-dimmed-CpVfQEqs.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Z7tvbGX4.js → github-dark-high-contrast-DYKqQwZh.js} +0 -0
- /package/dist/web/assets/{github-light-default-B0liF4d4.js → github-light-default-DVHfMPQ2.js} +0 -0
- /package/dist/web/assets/{github-light-BKxt-7zZ.js → github-light-gEYBmKaj.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-F4encJwr.js → github-light-high-contrast-ClVJDu-q.js} +0 -0
- /package/dist/web/assets/{gleam-Pz2bHgBB.js → gleam-BLaPu7ft.js} +0 -0
- /package/dist/web/assets/{gn-CgmdNsPU.js → gn-BKgerUyI.js} +0 -0
- /package/dist/web/assets/{gnuplot-qLNF5yZ9.js → gnuplot-cqJuEFb-.js} +0 -0
- /package/dist/web/assets/{go-CSDUNhE7.js → go-B2EHdyD8.js} +0 -0
- /package/dist/web/assets/{groovy-B3NEnOHF.js → groovy-DdImoj8s.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DcoftiUX.js → gruvbox-dark-hard-DlwstAds.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-D1wVSuD8.js → gruvbox-dark-medium-CiYEd1vd.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DWpunymH.js → gruvbox-dark-soft-C-wEa4h4.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-rHMvyUdS.js → gruvbox-light-hard-BqYmfna-.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-3O2KiXO7.js → gruvbox-light-medium-CxmfalT2.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-lNlXJPwH.js → gruvbox-light-soft-DGWXXMCB.js} +0 -0
- /package/dist/web/assets/{haskell-aaG3aC0h.js → haskell-H_C_IuVb.js} +0 -0
- /package/dist/web/assets/{haxe-CPcHhUDd.js → haxe-CGNSMpeZ.js} +0 -0
- /package/dist/web/assets/{hcl-CMQNlR_4.js → hcl-B7bSTSQf.js} +0 -0
- /package/dist/web/assets/{hjson-DeuklPFj.js → hjson-DlJ-glQZ.js} +0 -0
- /package/dist/web/assets/{hlsl-BI3hlsaM.js → hlsl-CehW5SJN.js} +0 -0
- /package/dist/web/assets/{horizon-C7-gOcjs.js → horizon-Beabcz2d.js} +0 -0
- /package/dist/web/assets/{horizon-bright-Cxt9bRW7.js → horizon-bright-BC87RXPa.js} +0 -0
- /package/dist/web/assets/{houston-DbF2G7iD.js → houston-Da7RFwUy.js} +0 -0
- /package/dist/web/assets/{hy-DRG6Hs2I.js → hy-m1H70w19.js} +0 -0
- /package/dist/web/assets/{imba-CxKv_2TW.js → imba-MExFsdAA.js} +0 -0
- /package/dist/web/assets/{ini-BiHsyCdO.js → ini-CJy_zmwy.js} +0 -0
- /package/dist/web/assets/{java-D3RZzgDB.js → java-DqDFvJBH.js} +0 -0
- /package/dist/web/assets/{javascript-4FmSqG-T.js → javascript-C3Wyv22X.js} +0 -0
- /package/dist/web/assets/{json-CjB236_E.js → json-DnGBw_DZ.js} +0 -0
- /package/dist/web/assets/{json5-bkfO-Rco.js → json5-CRN-zoN4.js} +0 -0
- /package/dist/web/assets/{jsonc-BwaTeLyv.js → jsonc-CKRUZNRo.js} +0 -0
- /package/dist/web/assets/{jsonl-CfxNSwGd.js → jsonl-CGfD6FrH.js} +0 -0
- /package/dist/web/assets/{jsonnet-CQETCYrp.js → jsonnet-nbFuDj0o.js} +0 -0
- /package/dist/web/assets/{jssm-D5o3Zzdz.js → jssm-D0ShzIqD.js} +0 -0
- /package/dist/web/assets/{jsx-BAMILPLo.js → jsx-D27hbRbs.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BhGVxu3R.js → kanagawa-dragon-Ov3f8K0T.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-FH4Y59ro.js → kanagawa-lotus-CLyOqh-g.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-eilvP9MB.js → kanagawa-wave-Kj3dzabH.js} +0 -0
- /package/dist/web/assets/{kdl-BQ-yvXUK.js → kdl-DFF58Gu4.js} +0 -0
- /package/dist/web/assets/{kotlin-BuCx0-VT.js → kotlin-BM4PKaB4.js} +0 -0
- /package/dist/web/assets/{kusto-DAN_n_Cy.js → kusto-DrvHLCv5.js} +0 -0
- /package/dist/web/assets/{laserwave-Cq2BlxrT.js → laserwave-Db6i6ZkO.js} +0 -0
- /package/dist/web/assets/{lean-Dhq7uXKw.js → lean-D0hEjzwk.js} +0 -0
- /package/dist/web/assets/{less-CxaMQb8j.js → less-BEj4npGG.js} +0 -0
- /package/dist/web/assets/{lib-MIEszzkI.js → lib-CBdlQ75W.js} +0 -0
- /package/dist/web/assets/{light-plus-B4sMAend.js → light-plus-DLIIbDea.js} +0 -0
- /package/dist/web/assets/{llvm-DQ6-B_6Y.js → llvm-DMt6umsO.js} +0 -0
- /package/dist/web/assets/{log-Bb_vqvu7.js → log-BopvdtPW.js} +0 -0
- /package/dist/web/assets/{logo-BzfYv3A_.js → logo-CYSUFPFR.js} +0 -0
- /package/dist/web/assets/{luau-BnAeNR77.js → luau-XC611HNO.js} +0 -0
- /package/dist/web/assets/{make-B6IFuxpC.js → make-BMYZIfBj.js} +0 -0
- /package/dist/web/assets/{markdown-Z0xi77Fn.js → markdown-BLza5nvF.js} +0 -0
- /package/dist/web/assets/{material-theme-1scy1SYn.js → material-theme-Uw27UfJW.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-G-JykXQP.js → material-theme-darker-dfqt8XBY.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BtKkCMVq.js → material-theme-lighter-CPtBTblV.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-irrDC89L.js → material-theme-ocean-BfZgVqC-.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-Eq5_K4LM.js → material-theme-palenight-DAZoVYg8.js} +0 -0
- /package/dist/web/assets/{matlab-BE3oCXZC.js → matlab-cSzmquiW.js} +0 -0
- /package/dist/web/assets/{mdx-DYiuFTGw.js → mdx-Dq0kW5VM.js} +0 -0
- /package/dist/web/assets/{mermaid-DVlaEvtw.js → mermaid-DoIKF0yS.js} +0 -0
- /package/dist/web/assets/{min-dark-DSkeTpQa.js → min-dark-DyraimDU.js} +0 -0
- /package/dist/web/assets/{min-light-CFv2Q0uF.js → min-light-Co9cbcUv.js} +0 -0
- /package/dist/web/assets/{mipsasm-CiAFuZQ2.js → mipsasm-ph6cKgHn.js} +0 -0
- /package/dist/web/assets/{mojo-ARshcaSQ.js → mojo-BBT0XwmY.js} +0 -0
- /package/dist/web/assets/{monokai-CPTV-SOg.js → monokai-IP0T5Rtg.js} +0 -0
- /package/dist/web/assets/{moonbit-D57B0dZr.js → moonbit-B8cogNXl.js} +0 -0
- /package/dist/web/assets/{move-qgy3G_o9.js → move-DkY26NtM.js} +0 -0
- /package/dist/web/assets/{narrat-CZ3rC-oc.js → narrat-rBs3byG0.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BxxIOcI4.js → nextflow-groovy-DkO9wMLP.js} +0 -0
- /package/dist/web/assets/{night-owl-B1ryGCda.js → night-owl-jVR4lFji.js} +0 -0
- /package/dist/web/assets/{night-owl-light-BZvNnvMF.js → night-owl-light-fBYJ7s3U.js} +0 -0
- /package/dist/web/assets/{nix-BVBQi8yv.js → nix-WX9oavu6.js} +0 -0
- /package/dist/web/assets/{nord-3NXw613h.js → nord-Cf7G21Pu.js} +0 -0
- /package/dist/web/assets/{nushell-BMOLRalb.js → nushell-DQdzcJ1H.js} +0 -0
- /package/dist/web/assets/{objective-c-AxruEUgh.js → objective-c-JSO2uvZ-.js} +0 -0
- /package/dist/web/assets/{objective-cpp-cFT0qRsD.js → objective-cpp-CZMGJ9Fb.js} +0 -0
- /package/dist/web/assets/{ocaml-_8JH6O8J.js → ocaml-C4S4NSDo.js} +0 -0
- /package/dist/web/assets/{odin-nwYvR0O6.js → odin-Y2jXCgVq.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-C1LaYJaH.js → one-dark-pro-dVIxuoJY.js} +0 -0
- /package/dist/web/assets/{one-light-B7rMc__c.js → one-light-BQ1IKfD1.js} +0 -0
- /package/dist/web/assets/{openscad-Cwes8bKO.js → openscad-Bddw4I3z.js} +0 -0
- /package/dist/web/assets/{pascal-DMt-6tdZ.js → pascal-BKVXrzC1.js} +0 -0
- /package/dist/web/assets/{pkl-QBTyYQmB.js → pkl-BTd-YRsO.js} +0 -0
- /package/dist/web/assets/{plastic-BTSUp8Fk.js → plastic-eiGvBIxV.js} +0 -0
- /package/dist/web/assets/{plsql-DQv3IFXP.js → plsql-C290tieP.js} +0 -0
- /package/dist/web/assets/{po-CNwZx4tW.js → po-DyDwh0tx.js} +0 -0
- /package/dist/web/assets/{poimandres-8Os7Qequ.js → poimandres-B87zc_g-.js} +0 -0
- /package/dist/web/assets/{polar-b4yK8oD8.js → polar-Cdub1YMT.js} +0 -0
- /package/dist/web/assets/{postcss-sFctwpxx.js → postcss-BcI_3K0v.js} +0 -0
- /package/dist/web/assets/{powerquery-Cg_WrtuN.js → powerquery-OdMrkJSe.js} +0 -0
- /package/dist/web/assets/{powershell-C7Ifz95h.js → powershell-C8auT6xg.js} +0 -0
- /package/dist/web/assets/{prisma-Z52HUJzQ.js → prisma-DIbVv8PA.js} +0 -0
- /package/dist/web/assets/{prolog-DLqW-zR1.js → prolog-DbTOH3Ld.js} +0 -0
- /package/dist/web/assets/{proto-BOIzxCCJ.js → proto-C4Cq2dCD.js} +0 -0
- /package/dist/web/assets/{puppet-De7TjZIf.js → puppet-DJp3uqum.js} +0 -0
- /package/dist/web/assets/{purescript-R76ou1WT.js → purescript-CLsV4rPU.js} +0 -0
- /package/dist/web/assets/{python-Dprm0tNP.js → python-2gBtEza2.js} +0 -0
- /package/dist/web/assets/{qmldir-Clp_jpNw.js → qmldir-DfhULcua.js} +0 -0
- /package/dist/web/assets/{qss-eQPYYzSo.js → qss-DK--VTTV.js} +0 -0
- /package/dist/web/assets/{r-CCcRHIf5.js → r-BArBO2Sa.js} +0 -0
- /package/dist/web/assets/{racket-CzE472yp.js → racket-5mijHsVy.js} +0 -0
- /package/dist/web/assets/{raku-DQwXrTSi.js → raku-ir0suElk.js} +0 -0
- /package/dist/web/assets/{red-KCS6wyop.js → red-DSecpIOT.js} +0 -0
- /package/dist/web/assets/{reg-CcrAddUh.js → reg-Dp30FusS.js} +0 -0
- /package/dist/web/assets/{regexp-DDbud-Ri.js → regexp-DXJnEM_6.js} +0 -0
- /package/dist/web/assets/{rel-99GDgaHp.js → rel-Dr483ozj.js} +0 -0
- /package/dist/web/assets/{riscv-Dm2eNv0R.js → riscv-ZfBy5IA1.js} +0 -0
- /package/dist/web/assets/{ron-CzFt0ygH.js → ron-ZJ_CljGP.js} +0 -0
- /package/dist/web/assets/{rose-pine-Ck6lCrCP.js → rose-pine-CcW_eJ6G.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-vtALRJNj.js → rose-pine-dawn-v2l_UF-j.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-DPto8qXw.js → rose-pine-moon-CNXkwf3D.js} +0 -0
- /package/dist/web/assets/{rosmsg-NtS6429H.js → rosmsg-BMYMVW84.js} +0 -0
- /package/dist/web/assets/{rust-BjIXvF9h.js → rust-BR-zJh5q.js} +0 -0
- /package/dist/web/assets/{sass-CQ23wf5E.js → sass-BukPOBFz.js} +0 -0
- /package/dist/web/assets/{scala-LfKLM143.js → scala-DUFY7D6D.js} +0 -0
- /package/dist/web/assets/{scheme-D3SLBXlF.js → scheme-Dl-dNNxQ.js} +0 -0
- /package/dist/web/assets/{sdbl-ODRd2rGN.js → sdbl-DdmlQhia.js} +0 -0
- /package/dist/web/assets/{shellscript-3KB1G_oA.js → shellscript-UyhrvGcd.js} +0 -0
- /package/dist/web/assets/{slack-dark-DTtyWmIy.js → slack-dark-Cv6Szr-q.js} +0 -0
- /package/dist/web/assets/{slack-ochin-Bu0PApk3.js → slack-ochin-V6Ii3lAm.js} +0 -0
- /package/dist/web/assets/{smalltalk-haMT-Sbe.js → smalltalk-BUCv5G91.js} +0 -0
- /package/dist/web/assets/{snazzy-light-DXJzefE9.js → snazzy-light-B67a78qi.js} +0 -0
- /package/dist/web/assets/{solarized-dark-B6LyYa18.js → solarized-dark-ccWnfYyA.js} +0 -0
- /package/dist/web/assets/{solarized-light-QLADebkv.js → solarized-light-C3WyRO5I.js} +0 -0
- /package/dist/web/assets/{solidity-mzfq1aDc.js → solidity-BFbRPNi_.js} +0 -0
- /package/dist/web/assets/{splunk-CyH4RWBp.js → splunk-zdpwCJLE.js} +0 -0
- /package/dist/web/assets/{sql-CWVlT5D3.js → sql-B2IPUsfz.js} +0 -0
- /package/dist/web/assets/{ssh-config-DNvQJ1Qx.js → ssh-config-Co4cXifq.js} +0 -0
- /package/dist/web/assets/{stylus-ByQQdtmf.js → stylus-CmI75XY8.js} +0 -0
- /package/dist/web/assets/{swift-D1cpY4ow.js → swift-Dfks9YQI.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BDF_-QiW.js → synthwave-84-B3LERzNS.js} +0 -0
- /package/dist/web/assets/{system-verilog-BYrkMydC.js → system-verilog-BFvldiwl.js} +0 -0
- /package/dist/web/assets/{systemd-CQm3zyK0.js → systemd-Cu4Vvgbu.js} +0 -0
- /package/dist/web/assets/{talonscript-DMW5CzE1.js → talonscript-Bnq32pUB.js} +0 -0
- /package/dist/web/assets/{tasl-BhFzMCr2.js → tasl-Nwe2K3J1.js} +0 -0
- /package/dist/web/assets/{tcl-D7VEAF-9.js → tcl-zOnpywrl.js} +0 -0
- /package/dist/web/assets/{terraform-DdT5A0Vy.js → terraform-Bc9Vj-n3.js} +0 -0
- /package/dist/web/assets/{tokyo-night-WGLMM6Q9.js → tokyo-night-9_KyM8M2.js} +0 -0
- /package/dist/web/assets/{toml-CZ7ipXHZ.js → toml-ByH-fxOY.js} +0 -0
- /package/dist/web/assets/{tsv-B1zVvoTr.js → tsv-DrCnDClP.js} +0 -0
- /package/dist/web/assets/{tsx-D4HwRUBq.js → tsx-B8LafSYr.js} +0 -0
- /package/dist/web/assets/{turtle-B4r8KEJH.js → turtle-DuyP_QCJ.js} +0 -0
- /package/dist/web/assets/{typescript-BnHhxMtp.js → typescript-BTBJb5RD.js} +0 -0
- /package/dist/web/assets/{typespec-DP2q46GT.js → typespec-DdOFo7M5.js} +0 -0
- /package/dist/web/assets/{typst-DFsD_yaD.js → typst-LYvnBWQu.js} +0 -0
- /package/dist/web/assets/{use-blob-url-DpDVcTeK.js → use-blob-url-CpyRr7Cx.js} +0 -0
- /package/dist/web/assets/{v-Bji8hSVT.js → v-BuBYA1_x.js} +0 -0
- /package/dist/web/assets/{vala-D3TIgGmU.js → vala-vRIBYYe3.js} +0 -0
- /package/dist/web/assets/{vb-DvcxFS2q.js → vb-DwGi-IYM.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DfNMj6xt.js → vendor-xterm-i6SHFTfU.js} +0 -0
- /package/dist/web/assets/{verilog-Dy7cF35d.js → verilog-6B1X4G2-.js} +0 -0
- /package/dist/web/assets/{vesper-BzjwUv9J.js → vesper-Dw1y_Ae7.js} +0 -0
- /package/dist/web/assets/{vhdl-DuFnFNmW.js → vhdl-DK8JwBzJ.js} +0 -0
- /package/dist/web/assets/{viml-yAPgKlbz.js → viml-BTKIEIqm.js} +0 -0
- /package/dist/web/assets/{vitesse-black-oSSQdyIZ.js → vitesse-black-BqqolhEg.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BZxwPihB.js → vitesse-dark-B9JL5WQA.js} +0 -0
- /package/dist/web/assets/{vitesse-light-uIigycvt.js → vitesse-light-DYJKj2se.js} +0 -0
- /package/dist/web/assets/{vyper-CX3NAxm5.js → vyper-D-jDI3WH.js} +0 -0
- /package/dist/web/assets/{wasm-Coen1MOg.js → wasm-1-GlNdpk.js} +0 -0
- /package/dist/web/assets/{wasm-D9td37qO.js → wasm-BBW4WmT2.js} +0 -0
- /package/dist/web/assets/{wenyan-lwNIHgk6.js → wenyan-B6cM7sDO.js} +0 -0
- /package/dist/web/assets/{wgsl-DQAzCfog.js → wgsl-FaHGQgko.js} +0 -0
- /package/dist/web/assets/{wikitext-DTXmt5NE.js → wikitext-DtIyPUrk.js} +0 -0
- /package/dist/web/assets/{wit-Bdw_7WDH.js → wit-BMVRif5Y.js} +0 -0
- /package/dist/web/assets/{wolfram-DqyHDZ07.js → wolfram-DQsCGohn.js} +0 -0
- /package/dist/web/assets/{yaml-CPXhdByd.js → yaml-C-Va56Gt.js} +0 -0
- /package/dist/web/assets/{zenscript-6s_i5zek.js → zenscript-DdobKGI-.js} +0 -0
- /package/dist/web/assets/{zig-B2htezJ_.js → zig-vqDFwj6q.js} +0 -0
|
@@ -575,16 +575,19 @@ class AccountService {
|
|
|
575
575
|
if (!res.ok) {
|
|
576
576
|
const errorBody = await res.text().catch(() => "");
|
|
577
577
|
console.error(`[accounts] Refresh failed for ${accountId}: ${res.status} ${errorBody}`);
|
|
578
|
-
// invalid_grant or invalid_request = refresh token permanently dead → clear it so account becomes temporary
|
|
579
578
|
if (errorBody.includes("invalid_grant") || errorBody.includes("invalid_request")) {
|
|
580
|
-
//
|
|
579
|
+
// Another session/process may have refreshed (and rotated) the token between
|
|
580
|
+
// our read and this OAuth call — if the DB token is now fresh, this failure is stale.
|
|
581
581
|
const recheckAccount = this.getWithTokens(accountId);
|
|
582
582
|
if (recheckAccount?.expiresAt && recheckAccount.expiresAt - Math.floor(Date.now() / 1000) > 60) {
|
|
583
583
|
console.log(`[accounts] Refresh failed with invalid_grant but DB token is now fresh — another session refreshed it`);
|
|
584
584
|
return;
|
|
585
585
|
}
|
|
586
|
-
|
|
587
|
-
|
|
586
|
+
// Do NOT wipe the refresh token. On multi-device/multi-process setups the token is
|
|
587
|
+
// usually rotated elsewhere, not truly dead; clearing it bricks the local copy
|
|
588
|
+
// permanently with no recovery path (esp. for parked/disabled accounts). Preserve
|
|
589
|
+
// it so re-enable / re-import / re-sync can restore access.
|
|
590
|
+
console.warn(`[accounts] Refresh token rejected for ${account.email ?? accountId} — preserving token for recovery (not clearing)`);
|
|
588
591
|
}
|
|
589
592
|
if (disableOnFail) {
|
|
590
593
|
this.setDisabled(accountId);
|
|
@@ -743,9 +746,12 @@ class AccountService {
|
|
|
743
746
|
const accounts = this.list();
|
|
744
747
|
const nowS = Math.floor(Date.now() / 1000);
|
|
745
748
|
for (const acc of accounts) {
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
+
// Freeze disabled accounts: skip background refresh entirely.
|
|
750
|
+
// Refreshing rotates the OAuth refresh token; when the same account is
|
|
751
|
+
// shared across machines/processes the rotation races and one side gets
|
|
752
|
+
// invalid_grant, killing an account the user only meant to park. Leaving
|
|
753
|
+
// a disabled account untouched keeps its token intact until re-enabled.
|
|
754
|
+
if (acc.status === "disabled") continue;
|
|
749
755
|
if (!acc.expiresAt) continue;
|
|
750
756
|
if (acc.expiresAt - nowS > REFRESH_BUFFER_S) continue;
|
|
751
757
|
// Skip temporary accounts (no refresh token) — they can't be refreshed
|
|
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { mkdirSync, existsSync } from "node:fs";
|
|
4
4
|
import { encrypt, decrypt } from "../lib/account-crypto.ts";
|
|
5
5
|
import { getPpmDir } from "./ppm-dir.ts";
|
|
6
|
-
export const CURRENT_SCHEMA_VERSION =
|
|
6
|
+
export const CURRENT_SCHEMA_VERSION = 36;
|
|
7
7
|
|
|
8
8
|
let db: Database | null = null;
|
|
9
9
|
let dbProfile: string | null = null;
|
|
@@ -831,6 +831,12 @@ function runMigrations(database: Database): void {
|
|
|
831
831
|
PRAGMA user_version = 35;
|
|
832
832
|
`);
|
|
833
833
|
}
|
|
834
|
+
|
|
835
|
+
if (current < 36) {
|
|
836
|
+
try { database.exec("ALTER TABLE session_metadata ADD COLUMN effort TEXT"); } catch {}
|
|
837
|
+
try { database.exec("ALTER TABLE session_metadata ADD COLUMN thinking_budget INTEGER"); } catch {}
|
|
838
|
+
database.exec("PRAGMA user_version = 36;");
|
|
839
|
+
}
|
|
834
840
|
}
|
|
835
841
|
|
|
836
842
|
// ---------------------------------------------------------------------------
|
|
@@ -1023,6 +1029,30 @@ export function setSessionModel(sessionId: string, model: string): void {
|
|
|
1023
1029
|
).run(sessionId, model);
|
|
1024
1030
|
}
|
|
1025
1031
|
|
|
1032
|
+
/** Per-session effort override; null when session uses provider default */
|
|
1033
|
+
export function getSessionEffort(sessionId: string): string | null {
|
|
1034
|
+
const row = getDb().query("SELECT effort FROM session_metadata WHERE session_id = ?").get(sessionId) as { effort: string | null } | null;
|
|
1035
|
+
return row?.effort ?? null;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export function setSessionEffort(sessionId: string, effort: string): void {
|
|
1039
|
+
getDb().query(
|
|
1040
|
+
"INSERT INTO session_metadata (session_id, effort) VALUES (?, ?) ON CONFLICT(session_id) DO UPDATE SET effort = excluded.effort",
|
|
1041
|
+
).run(sessionId, effort);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/** Per-session thinking budget (maxThinkingTokens); null = thinking OFF / use provider default */
|
|
1045
|
+
export function getSessionThinking(sessionId: string): number | null {
|
|
1046
|
+
const row = getDb().query("SELECT thinking_budget FROM session_metadata WHERE session_id = ?").get(sessionId) as { thinking_budget: number | null } | null;
|
|
1047
|
+
return row?.thinking_budget ?? null;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
export function setSessionThinking(sessionId: string, budget: number | null): void {
|
|
1051
|
+
getDb().query(
|
|
1052
|
+
"INSERT INTO session_metadata (session_id, thinking_budget) VALUES (?, ?) ON CONFLICT(session_id) DO UPDATE SET thinking_budget = excluded.thinking_budget",
|
|
1053
|
+
).run(sessionId, budget);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1026
1056
|
// ---------------------------------------------------------------------------
|
|
1027
1057
|
// Unread tracking
|
|
1028
1058
|
// ---------------------------------------------------------------------------
|
package/src/types/api.ts
CHANGED
|
@@ -23,9 +23,11 @@ export type TerminalWsMessage =
|
|
|
23
23
|
|
|
24
24
|
/** WebSocket message types (chat) */
|
|
25
25
|
export type ChatWsClientMessage =
|
|
26
|
-
| { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>; model?: string }
|
|
26
|
+
| { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>; model?: string; effort?: string; thinking?: boolean }
|
|
27
27
|
| { type: "cancel" }
|
|
28
28
|
| { type: "set_model"; model: string }
|
|
29
|
+
| { type: "set_effort"; effort: string }
|
|
30
|
+
| { type: "set_thinking"; enabled: boolean }
|
|
29
31
|
| { type: "approval_response"; requestId: string; approved: boolean; reason?: string; data?: unknown }
|
|
30
32
|
| { type: "kill_background_shell"; shellId: string }
|
|
31
33
|
| { type: "ready" };
|
|
@@ -57,7 +59,7 @@ export type ChatWsServerMessage =
|
|
|
57
59
|
| { type: "error"; message: string }
|
|
58
60
|
| { type: "account_info"; accountId: string; accountLabel: string }
|
|
59
61
|
| { type: "phase_changed"; phase: SessionPhase; elapsed?: number }
|
|
60
|
-
| { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: { requestId: string; tool: string; input: unknown } | null; sessionTitle: string | null; model?: string }
|
|
62
|
+
| { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: { requestId: string; tool: string; input: unknown } | null; sessionTitle: string | null; model?: string; effort?: string; thinking?: boolean }
|
|
61
63
|
| { type: "turn_events"; events: unknown[] }
|
|
62
64
|
| { type: "title_updated"; title: string }
|
|
63
65
|
| { type: "compact_status"; status: "compacting" | "done" }
|
package/src/types/chat.ts
CHANGED
|
@@ -4,8 +4,16 @@ export interface SendMessageOpts {
|
|
|
4
4
|
images?: Array<{ data: string; mediaType: string }>;
|
|
5
5
|
/** Per-session model override; falls back to provider config model when absent */
|
|
6
6
|
model?: string;
|
|
7
|
+
/** Override the provider's 1M-context setting for this call (false = never add the
|
|
8
|
+
* [1m] suffix). Used by lightweight calls (e.g. the group-chat router) whose small
|
|
9
|
+
* model may not support a 1M window. Falls back to provider config when absent. */
|
|
10
|
+
oneMContext?: boolean;
|
|
7
11
|
/** Per-query turn cap; falls back to provider config max_turns when absent */
|
|
8
12
|
maxTurns?: number;
|
|
13
|
+
/** Per-session effort override (low|medium|high|xhigh|max); falls back to provider config */
|
|
14
|
+
effort?: string;
|
|
15
|
+
/** Per-session thinking budget → SDK maxThinkingTokens; falls back to provider config */
|
|
16
|
+
maxThinkingTokens?: number;
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
export interface AIProvider {
|
package/src/types/config.ts
CHANGED
|
@@ -67,7 +67,7 @@ export interface AIProviderConfig {
|
|
|
67
67
|
api_key_env?: string;
|
|
68
68
|
api_key?: string;
|
|
69
69
|
base_url?: string;
|
|
70
|
-
effort?: "low" | "medium" | "high" | "max";
|
|
70
|
+
effort?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
71
71
|
max_turns?: number;
|
|
72
72
|
max_budget_usd?: number;
|
|
73
73
|
thinking_budget_tokens?: number;
|
|
@@ -97,7 +97,7 @@ export const DEFAULT_CONFIG: PpmConfig = {
|
|
|
97
97
|
claude: {
|
|
98
98
|
type: "agent-sdk",
|
|
99
99
|
api_key_env: "ANTHROPIC_API_KEY",
|
|
100
|
-
model: "claude-opus-
|
|
100
|
+
model: "claude-opus-5",
|
|
101
101
|
effort: "high",
|
|
102
102
|
max_turns: 1000,
|
|
103
103
|
permission_mode: "bypassPermissions",
|
|
@@ -120,8 +120,8 @@ export const DEFAULT_CONFIG: PpmConfig = {
|
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
const VALID_TYPES = ["agent-sdk", "cli", "mock"] as const;
|
|
123
|
-
const VALID_EFFORTS = ["low", "medium", "high"] as const;
|
|
124
|
-
const VALID_MODELS = ["claude-fable-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"] as const;
|
|
123
|
+
const VALID_EFFORTS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
124
|
+
const VALID_MODELS = ["claude-opus-5", "claude-fable-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-5", "claude-sonnet-4-6", "claude-haiku-4-5"] as const;
|
|
125
125
|
/** Allowed CLI commands for CLI providers (prevents command injection) */
|
|
126
126
|
const VALID_CLI_COMMANDS = ["cursor-agent", "codex", "gemini"] as const;
|
|
127
127
|
/** Only these values are allowed for default_provider in config */
|
|
@@ -241,12 +241,7 @@ export function sanitizeConfig(config: PpmConfig): boolean {
|
|
|
241
241
|
dirty = true;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
// Downgrade "max" effort → "high" (not available for Claude.ai subscribers)
|
|
245
244
|
for (const provider of Object.values(config.ai.providers)) {
|
|
246
|
-
if ((provider as any).effort === "max") {
|
|
247
|
-
provider.effort = "high";
|
|
248
|
-
dirty = true;
|
|
249
|
-
}
|
|
250
245
|
// Fix invalid permission_mode
|
|
251
246
|
if (provider.permission_mode != null && !["default", "acceptEdits", "plan", "bypassPermissions"].includes(provider.permission_mode)) {
|
|
252
247
|
provider.permission_mode = "bypassPermissions";
|
|
@@ -112,6 +112,10 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
112
112
|
sessionTitle,
|
|
113
113
|
model,
|
|
114
114
|
setModel,
|
|
115
|
+
effort,
|
|
116
|
+
setEffort,
|
|
117
|
+
thinking,
|
|
118
|
+
setThinking,
|
|
115
119
|
sendMessage,
|
|
116
120
|
respondToApproval,
|
|
117
121
|
cancelStreaming,
|
|
@@ -681,6 +685,10 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
681
685
|
onProviderChange={!sessionId ? setProviderId : undefined}
|
|
682
686
|
model={model}
|
|
683
687
|
onModelChange={setModel}
|
|
688
|
+
effort={effort}
|
|
689
|
+
onEffortChange={setEffort}
|
|
690
|
+
thinking={thinking}
|
|
691
|
+
onThinkingChange={setThinking}
|
|
684
692
|
/>
|
|
685
693
|
)}
|
|
686
694
|
</div>
|
|
@@ -7,7 +7,7 @@ import { isImageFile } from "@/lib/file-support";
|
|
|
7
7
|
import { AttachmentChips } from "./attachment-chips";
|
|
8
8
|
import { ModeSelector, getModeLabel, getModeIcon } from "./mode-selector";
|
|
9
9
|
import { ProviderSelector } from "./provider-selector";
|
|
10
|
-
import {
|
|
10
|
+
import { ModelThinkingSelector } from "./model-thinking-selector";
|
|
11
11
|
import type { SlashItem } from "./slash-command-picker";
|
|
12
12
|
import type { FileNode } from "../../../types/project";
|
|
13
13
|
import { useFileStore } from "@/stores/file-store";
|
|
@@ -69,6 +69,14 @@ interface MessageInputProps {
|
|
|
69
69
|
model?: string | null;
|
|
70
70
|
/** Model change handler — undefined when no active session */
|
|
71
71
|
onModelChange?: (model: string) => void;
|
|
72
|
+
/** Current per-session effort (null = provider default) */
|
|
73
|
+
effort?: string | null;
|
|
74
|
+
/** Effort change handler */
|
|
75
|
+
onEffortChange?: (effort: string) => void;
|
|
76
|
+
/** Current per-session thinking on/off */
|
|
77
|
+
thinking?: boolean;
|
|
78
|
+
/** Thinking toggle handler */
|
|
79
|
+
onThinkingChange?: (enabled: boolean) => void;
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export const MessageInput = memo(function MessageInput({
|
|
@@ -96,6 +104,10 @@ export const MessageInput = memo(function MessageInput({
|
|
|
96
104
|
onProviderChange,
|
|
97
105
|
model,
|
|
98
106
|
onModelChange,
|
|
107
|
+
effort,
|
|
108
|
+
onEffortChange,
|
|
109
|
+
thinking,
|
|
110
|
+
onThinkingChange,
|
|
99
111
|
}: MessageInputProps) {
|
|
100
112
|
// Uncontrolled textarea: value lives in DOM + ref, not React state.
|
|
101
113
|
// Only `hasText` state triggers re-renders (empty↔non-empty for send button).
|
|
@@ -700,9 +712,13 @@ export const MessageInput = memo(function MessageInput({
|
|
|
700
712
|
/>
|
|
701
713
|
)}
|
|
702
714
|
{onModelChange && projectName && (
|
|
703
|
-
<
|
|
704
|
-
|
|
705
|
-
|
|
715
|
+
<ModelThinkingSelector
|
|
716
|
+
model={model ?? null}
|
|
717
|
+
effort={effort ?? null}
|
|
718
|
+
thinking={thinking ?? false}
|
|
719
|
+
onModelChange={onModelChange}
|
|
720
|
+
onEffortChange={onEffortChange ?? (() => {})}
|
|
721
|
+
onThinkingChange={onThinkingChange ?? (() => {})}
|
|
706
722
|
projectName={projectName}
|
|
707
723
|
providerId={providerId ?? "claude"}
|
|
708
724
|
disabled={isStreaming}
|
|
@@ -795,9 +811,13 @@ export const MessageInput = memo(function MessageInput({
|
|
|
795
811
|
/>
|
|
796
812
|
)}
|
|
797
813
|
{onModelChange && projectName && (
|
|
798
|
-
<
|
|
799
|
-
|
|
800
|
-
|
|
814
|
+
<ModelThinkingSelector
|
|
815
|
+
model={model ?? null}
|
|
816
|
+
effort={effort ?? null}
|
|
817
|
+
thinking={thinking ?? false}
|
|
818
|
+
onModelChange={onModelChange}
|
|
819
|
+
onEffortChange={onEffortChange ?? (() => {})}
|
|
820
|
+
onThinkingChange={onThinkingChange ?? (() => {})}
|
|
801
821
|
projectName={projectName}
|
|
802
822
|
providerId={providerId ?? "claude"}
|
|
803
823
|
disabled={isStreaming}
|
|
@@ -637,12 +637,15 @@ function parseIdeOpenedFile(text: string): { idePath: string | null; cleanText:
|
|
|
637
637
|
/** Parse user message content, extracting attached file paths and the actual text */
|
|
638
638
|
function parseUserAttachments(content: string): { files: string[]; text: string } {
|
|
639
639
|
// Match: [Attached file: /path] or [Attached files:\n/path1\n/path2\n]
|
|
640
|
-
|
|
640
|
+
// Trailing newlines are optional — an attachment-only message has the marker
|
|
641
|
+
// trimmed to the end of the string (extractTerminalBlocks trims), so the
|
|
642
|
+
// separator newlines may be absent.
|
|
643
|
+
const singleMatch = content.match(/^\[Attached file: (.+?)\]\n*/);
|
|
641
644
|
if (singleMatch) {
|
|
642
645
|
return { files: [singleMatch[1]!], text: content.slice(singleMatch[0].length) };
|
|
643
646
|
}
|
|
644
647
|
|
|
645
|
-
const multiMatch = content.match(/^\[Attached files:\n([\s\S]+?)\]\n
|
|
648
|
+
const multiMatch = content.match(/^\[Attached files:\n([\s\S]+?)\]\n*/);
|
|
646
649
|
if (multiMatch) {
|
|
647
650
|
const files = multiMatch[1]!.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
648
651
|
return { files, text: content.slice(multiMatch[0].length) };
|
|
@@ -815,7 +818,7 @@ function UserBubble({ content, messageId, timestamp, projectName, onFork, onEdit
|
|
|
815
818
|
expanded && "max-h-[50vh] overflow-y-auto",
|
|
816
819
|
)}
|
|
817
820
|
>
|
|
818
|
-
{isSystemContext ? <TextWithFilePaths text={text} projectName={projectName} /> : text}
|
|
821
|
+
{isSystemContext ? <TextWithFilePaths text={text} projectName={projectName} /> : <TextWithLinks text={text} />}
|
|
819
822
|
</div>
|
|
820
823
|
)}
|
|
821
824
|
{(isOverflowing || expanded) && (
|
|
@@ -1002,6 +1005,48 @@ function FilePathChip({ path, projectName }: { path: string; projectName?: strin
|
|
|
1002
1005
|
);
|
|
1003
1006
|
}
|
|
1004
1007
|
|
|
1008
|
+
/** Render plain text with http(s) URLs turned into clickable links. Preserves
|
|
1009
|
+
* whitespace/newlines via the surrounding `whitespace-pre-wrap` container. */
|
|
1010
|
+
function TextWithLinks({ text }: { text: string }) {
|
|
1011
|
+
const parts = useMemo(() => {
|
|
1012
|
+
const re = /(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/g;
|
|
1013
|
+
const result: { kind: "text" | "url"; value: string }[] = [];
|
|
1014
|
+
let last = 0;
|
|
1015
|
+
let m;
|
|
1016
|
+
while ((m = re.exec(text)) !== null) {
|
|
1017
|
+
if (m.index > last) result.push({ kind: "text", value: text.slice(last, m.index) });
|
|
1018
|
+
result.push({ kind: "url", value: m[1]! });
|
|
1019
|
+
last = m.index + m[0].length;
|
|
1020
|
+
}
|
|
1021
|
+
if (last < text.length) result.push({ kind: "text", value: text.slice(last) });
|
|
1022
|
+
return result;
|
|
1023
|
+
}, [text]);
|
|
1024
|
+
|
|
1025
|
+
// No URLs — return the raw string so nothing about the layout changes.
|
|
1026
|
+
if (parts.every((p) => p.kind === "text")) return <>{text}</>;
|
|
1027
|
+
|
|
1028
|
+
return (
|
|
1029
|
+
<>
|
|
1030
|
+
{parts.map((p, i) =>
|
|
1031
|
+
p.kind === "url" ? (
|
|
1032
|
+
<a
|
|
1033
|
+
key={i}
|
|
1034
|
+
href={p.value}
|
|
1035
|
+
target="_blank"
|
|
1036
|
+
rel="noopener noreferrer"
|
|
1037
|
+
className="text-primary underline underline-offset-2 hover:text-primary/80 break-all"
|
|
1038
|
+
onClick={(e) => e.stopPropagation()}
|
|
1039
|
+
>
|
|
1040
|
+
{p.value}
|
|
1041
|
+
</a>
|
|
1042
|
+
) : (
|
|
1043
|
+
<span key={i}>{p.value}</span>
|
|
1044
|
+
),
|
|
1045
|
+
)}
|
|
1046
|
+
</>
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1005
1050
|
/** Render text with absolute file paths detected and turned into clickable chips */
|
|
1006
1051
|
function TextWithFilePaths({ text, projectName }: { text: string; projectName?: string }) {
|
|
1007
1052
|
const parts = useMemo(() => {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Pure helpers for the unified model + thinking picker. Kept DOM-free so the
|
|
2
|
+
// label↔value mapping (the crash-prone "Extra"→"xhigh" contract) is unit-testable
|
|
3
|
+
// without a browser harness.
|
|
4
|
+
//
|
|
5
|
+
// SDK effort enum = low | medium | high | xhigh | max. The app UI shows "Extra"
|
|
6
|
+
// for the SDK value "xhigh"; the literal "extra" would crash the CLI subprocess,
|
|
7
|
+
// so it must NEVER appear as a value here.
|
|
8
|
+
|
|
9
|
+
export interface EffortOption {
|
|
10
|
+
label: string;
|
|
11
|
+
value: "low" | "medium" | "high" | "xhigh" | "max";
|
|
12
|
+
default?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const EFFORT_OPTIONS: EffortOption[] = [
|
|
16
|
+
{ label: "Low", value: "low" },
|
|
17
|
+
{ label: "Medium", value: "medium" },
|
|
18
|
+
{ label: "High", value: "high", default: true },
|
|
19
|
+
{ label: "Extra", value: "xhigh" },
|
|
20
|
+
{ label: "Max", value: "max" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/** UI label for an effort value; falls back to the raw value when unknown. */
|
|
24
|
+
export function effortLabel(value: string | null | undefined): string {
|
|
25
|
+
if (!value) return "";
|
|
26
|
+
return EFFORT_OPTIONS.find((o) => o.value === value)?.label ?? value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Chip text: model display, plus " · <Effort>" when an effort is set. */
|
|
30
|
+
export function chipLabel(modelDisplay: string, effortValue: string | null | undefined): string {
|
|
31
|
+
const eff = effortLabel(effortValue);
|
|
32
|
+
return eff ? `${modelDisplay} · ${eff}` : modelDisplay;
|
|
33
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, type ReactNode } from "react";
|
|
2
|
+
import { Check, Sparkles, Brain } from "lucide-react";
|
|
3
|
+
import { api, projectUrl } from "@/lib/api-client";
|
|
4
|
+
import { useIsMobile } from "@/hooks/use-is-mobile";
|
|
5
|
+
import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
|
|
6
|
+
import { EFFORT_OPTIONS, chipLabel } from "./model-thinking-selector-helpers";
|
|
7
|
+
|
|
8
|
+
interface ModelOption {
|
|
9
|
+
value: string;
|
|
10
|
+
label: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ModelThinkingSelectorProps {
|
|
14
|
+
model: string | null;
|
|
15
|
+
effort: string | null;
|
|
16
|
+
thinking: boolean;
|
|
17
|
+
onModelChange: (model: string) => void;
|
|
18
|
+
onEffortChange: (effort: string) => void;
|
|
19
|
+
onThinkingChange: (enabled: boolean) => void;
|
|
20
|
+
projectName: string;
|
|
21
|
+
providerId: string;
|
|
22
|
+
/** Locked while a turn is streaming (model/effort can't change mid-turn). */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Strip the leading "Claude " so the chip stays compact: "Claude Opus 5" → "Opus 5" */
|
|
27
|
+
function shortLabel(label: string): string {
|
|
28
|
+
return label.replace(/^Claude\s+/i, "");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const DEFAULT_EFFORT = EFFORT_OPTIONS.find((o) => o.default)!.value;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Unified model + thinking picker — one chip, one popup. Mirrors Anthropic's native
|
|
35
|
+
* picker: model list, effort levels (Extra→SDK "xhigh"), and a thinking on/off toggle.
|
|
36
|
+
* Desktop → absolute popover; mobile (<md) → bottom sheet with 44px targets.
|
|
37
|
+
*/
|
|
38
|
+
export function ModelThinkingSelector({
|
|
39
|
+
model,
|
|
40
|
+
effort,
|
|
41
|
+
thinking,
|
|
42
|
+
onModelChange,
|
|
43
|
+
onEffortChange,
|
|
44
|
+
onThinkingChange,
|
|
45
|
+
projectName,
|
|
46
|
+
providerId,
|
|
47
|
+
disabled,
|
|
48
|
+
}: ModelThinkingSelectorProps) {
|
|
49
|
+
const [models, setModels] = useState<ModelOption[]>([]);
|
|
50
|
+
const [open, setOpen] = useState(false);
|
|
51
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
52
|
+
const isMobile = useIsMobile();
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!projectName || !providerId) return;
|
|
56
|
+
api
|
|
57
|
+
.get<ModelOption[]>(`${projectUrl(projectName)}/chat/providers/${providerId}/models`)
|
|
58
|
+
.then(setModels)
|
|
59
|
+
.catch(() => {});
|
|
60
|
+
}, [projectName, providerId]);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (disabled) setOpen(false);
|
|
64
|
+
}, [disabled]);
|
|
65
|
+
|
|
66
|
+
// Desktop popover: close on click outside (mobile bottom sheet handles its own backdrop).
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!open || isMobile) return;
|
|
69
|
+
const handler = (e: MouseEvent) => {
|
|
70
|
+
if (panelRef.current && !panelRef.current.contains(e.target as Node)) setOpen(false);
|
|
71
|
+
};
|
|
72
|
+
document.addEventListener("mousedown", handler);
|
|
73
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
74
|
+
}, [open, isMobile]);
|
|
75
|
+
|
|
76
|
+
const current = models.find((m) => m.value === model);
|
|
77
|
+
const modelDisplay = current ? shortLabel(current.label) : model ? shortLabel(model) : "Model";
|
|
78
|
+
const effortValue = effort ?? DEFAULT_EFFORT;
|
|
79
|
+
const showModelList = models.length > 1;
|
|
80
|
+
const chipText = chipLabel(modelDisplay, effortValue);
|
|
81
|
+
|
|
82
|
+
const pick = (fn: () => void) => {
|
|
83
|
+
fn();
|
|
84
|
+
if (!isMobile) setOpen(false);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const content = (
|
|
88
|
+
<div className="flex divide-x divide-border">
|
|
89
|
+
{/* Left column: model list */}
|
|
90
|
+
{showModelList && (
|
|
91
|
+
<div className="flex-1 min-w-0 py-1">
|
|
92
|
+
<SectionLabel>Model</SectionLabel>
|
|
93
|
+
{models.map((m) => (
|
|
94
|
+
<OptionRow key={m.value} active={m.value === model} onClick={() => pick(() => onModelChange(m.value))}>
|
|
95
|
+
<span className="flex-1 truncate">{shortLabel(m.label)}</span>
|
|
96
|
+
{m.value === model && <Check className="size-4 shrink-0 text-primary" />}
|
|
97
|
+
</OptionRow>
|
|
98
|
+
))}
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
{/* Right column: effort + thinking toggle */}
|
|
102
|
+
<div className="flex-1 min-w-0 py-1">
|
|
103
|
+
<SectionLabel>Effort</SectionLabel>
|
|
104
|
+
{EFFORT_OPTIONS.map((o) => (
|
|
105
|
+
<OptionRow key={o.value} active={o.value === effortValue} onClick={() => pick(() => onEffortChange(o.value))}>
|
|
106
|
+
<span className="flex-1 truncate">
|
|
107
|
+
{o.label}
|
|
108
|
+
{o.default && <span className="ml-2 text-xs text-text-subtle">Default</span>}
|
|
109
|
+
</span>
|
|
110
|
+
{o.value === effortValue && <Check className="size-4 shrink-0 text-primary" />}
|
|
111
|
+
</OptionRow>
|
|
112
|
+
))}
|
|
113
|
+
<Separator />
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
onClick={() => onThinkingChange(!thinking)}
|
|
117
|
+
className="w-full flex items-center gap-3 px-3 min-h-[44px] text-left text-sm hover:bg-surface-elevated"
|
|
118
|
+
role="switch"
|
|
119
|
+
aria-checked={thinking}
|
|
120
|
+
>
|
|
121
|
+
<Brain className="size-4 shrink-0" />
|
|
122
|
+
<span className="flex-1">Thinking</span>
|
|
123
|
+
<span className={`inline-flex h-5 w-9 items-center rounded-full transition-colors ${thinking ? "bg-primary" : "bg-border"}`}>
|
|
124
|
+
<span className={`h-4 w-4 rounded-full bg-white transition-transform ${thinking ? "translate-x-4" : "translate-x-0.5"}`} />
|
|
125
|
+
</span>
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<div className="relative">
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
disabled={disabled}
|
|
136
|
+
onClick={(e) => {
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
if (!disabled) setOpen((v) => !v);
|
|
139
|
+
}}
|
|
140
|
+
className="inline-flex items-center gap-1.5 px-[9px] py-1 rounded-full text-[11.5px] text-primary bg-accent-wash border border-accent-wash-border hover:brightness-110 transition-[filter] disabled:opacity-50 disabled:cursor-default"
|
|
141
|
+
aria-label={`Model ${modelDisplay}, effort ${effortValue}${thinking ? ", thinking on" : ""}`}
|
|
142
|
+
title={disabled ? "Can't change while running" : chipText}
|
|
143
|
+
>
|
|
144
|
+
<Sparkles className="h-3.5 w-3.5 shrink-0" />
|
|
145
|
+
<span className="max-w-[140px] truncate">{chipText}</span>
|
|
146
|
+
{thinking && <Brain className="h-3 w-3 shrink-0" />}
|
|
147
|
+
</button>
|
|
148
|
+
|
|
149
|
+
{open && !disabled && (
|
|
150
|
+
isMobile ? (
|
|
151
|
+
<BottomSheet open={open} onClose={() => setOpen(false)}>
|
|
152
|
+
<div className="px-1 pb-2">{content}</div>
|
|
153
|
+
</BottomSheet>
|
|
154
|
+
) : (
|
|
155
|
+
<div
|
|
156
|
+
ref={panelRef}
|
|
157
|
+
role="listbox"
|
|
158
|
+
aria-label="Model and thinking"
|
|
159
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
160
|
+
onClick={(e) => e.stopPropagation()}
|
|
161
|
+
className="absolute bottom-full left-0 mb-1 z-50 w-80 rounded-lg border border-border popover-solid shadow-[var(--shadow-panel)] max-h-[70vh] overflow-y-auto"
|
|
162
|
+
>
|
|
163
|
+
{content}
|
|
164
|
+
</div>
|
|
165
|
+
)
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function SectionLabel({ children }: { children: ReactNode }) {
|
|
172
|
+
return <div className="px-3 py-1.5 text-xs font-medium text-text-secondary">{children}</div>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function Separator() {
|
|
176
|
+
return <div className="my-1 border-t border-border" />;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function OptionRow({ active, onClick, children }: { active: boolean; onClick: () => void; children: ReactNode }) {
|
|
180
|
+
return (
|
|
181
|
+
<button
|
|
182
|
+
type="button"
|
|
183
|
+
onClick={onClick}
|
|
184
|
+
className={`w-full flex items-center gap-3 px-3 min-h-[44px] text-left text-sm text-text-primary transition-colors hover:bg-surface-elevated ${active ? "bg-surface-elevated" : ""}`}
|
|
185
|
+
>
|
|
186
|
+
{children}
|
|
187
|
+
</button>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "@/components/ui/dialog";
|
|
9
9
|
|
|
10
10
|
const DEFAULT_MESSAGE = "Hello! Reply briefly.";
|
|
11
|
-
const DEFAULT_MODEL = "claude-opus-
|
|
11
|
+
const DEFAULT_MODEL = "claude-opus-5";
|
|
12
12
|
|
|
13
13
|
type EndpointFormat = "anthropic" | "openai";
|
|
14
14
|
|
|
@@ -159,10 +159,12 @@ function ProxyTestForm({ authKey, baseUrl }: ProxyTestDialogProps) {
|
|
|
159
159
|
onChange={(e) => setModel(e.target.value)}
|
|
160
160
|
className="h-8 w-full rounded-md border bg-background px-2 text-[11px]"
|
|
161
161
|
>
|
|
162
|
+
<option value="claude-opus-5">claude-opus-5</option>
|
|
162
163
|
<option value="claude-fable-5">claude-fable-5</option>
|
|
163
164
|
<option value="claude-opus-4-8">claude-opus-4-8</option>
|
|
164
165
|
<option value="claude-opus-4-7">claude-opus-4-7</option>
|
|
165
166
|
<option value="claude-opus-4-6">claude-opus-4-6</option>
|
|
167
|
+
<option value="claude-sonnet-5">claude-sonnet-5</option>
|
|
166
168
|
<option value="claude-sonnet-4-6">claude-sonnet-4-6</option>
|
|
167
169
|
<option value="claude-haiku-4-5">claude-haiku-4-5</option>
|
|
168
170
|
</select>
|