@hienlh/ppm 0.18.11 → 0.18.13
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/AGENTS.md +17 -5
- package/CHANGELOG.md +27 -0
- package/CLAUDE.md +3 -0
- package/README.md +87 -29
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +19 -1
- package/dist/web/assets/{adaptive-context-menu-DGCKKeTy.js → adaptive-context-menu-CbgPYQmj.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-BOHHTTe7.js → ai-resource-editor-unM2HNmg.js} +1 -1
- package/dist/web/assets/{ai-settings-section-BigC_nZR.js → ai-settings-section-CdktdrwF.js} +1 -1
- package/dist/web/assets/{angular-html-BYPaydvh.js → angular-html-DcpbdcNG.js} +1 -1
- package/dist/web/assets/{angular-ts-Bb4AMKlk.js → angular-ts-Bv_WIk34.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/{api-settings-Bd3M4YpI.js → api-settings-BdCtex9i.js} +1 -1
- package/dist/web/assets/api-settings-DfXViPKz.js +1 -0
- package/dist/web/assets/{api-themes-Cpx419SL.js → api-themes-pRRoLvgD.js} +1 -1
- package/dist/web/assets/{apl-DB2VjFCS.js → apl-D5_lLszu.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +1 -0
- package/dist/web/assets/{astro-BeoJdVpk.js → astro-CBHwddYL.js} +1 -1
- package/dist/web/assets/{audio-preview-BgFuTn-0.js → audio-preview-BD1iDovf.js} +1 -1
- package/dist/web/assets/{blade-Cf2q1sPf.js → blade-DGe1d2Pq.js} +1 -1
- package/dist/web/assets/{bsl-W1kOaNpu.js → bsl-2lyAzRJA.js} +1 -1
- package/dist/web/assets/{button-5zTrkEoN.js → button-2WCAS8dI.js} +1 -1
- package/dist/web/assets/{cairo-BoAZFcje.js → cairo-DjzZeJrq.js} +1 -1
- package/dist/web/assets/chat-tab-DzK8LGti.js +15 -0
- package/dist/web/assets/{cobol-B0Nm7RXj.js → cobol-cC3XIuAJ.js} +1 -1
- package/dist/web/assets/code-editor-CuMfPN9X.js +10 -0
- package/dist/web/assets/{coffee-BakMn3on.js → coffee-4eGboVoU.js} +1 -1
- package/dist/web/assets/{conflict-editor-BUs5k9q_.js → conflict-editor-DdJag4QK.js} +1 -1
- package/dist/web/assets/{context-menu-Bq92km4s.js → context-menu-BO1c9jKy.js} +1 -1
- package/dist/web/assets/{cpp-BRAJQHK2.js → cpp-DF9_G-OU.js} +1 -1
- package/dist/web/assets/{crystal-kqqO3zkK.js → crystal-OavZlzcm.js} +1 -1
- package/dist/web/assets/{csv-preview-ByoDgLAZ.js → csv-preview-DnXwouF3.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-Brkf9z41.js → data-grid-overlay-editor-B9Gpjm3l.js} +1 -1
- package/dist/web/assets/{database-viewer-Btc3h8Yu.js → database-viewer-Dk8oGyZx.js} +1 -1
- package/dist/web/assets/{dialog-DwtGlowE.js → dialog-B-l2ZxVm.js} +1 -1
- package/dist/web/assets/{diff-viewer-CZaDnR4w.js → diff-viewer-eaUBjLCH.js} +1 -1
- package/dist/web/assets/{docx-preview-DAD_vg59.js → docx-preview-DAdHPlhZ.js} +1 -1
- package/dist/web/assets/{edge-Cq1ZKZuE.js → edge-TzYh8OlN.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-B_VE7ppQ.js → edit-diff-preview-DlwP3UIU.js} +1 -1
- package/dist/web/assets/{elixir-BM_JoJRk.js → elixir-Cy3RhKKH.js} +1 -1
- package/dist/web/assets/{elm-Dw7oO08Y.js → elm-BTBZdweK.js} +1 -1
- package/dist/web/assets/{erb-CfKOGAfk.js → erb-dv1mI0y4.js} +1 -1
- package/dist/web/assets/{erlang-DlDmGVeE.js → erlang-DjeiDrkl.js} +1 -1
- package/dist/web/assets/{esm-DGAP0GFh.js → esm-FRwvcesu.js} +1 -1
- package/dist/web/assets/explorer-body-DkCkSVrN.js +2 -0
- package/dist/web/assets/explorer-window-content-MsnTVngu.js +1 -0
- package/dist/web/assets/{extension-store-Cpfh2vMm.js → extension-store-DUaZINyC.js} +1 -1
- package/dist/web/assets/{extension-webview-B2OnjuCu.js → extension-webview-W0qRfaAU.js} +1 -1
- package/dist/web/assets/{file-store-aOERIEo-.js → file-store-yReVbR8q.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-XnAhQWxS.js → fortran-fixed-form-BCH9DAIJ.js} +1 -1
- package/dist/web/assets/{fsharp-CdNvy0PG.js → fsharp-C93NYKYD.js} +1 -1
- package/dist/web/assets/{gdresource-DxuG5i8i.js → gdresource-BcIEE2eX.js} +1 -1
- package/dist/web/assets/{git-commit-3lMonsur.js → git-commit-BVjlXEy8.js} +1 -1
- package/dist/web/assets/{git-log-panel-Co0PBPHC.js → git-log-panel-2_4HuP47.js} +1 -1
- package/dist/web/assets/{git-rebase-N8Z9kIMf.js → git-rebase-Bj_21B1V.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +1 -0
- package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +138 -0
- package/dist/web/assets/{glimmer-js-Dp9OXiib.js → glimmer-js-C20yj4Zv.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CDVKBcDj.js → glimmer-ts-ipbYC7kh.js} +1 -1
- package/dist/web/assets/{glsl-DPJxI4us.js → glsl-Dovazu3t.js} +1 -1
- package/dist/web/assets/{graphql-BrFhaQQl.js → graphql-DpMlM7hC.js} +1 -1
- package/dist/web/assets/{group-chat-tab-rOd8J8GC.js → group-chat-tab-DRLDHOp6.js} +1 -1
- package/dist/web/assets/{hack-BzXbDMlA.js → hack-gc7LCBuV.js} +1 -1
- package/dist/web/assets/{haml-CPpkX0s5.js → haml-vtlFm9gm.js} +1 -1
- package/dist/web/assets/{handlebars-BqrApy52.js → handlebars-DGKhpCji.js} +1 -1
- package/dist/web/assets/{html-DmjUl-OA.js → html-DvfhQbXb.js} +1 -1
- package/dist/web/assets/{html-derivative-Cnd6XFR5.js → html-derivative-C3oTSSCN.js} +1 -1
- package/dist/web/assets/{http-DMLWYun2.js → http-SG6zLTo7.js} +1 -1
- package/dist/web/assets/{hurl-CKkCVxbj.js → hurl-BWXsYlqW.js} +1 -1
- package/dist/web/assets/{hxml-CAx19TtQ.js → hxml-DUdMzaoh.js} +1 -1
- package/dist/web/assets/{image-preview-DHUoylQ1.js → image-preview-DxgFOV1T.js} +1 -1
- package/dist/web/assets/index-CBn4SOoT.js +52 -0
- package/dist/web/assets/index-DWpNR3nB.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +1 -0
- package/dist/web/assets/{input-DcEk7mA3.js → input-DuAF-Stj.js} +1 -1
- package/dist/web/assets/{jinja-BmB9YN9l.js → jinja-955w9RKM.js} +1 -1
- package/dist/web/assets/{jison-BaVsKKpw.js → jison-WWfqPo50.js} +1 -1
- package/dist/web/assets/{julia-BgVBLveX.js → julia-BFrfnaNl.js} +1 -1
- package/dist/web/assets/{just-C2ENIbl9.js → just-BrZdyR_I.js} +1 -1
- package/dist/web/assets/{keybindings-store-BGIyqXHI.js → keybindings-store-0Wqfa7Qp.js} +1 -1
- package/dist/web/assets/keybindings-store-d54pv2NK.js +1 -0
- package/dist/web/assets/{latex-BiDIWxKL.js → latex-CwtPPASD.js} +1 -1
- package/dist/web/assets/{liquid-NxpapreE.js → liquid-CYZuvDDd.js} +1 -1
- package/dist/web/assets/{lua-DVepQvwH.js → lua-BV095ZyY.js} +1 -1
- package/dist/web/assets/markdown-renderer-BTS8ifdu.js +1 -0
- package/dist/web/assets/{markdown-renderer-DlcDhgrF.js → markdown-renderer-CnRcKK3d.js} +3 -3
- package/dist/web/assets/{marko-CJ2606Xl.js → marko-CNeljZ1C.js} +1 -1
- package/dist/web/assets/{mdc-D2bjqclE.js → mdc-JfGapShy.js} +1 -1
- package/dist/web/assets/{media-url-H17XJF-I.js → media-url-Bm4PsduE.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-g-r2hCFa.js → mobile-bottom-sheet-M5iZDX7y.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +1 -0
- package/dist/web/assets/{nextflow-BtJwUbfF.js → nextflow-DD5C8nqX.js} +1 -1
- package/dist/web/assets/{nginx-j4gyIhHE.js → nginx-Ca7B__x_.js} +1 -1
- package/dist/web/assets/{nim-ClH_gjP5.js → nim-B8FogsDw.js} +1 -1
- package/dist/web/assets/notification-store-Yvy1I8gd.js +1 -0
- package/dist/web/assets/{number-overlay-editor-C1EqJnWq.js → number-overlay-editor-DG9xJwEG.js} +1 -1
- package/dist/web/assets/{overview-panel-vTeeJYVZ.js → overview-panel-s53bnl0q.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +1 -0
- package/dist/web/assets/{panel-store-BSnkjgHt.js → panel-store-B4rijFKN.js} +1 -1
- package/dist/web/assets/{pdf-preview-CqFGRY0Z.js → pdf-preview-DYt3aBzM.js} +1 -1
- package/dist/web/assets/{perl-C9H7X7CT.js → perl-BR78hS8R.js} +1 -1
- package/dist/web/assets/{php-Bx2U-cQ_.js → php-DXBOMC9z.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +1 -0
- package/dist/web/assets/{postgres-viewer-DHwcQucn.js → postgres-viewer-BvoWSuoZ.js} +3 -3
- package/dist/web/assets/{process-table-BGRJNlpn.js → process-table-BYwKigiD.js} +1 -1
- package/dist/web/assets/{project-store--nvQiy75.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/{pug-Cw74sKsE.js → pug-CvTwVVKV.js} +1 -1
- package/dist/web/assets/{qml-BXD94Eqz.js → qml-CPSoZ1-B.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +1 -0
- package/dist/web/assets/{razor-DZ0-Ge0S.js → razor-BUA1YKtF.js} +1 -1
- package/dist/web/assets/{rst-Dtdh0QUj.js → rst-BJsq7ISz.js} +1 -1
- package/dist/web/assets/{ruby-BC51BW2Q.js → ruby-DSDMeYf5.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Sd3KyjN4.js → run-in-terminal-C9xpskFn.js} +1 -1
- package/dist/web/assets/{sas-BN8i4qOU.js → sas-B1nJOjbN.js} +1 -1
- package/dist/web/assets/{scroll-area-0gljZ6lF.js → scroll-area-DBcG1fQo.js} +1 -1
- package/dist/web/assets/{scss-H-QtA7Rz.js → scss-BZFyRiQn.js} +1 -1
- package/dist/web/assets/{send-to-chat-u6m8l-rH.js → send-to-chat-BIg70MUZ.js} +1 -1
- package/dist/web/assets/{settings-store-CHY6Kzin.js → settings-store-GV5pkkke.js} +2 -2
- package/dist/web/assets/{settings-tab-IY6K1MiZ.js → settings-tab-BePvIcjd.js} +1 -1
- package/dist/web/assets/settings-tab-Br-lJSPa.js +1 -0
- package/dist/web/assets/{shaderlab-CalNsj_x.js → shaderlab-laINRXBo.js} +1 -1
- package/dist/web/assets/{shellsession-BFrcoR8x.js → shellsession-BpOOvWVR.js} +1 -1
- package/dist/web/assets/{soy-CNbku7vP.js → soy-BW4d91Sa.js} +1 -1
- package/dist/web/assets/{sparql-BN_orcVp.js → sparql-BsvvyoHn.js} +1 -1
- package/dist/web/assets/{sql-query-editor-C6pC3I8l.js → sql-query-editor-qQce5QJ6.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DTr5PzUb.js → sqlite-viewer-yhc0YNvu.js} +1 -1
- package/dist/web/assets/{stata-C6a5Ijzq.js → stata-K-dZInUt.js} +1 -1
- package/dist/web/assets/{surrealql-ClplI_HY.js → surrealql-CpNp2sgf.js} +1 -1
- package/dist/web/assets/{svelte-DNm-UNXB.js → svelte-BG6iBA8R.js} +1 -1
- package/dist/web/assets/{switch-DKvKya9y.js → switch-_NcrktLe.js} +1 -1
- package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +2 -0
- package/dist/web/assets/system-monitor-tab-BclYXFTe.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BUqRsPK2.js → system-monitor-window-content-CaOhD0AU.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-BnqGPu8a.js → tab-host-window-content-f_Rc5rJK.js} +1 -1
- package/dist/web/assets/{tab-store-CJuJRapl.js → tab-store-tFnyPmm4.js} +1 -1
- package/dist/web/assets/team-member-sheet-QDHoYCs0.js +2 -0
- package/dist/web/assets/{team-member-window-content-T4Q5JI-G.js → team-member-window-content-B1jkKkDi.js} +1 -1
- package/dist/web/assets/{templ-CSdqVobu.js → templ-gxNKXmt3.js} +1 -1
- package/dist/web/assets/{terminal-tab-Bb2rZegQ.js → terminal-tab-UvwM0sty.js} +3 -3
- package/dist/web/assets/{tex-DzSJ4kcZ.js → tex-C0ON27g5.js} +1 -1
- package/dist/web/assets/{tool-cards-lhqhrDTT.js → tool-cards-jB7GT-wj.js} +4 -4
- package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +1 -0
- package/dist/web/assets/{ts-tags-C28w3bTg.js → ts-tags-BX_CTVgQ.js} +1 -1
- package/dist/web/assets/{twig-i5jY3QXB.js → twig-CwL7XnU0.js} +1 -1
- package/dist/web/assets/{use-blob-url-Dsq9OjTh.js → use-blob-url-DcRcjNTE.js} +1 -1
- package/dist/web/assets/use-coarse-long-press-BmI_GVW5.js +1 -0
- package/dist/web/assets/{use-monaco-theme-BZvlsoBv.js → use-monaco-theme--9tOOkDA.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DkiEjFYE.js → use-open-team-member-1hRsHLKg.js} +1 -1
- package/dist/web/assets/{use-websocket-B8R50f7f.js → use-websocket-rxJLbFqy.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-By4AVvJY.js → vendor-mermaid-CHTa6D69.js} +1 -1
- package/dist/web/assets/video-preview-DMhnf2Gi.js +1 -0
- package/dist/web/assets/{vue-Ceyet_lv.js → vue-Bykm6wSS.js} +1 -1
- package/dist/web/assets/{vue-html-GuU7g_hg.js → vue-html-CETdK_iV.js} +1 -1
- package/dist/web/assets/{vue-vine-CxXoaJr2.js → vue-vine-DiJb6fO5.js} +1 -1
- package/dist/web/assets/{window-store-CsEW6jiE.js → window-store-CQ1lghvv.js} +1 -1
- package/dist/web/assets/{xml-CbqOVhkP.js → xml-BNw8J5YW.js} +1 -1
- package/dist/web/assets/{xsl-KfAmCauy.js → xsl-CsG9L31b.js} +1 -1
- package/dist/web/index.html +46 -45
- package/dist/web/sw.js +1 -1
- package/docs/architecture/ai-chat-and-providers.md +309 -0
- package/docs/architecture/data-and-storage.md +356 -0
- package/docs/architecture/extensions.md +496 -0
- package/docs/architecture/integrations.md +214 -0
- package/docs/architecture/workspace-and-ui.md +391 -0
- package/docs/claude-agent-sdk-reference.md +1 -1
- package/docs/code-patterns.md +361 -0
- package/docs/code-standards.md +8 -358
- package/docs/codebase-subsystems.md +364 -0
- package/docs/codebase-summary.md +34 -379
- package/docs/deployment-guide.md +280 -490
- package/docs/design-guidelines.md +1 -1
- package/docs/lessons-learned.md +27 -10
- package/docs/project-changelog.md +18 -806
- package/docs/project-overview-pdr.md +117 -111
- package/docs/project-roadmap.md +89 -133
- package/docs/system-architecture.md +77 -1826
- package/package.json +2 -2
- package/src/cli/commands/autostart.ts +2 -2
- package/src/cli/commands/config-cmd.ts +12 -3
- package/src/cli/commands/status.ts +3 -2
- package/src/cli/commands/stop.ts +32 -4
- package/src/server/index.ts +17 -3
- package/src/server/routes/chat-unread.ts +36 -0
- package/src/server/routes/named-tunnel.ts +132 -0
- package/src/server/routes/tunnel.ts +13 -0
- package/src/server/ws/chat.ts +5 -2
- package/src/services/autostart-register.ts +47 -29
- package/src/services/cloud.service.ts +26 -1
- package/src/services/config-secret-keys.ts +48 -0
- package/src/services/config.service.ts +1 -1
- package/src/services/db.service.ts +62 -12
- package/src/services/extension-rpc-handlers.ts +9 -1
- package/src/services/file-watcher/watch-tree.ts +67 -33
- package/src/services/file-watcher.service.ts +28 -10
- package/src/services/fs-credential-path-guard.ts +103 -0
- package/src/services/fs-path-guard.service.ts +14 -66
- package/src/services/named-tunnel/cloudflare-dns-api.ts +40 -0
- package/src/services/named-tunnel/cloudflare-zone-api.ts +28 -0
- package/src/services/named-tunnel/cloudflared-cert.ts +95 -0
- package/src/services/named-tunnel/cloudflared-exec.ts +50 -0
- package/src/services/named-tunnel/cloudflared-login-helpers.ts +98 -0
- package/src/services/named-tunnel/cloudflared-login.service.ts +182 -0
- package/src/services/named-tunnel/hostname-rules.ts +58 -0
- package/src/services/named-tunnel/login-output-parser.ts +36 -0
- package/src/services/named-tunnel/named-tunnel-args.ts +110 -0
- package/src/services/named-tunnel/named-tunnel-config.ts +67 -0
- package/src/services/named-tunnel/named-tunnel-probe-state.ts +95 -0
- package/src/services/named-tunnel/named-tunnel-readiness.ts +59 -0
- package/src/services/named-tunnel/named-tunnel-runtime.ts +42 -0
- package/src/services/named-tunnel/named-tunnel-setup-confirm.ts +98 -0
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +191 -0
- package/src/services/supervisor-state.ts +67 -2
- package/src/services/supervisor.ts +454 -157
- package/src/services/tunnel-registry-parse.ts +27 -0
- package/src/services/tunnel-registry.service.ts +2 -15
- package/src/services/tunnel.service.ts +12 -3
- package/src/types/config.ts +31 -0
- package/src/web/app.tsx +5 -0
- package/src/web/components/chat/chat-history-bar.tsx +95 -98
- package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +57 -0
- package/src/web/components/chat/session-debug-dialog.tsx +35 -27
- package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +111 -0
- package/src/web/components/layout/cloud-share-popover.tsx +56 -14
- package/src/web/components/shared/connection-lost-overlay.tsx +44 -5
- package/src/web/components/tunnels/named-tunnel/hostname-validation.ts +14 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +96 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-hostname-field.tsx +51 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +105 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +159 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +197 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +57 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-step-reducer.ts +191 -0
- package/src/web/components/tunnels/named-tunnel/use-named-tunnel-setup.ts +212 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/hooks/use-global-events.ts +3 -1
- package/src/web/lib/api-named-tunnel.ts +54 -0
- package/src/web/lib/last-known-local-url.ts +23 -0
- package/src/web/stores/notification-store.ts +20 -1
- package/.spike/cdp-client.ts +0 -116
- package/.spike/cleanup-ppm-workspace.mjs +0 -44
- package/.spike/group-protected-probe.ts +0 -21
- package/.spike/pip-eval.ts +0 -17
- package/.spike/pip-page-helpers.js +0 -119
- package/.spike/pip-results-editor-glue.json +0 -105
- package/.spike/pip-results-editor.json +0 -158
- package/.spike/pip-results-input-isolation.json +0 -28
- package/.spike/pip-results-key-probe.json +0 -58
- package/.spike/pip-results-listener-survival.json +0 -98
- package/.spike/pip-results-listeners.json +0 -36
- package/.spike/pip-results-pagehide.json +0 -92
- package/.spike/pip-results-resize.json +0 -104
- package/.spike/pip-results-terminal-debug.json +0 -99
- package/.spike/pip-results-terminal-v2.json +0 -191
- package/.spike/pip-results-terminal.json +0 -220
- package/.spike/pip-results-xterm-trace.json +0 -73
- package/.spike/pip-spike-common.ts +0 -83
- package/.spike/pip-spike-editor-glue.ts +0 -82
- package/.spike/pip-spike-editor.ts +0 -129
- package/.spike/pip-spike-input-isolation.ts +0 -38
- package/.spike/pip-spike-key-probe.ts +0 -34
- package/.spike/pip-spike-listener-survival.ts +0 -54
- package/.spike/pip-spike-listeners.ts +0 -19
- package/.spike/pip-spike-pagehide.ts +0 -59
- package/.spike/pip-spike-resize.ts +0 -42
- package/.spike/pip-spike-setup.ts +0 -63
- package/.spike/pip-spike-terminal-debug.ts +0 -81
- package/.spike/pip-spike-terminal-v2.ts +0 -139
- package/.spike/pip-spike-terminal.ts +0 -169
- package/.spike/pip-spike-xterm-trace.ts +0 -36
- package/.spike/probe-drag-memo-error.mjs +0 -67
- package/.spike/read-dev-db.ts +0 -15
- package/.spike/s.err +0 -63
- package/.spike/scratch-editor-target.ts +0 -7
- package/.spike/smoke-unified-chrome-pip.mjs +0 -353
- package/.spike/tunnel.err +0 -63
- package/.spike/vite-restart.err +0 -663
- package/.spike/w.err +0 -571
- package/dist/web/assets/api-client-DTZEgjBQ.js +0 -1
- package/dist/web/assets/api-settings-kd1j4iR8.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +0 -1
- package/dist/web/assets/chat-tab-E3bf_V8X.js +0 -15
- package/dist/web/assets/code-editor-BUlM2gXk.js +0 -10
- package/dist/web/assets/explorer-body-9aBmyVpZ.js +0 -2
- package/dist/web/assets/explorer-window-content-DRtqoSPA.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +0 -1
- package/dist/web/assets/glide-data-grid-C4_zIneN.js +0 -138
- package/dist/web/assets/index-BImUaigO.js +0 -52
- package/dist/web/assets/index-CSfi6DSU.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +0 -1
- package/dist/web/assets/keybindings-store-CzV2sPls.js +0 -1
- package/dist/web/assets/markdown-renderer-CcbR33VG.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +0 -1
- package/dist/web/assets/notification-store-DV5QtUTg.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +0 -1
- package/dist/web/assets/settings-tab-Dic6TKkU.js +0 -1
- package/dist/web/assets/system-monitor-body-iwGiCns9.js +0 -2
- package/dist/web/assets/system-monitor-tab-DdXQhEel.js +0 -1
- package/dist/web/assets/team-member-sheet-LkAFSCvb.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +0 -1
- package/dist/web/assets/video-preview-m0-3p7QZ.js +0 -1
- /package/dist/web/assets/{abap-C6nB04tn.js → abap-DGlrX_8G.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Vx4rw2sC.js → actionscript-3-CUqRtUJE.js} +0 -0
- /package/dist/web/assets/{ada-Ccd8vM20.js → ada-CY3Spa33.js} +0 -0
- /package/dist/web/assets/{andromeeda-DnBX5xMg.js → andromeeda-Lxs2TJn7.js} +0 -0
- /package/dist/web/assets/{apache-B-9_sx0g.js → apache-DfxgGdgM.js} +0 -0
- /package/dist/web/assets/{apex-_wPVBYoL.js → apex-QVDF1dnI.js} +0 -0
- /package/dist/web/assets/{api-client-aQb5hHE5.js → api-client-oY1tC3tM.js} +0 -0
- /package/dist/web/assets/{applescript-CcvD-qr0.js → applescript-pwJ8QXh-.js} +0 -0
- /package/dist/web/assets/{apply-theme-BeLRfsSu.js → apply-theme-B_bMVL6a.js} +0 -0
- /package/dist/web/assets/{ara-C40Ztoms.js → ara-mPjeX5YX.js} +0 -0
- /package/dist/web/assets/{asciidoc-CybYkJV4.js → asciidoc-Bf7LsDkI.js} +0 -0
- /package/dist/web/assets/{asm-9eOKwzR0.js → asm-rRKC1OUx.js} +0 -0
- /package/dist/web/assets/{aurora-x-B5ZH4t7t.js → aurora-x-DwQr0DkG.js} +0 -0
- /package/dist/web/assets/{awk-Dqilob-2.js → awk-Bpt768fN.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CPgRhf5r.js → ayu-dark-D9lwQHMs.js} +0 -0
- /package/dist/web/assets/{ayu-light-Do06Q52f.js → ayu-light-B4-ptrU5.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-_BhxcrxY.js → ayu-mirage-DAyRzH5Q.js} +0 -0
- /package/dist/web/assets/{ballerina-DDwK54QW.js → ballerina-Cgdpj1HC.js} +0 -0
- /package/dist/web/assets/{bat-BGyGlzGk.js → bat-4FrN36S2.js} +0 -0
- /package/dist/web/assets/{beancount-CRWBdapI.js → beancount-DkcxQER3.js} +0 -0
- /package/dist/web/assets/{berry-DbSFn2gO.js → berry-Dnuut-Ml.js} +0 -0
- /package/dist/web/assets/{bibtex-QpsV9gm9.js → bibtex-CCDwmbuA.js} +0 -0
- /package/dist/web/assets/{bicep-CxS8CUve.js → bicep-BC0ryoGM.js} +0 -0
- /package/dist/web/assets/{bird2-CuZhQzYR.js → bird2-C50V_A-0.js} +0 -0
- /package/dist/web/assets/{c-9bnDqE3g.js → c-Ddorhq0U.js} +0 -0
- /package/dist/web/assets/{c3-JMZTrOhA.js → c3-DdIf5r9E.js} +0 -0
- /package/dist/web/assets/{cadence-DsDNayJS.js → cadence-s9pd1Wng.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BJfpynya.js → can-open-in-ppm-C6hZV1OP.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DYIKXXBe.js → catppuccin-frappe-CzJNTyu-.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-IYYOnUJ8.js → catppuccin-latte-BNpt-m7s.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BrJ_7mfS.js → catppuccin-macchiato-FYGkfsJ5.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-gzSu7v7-.js → catppuccin-mocha-it2g5D1q.js} +0 -0
- /package/dist/web/assets/{clarity-DLzsC0Hb.js → clarity-BVFBTjMe.js} +0 -0
- /package/dist/web/assets/{clipboard-7fw0Shic.js → clipboard-BWznymz7.js} +0 -0
- /package/dist/web/assets/{clojure-DS1BvoTt.js → clojure-BSYvXMI8.js} +0 -0
- /package/dist/web/assets/{cmake-CDvsJwYp.js → cmake-DGEqH-Np.js} +0 -0
- /package/dist/web/assets/{codeowners-BjsYiflJ.js → codeowners-D8FCOxWg.js} +0 -0
- /package/dist/web/assets/{codeql-CG-7ARat.js → codeql-COV0eluG.js} +0 -0
- /package/dist/web/assets/{common-lisp-DSO1Lbh9.js → common-lisp-xHnZUlgt.js} +0 -0
- /package/dist/web/assets/{coq-DYdh3oP_.js → coq-Cs_2i3Xg.js} +0 -0
- /package/dist/web/assets/{csharp-DxEBz5s0.js → csharp-B1ONB4PT.js} +0 -0
- /package/dist/web/assets/{css-dal3Q-8C.js → css-BgGQbvrZ.js} +0 -0
- /package/dist/web/assets/{csv-CpqjMGiA.js → csv-CNHmQBzs.js} +0 -0
- /package/dist/web/assets/{csv-parser-3umBA4NY.js → csv-parser-DWR24J17.js} +0 -0
- /package/dist/web/assets/{cue-DinoJ_5k.js → cue-ZXAKzwIy.js} +0 -0
- /package/dist/web/assets/{cypher-DzxqmkUW.js → cypher-4PL5bmT5.js} +0 -0
- /package/dist/web/assets/{d-BxNwJuEC.js → d-CEsV3RT2.js} +0 -0
- /package/dist/web/assets/{dark-plus-Clmv3V-0.js → dark-plus-NtFnYSDz.js} +0 -0
- /package/dist/web/assets/{dart-BWVtXD35.js → dart-CKHn4mDf.js} +0 -0
- /package/dist/web/assets/{data-grid-types-B4n51Fck.js → data-grid-types-CxU8oo-j.js} +0 -0
- /package/dist/web/assets/{dax-DsImuWgJ.js → dax-Cmkj1I_M.js} +0 -0
- /package/dist/web/assets/{desktop-B0jJuG2r.js → desktop-DJLalvPQ.js} +0 -0
- /package/dist/web/assets/{diff-CWqAAxoE.js → diff-BnS2wpj6.js} +0 -0
- /package/dist/web/assets/{dist-C0eNNfbt.js → dist-CuIdRyUB.js} +0 -0
- /package/dist/web/assets/{dist-B-EXdSYE.js → dist-uYjs3Wv6.js} +0 -0
- /package/dist/web/assets/{docker-CYFCJdIL.js → docker-CovNy7ml.js} +0 -0
- /package/dist/web/assets/{dotenv-CKDUjURg.js → dotenv-Gh4w3I6B.js} +0 -0
- /package/dist/web/assets/{dracula-CSoPspHE.js → dracula-NF0tAHXl.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BuFjw5L1.js → dracula-soft-Cj_qBurK.js} +0 -0
- /package/dist/web/assets/{dream-maker-B6PdDRZu.js → dream-maker-CRuZPcl7.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CMcXvD0G.js → emacs-lisp-BACmC9qj.js} +0 -0
- /package/dist/web/assets/{everforest-dark-cU8-wYzi.js → everforest-dark-Dd9av4QG.js} +0 -0
- /package/dist/web/assets/{everforest-light-KhorllqK.js → everforest-light-C-jsss-B.js} +0 -0
- /package/dist/web/assets/{external-link-B8BynhvY.js → external-link-B6Hl0U4X.js} +0 -0
- /package/dist/web/assets/{fennel-BCELZK87.js → fennel-BJA9XnL8.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-CZ4DiR75.js → file-exclamation-point-JzhTU2TX.js} +0 -0
- /package/dist/web/assets/{fish-BQc0y_jy.js → fish-CKlhNSc2.js} +0 -0
- /package/dist/web/assets/{fluent-BTINnwF6.js → fluent-wfglRk5D.js} +0 -0
- /package/dist/web/assets/{folder-symlink-B8XrtvQK.js → folder-symlink-Dx1hOk8X.js} +0 -0
- /package/dist/web/assets/{format-bytes-WVcFxJuT.js → format-bytes-utwHMTry.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BQim9wDz.js → fortran-free-form-BlXCdid3.js} +0 -0
- /package/dist/web/assets/{gdscript-03w5u8un.js → gdscript-BwAVG6Et.js} +0 -0
- /package/dist/web/assets/{gdshader-Dp2daNzU.js → gdshader-DPc_T7ib.js} +0 -0
- /package/dist/web/assets/{genie-CoNcjPiD.js → genie-B-zZSLCX.js} +0 -0
- /package/dist/web/assets/{gherkin-NrPOSPQL.js → gherkin-CJ0ajpGs.js} +0 -0
- /package/dist/web/assets/{github-dark-z4RE9ZqA.js → github-dark-Dz8ZkzSP.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DOKw-1cI.js → github-dark-default-CNUYo_kp.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-DJRNMkEA.js → github-dark-dimmed-241z_Ycx.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Bj7jJ_Gs.js → github-dark-high-contrast-7r6yZZYH.js} +0 -0
- /package/dist/web/assets/{github-light-B4k_yMFu.js → github-light-C8gmZn-D.js} +0 -0
- /package/dist/web/assets/{github-light-default-BHc-Uzhs.js → github-light-default-GAEgq1eA.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CHisIywL.js → github-light-high-contrast-3z91352E.js} +0 -0
- /package/dist/web/assets/{gleam-CfbypZ4q.js → gleam-C7vxW5zd.js} +0 -0
- /package/dist/web/assets/{gn-YoAjyhJa.js → gn-CyJIXwHP.js} +0 -0
- /package/dist/web/assets/{gnuplot-xVkc1f1c.js → gnuplot-CqLbGgPd.js} +0 -0
- /package/dist/web/assets/{go-GPCwP3Gu.js → go-DdDcK9aX.js} +0 -0
- /package/dist/web/assets/{groovy-Crh4Yww9.js → groovy-UYHggEqH.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BluUFuDS.js → gruvbox-dark-hard-B4ierkJn.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Cxk1FWQp.js → gruvbox-dark-medium-DYwkDSZ6.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CHXMiFmj.js → gruvbox-dark-soft-CVL4P-Lz.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-B6eKPjAD.js → gruvbox-light-hard-BJ0LewUZ.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-pDwMm1ut.js → gruvbox-light-medium-CaqqjeGJ.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Cxu6kHqK.js → gruvbox-light-soft-ByKMoVNr.js} +0 -0
- /package/dist/web/assets/{haskell-C6JKHjbK.js → haskell-BilYRVPp.js} +0 -0
- /package/dist/web/assets/{haxe-BuBNdF0a.js → haxe-Brs9Bj1s.js} +0 -0
- /package/dist/web/assets/{hcl-DZpDdGj2.js → hcl-ClrZpol-.js} +0 -0
- /package/dist/web/assets/{hjson-CBkhYRuo.js → hjson-9PvHnYMX.js} +0 -0
- /package/dist/web/assets/{hlsl-C5nMxnp9.js → hlsl-BbBk4vqV.js} +0 -0
- /package/dist/web/assets/{horizon-VAFvA6R6.js → horizon-E5Py0cny.js} +0 -0
- /package/dist/web/assets/{horizon-bright-BC0xn7T3.js → horizon-bright-DVCsNGEk.js} +0 -0
- /package/dist/web/assets/{houston-BxrUivmz.js → houston-JdFEax9Y.js} +0 -0
- /package/dist/web/assets/{hy-DLtOXoaH.js → hy-W_W3tLKT.js} +0 -0
- /package/dist/web/assets/{image-gallery-Gnf7TdCR.js → image-gallery-5IURd6db.js} +0 -0
- /package/dist/web/assets/{imba-BdpFXM2W.js → imba-CfAqJz7T.js} +0 -0
- /package/dist/web/assets/{ini-BInJO_T0.js → ini-C_3gvKWs.js} +0 -0
- /package/dist/web/assets/{java-Dxuddnkb.js → java-CKqtXZ-j.js} +0 -0
- /package/dist/web/assets/{javascript-CVamJfLY.js → javascript-C7gSEfae.js} +0 -0
- /package/dist/web/assets/{json-BLZ0ozYk.js → json-BoN6e11T.js} +0 -0
- /package/dist/web/assets/{json5-BnXzmMKs.js → json5-CgE7C8HW.js} +0 -0
- /package/dist/web/assets/{jsonc-CSgkSga7.js → jsonc-DjyoattY.js} +0 -0
- /package/dist/web/assets/{jsonl-CY5W2nF2.js → jsonl-lI82wGSw.js} +0 -0
- /package/dist/web/assets/{jsonnet-CEJYH3sF.js → jsonnet-CszEI8va.js} +0 -0
- /package/dist/web/assets/{jssm-CsJc7Uhk.js → jssm-CZXDlszb.js} +0 -0
- /package/dist/web/assets/{jsx-jCiRaHU7.js → jsx-BeRz0eEz.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DfoA_GxX.js → kanagawa-dragon-BOmnUiyd.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-BOLYJpWd.js → kanagawa-lotus-ffU_P5w-.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DfKlbT1G.js → kanagawa-wave-DKTJZrMu.js} +0 -0
- /package/dist/web/assets/{kdl-CmssIFTi.js → kdl-BWECJveH.js} +0 -0
- /package/dist/web/assets/{kotlin-BRsNgp6x.js → kotlin-DpVwBCSt.js} +0 -0
- /package/dist/web/assets/{kusto-T8X9jUab.js → kusto-BgVqjUZA.js} +0 -0
- /package/dist/web/assets/{laserwave-DkJMVJ8Z.js → laserwave-CEVCi8em.js} +0 -0
- /package/dist/web/assets/{lean-C65KGVPq.js → lean-CGyfRhLJ.js} +0 -0
- /package/dist/web/assets/{less-exIPdDlf.js → less-B0_H10Ku.js} +0 -0
- /package/dist/web/assets/{lib-mC1NZfuw.js → lib-hrC3bruJ.js} +0 -0
- /package/dist/web/assets/{libesm-DSutUtl1.js → libesm-DVpFmwJu.js} +0 -0
- /package/dist/web/assets/{light-plus-DiHUXAZt.js → light-plus-DqzMqPID.js} +0 -0
- /package/dist/web/assets/{llvm-CJBnEzFM.js → llvm-9ucnfNiF.js} +0 -0
- /package/dist/web/assets/{loader-circle-gSPVxfvH.js → loader-circle-Dpqcv0eS.js} +0 -0
- /package/dist/web/assets/{lock-Did-87UV.js → lock-DldbT31x.js} +0 -0
- /package/dist/web/assets/{log-DxMyUMuP.js → log-m6A2dOSj.js} +0 -0
- /package/dist/web/assets/{logo-CC9NKzca.js → logo-ByPcsC9P.js} +0 -0
- /package/dist/web/assets/{luau-Bw10I6zf.js → luau-DFCo8nBn.js} +0 -0
- /package/dist/web/assets/{make-C_6M90m6.js → make-C90w3MeA.js} +0 -0
- /package/dist/web/assets/{markdown-bR9_R-LF.js → markdown-DT0uywZG.js} +0 -0
- /package/dist/web/assets/{material-theme-BUx2Qujn.js → material-theme-BVUO93tJ.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CRJrPgKm.js → material-theme-darker-xXgGDwnF.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-CUAYppPa.js → material-theme-lighter-BjStPzWB.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-DuINF1fg.js → material-theme-ocean-wFmSiKjy.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-W6_icGCj.js → material-theme-palenight-e-0CtRoe.js} +0 -0
- /package/dist/web/assets/{matlab-rfqn_b3z.js → matlab-BNaNQAUH.js} +0 -0
- /package/dist/web/assets/{mdx-Bh1B_YOJ.js → mdx-DfpVWAqO.js} +0 -0
- /package/dist/web/assets/{mermaid-Dxf6Ya2k.js → mermaid-Bpq7wFCP.js} +0 -0
- /package/dist/web/assets/{min-dark-CivZ2L0h.js → min-dark-CL2Y0WH6.js} +0 -0
- /package/dist/web/assets/{min-light-B1Ykqtma.js → min-light-DOgr_IdP.js} +0 -0
- /package/dist/web/assets/{mipsasm-DynHaM0d.js → mipsasm-C9ie2zlB.js} +0 -0
- /package/dist/web/assets/{mojo-Clw5PbGH.js → mojo-Md2A2sJy.js} +0 -0
- /package/dist/web/assets/{monitor-Dv8vn_6O.js → monitor-4vWfqzuN.js} +0 -0
- /package/dist/web/assets/{monokai-DFIooFZT.js → monokai-nbXIEySB.js} +0 -0
- /package/dist/web/assets/{moonbit-DnBSZWGM.js → moonbit-CJ-T-s4A.js} +0 -0
- /package/dist/web/assets/{move-CCcL2PfL.js → move-DwEehLIK.js} +0 -0
- /package/dist/web/assets/{narrat-BDarg5Lk.js → narrat-CuF4sIKr.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-DrZOeCNW.js → nextflow-groovy-1dYhfPfq.js} +0 -0
- /package/dist/web/assets/{night-owl-BnZZCK7v.js → night-owl-gZS5mfzY.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CEwBsVHS.js → night-owl-light-DDAster5.js} +0 -0
- /package/dist/web/assets/{nix-amCzOPMR.js → nix-BlbEYs2c.js} +0 -0
- /package/dist/web/assets/{nord-BIs2BC6q.js → nord-BRiPb8Vy.js} +0 -0
- /package/dist/web/assets/{nushell-Y02fo4yw.js → nushell-DgWicB1i.js} +0 -0
- /package/dist/web/assets/{objective-c-7xOZtWW1.js → objective-c-D8mPZ42Y.js} +0 -0
- /package/dist/web/assets/{objective-cpp-eVtjUvVB.js → objective-cpp-B0pNXzuj.js} +0 -0
- /package/dist/web/assets/{ocaml-Bi6ZwqG4.js → ocaml-lQd7aK5d.js} +0 -0
- /package/dist/web/assets/{odin-8xf1Y0S9.js → odin-CxtIOBjm.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-hVv-_y_b.js → one-dark-pro-T_UFnWfe.js} +0 -0
- /package/dist/web/assets/{one-light-CJJ3MtHt.js → one-light-LBVqwyV-.js} +0 -0
- /package/dist/web/assets/{openscad-B2HcywLk.js → openscad-C5dnS6J7.js} +0 -0
- /package/dist/web/assets/{pascal-C7fllV_7.js → pascal-CmWCWQML.js} +0 -0
- /package/dist/web/assets/{pkl-qvLC1IWP.js → pkl-BWWOtDQk.js} +0 -0
- /package/dist/web/assets/{plastic-BT1wHZv2.js → plastic-ekhupckF.js} +0 -0
- /package/dist/web/assets/{play-4MZi1Axp.js → play-CpCAoQRZ.js} +0 -0
- /package/dist/web/assets/{plsql-1MsmdmJD.js → plsql-BPsDHXx4.js} +0 -0
- /package/dist/web/assets/{plus-BhMZqFsN.js → plus-BR0qewJI.js} +0 -0
- /package/dist/web/assets/{po-BigskytD.js → po-BX2FiuW2.js} +0 -0
- /package/dist/web/assets/{poimandres-DuklniF7.js → poimandres-DzXOdZcN.js} +0 -0
- /package/dist/web/assets/{polar-e2pjN_sp.js → polar-DDRXgJzG.js} +0 -0
- /package/dist/web/assets/{portal-container-context-Cm5HipFr.js → portal-container-context-a399Hd9F.js} +0 -0
- /package/dist/web/assets/{postcss-DtAeYjWw.js → postcss-BHW6XBhx.js} +0 -0
- /package/dist/web/assets/{powerquery-Cr2PtehI.js → powerquery-B89c2NBb.js} +0 -0
- /package/dist/web/assets/{powershell-BxFTGLJ4.js → powershell-Dvxrd3Tu.js} +0 -0
- /package/dist/web/assets/{prisma-COWJiwdZ.js → prisma-lMK8EFLU.js} +0 -0
- /package/dist/web/assets/{prolog-DFvGKRx6.js → prolog-D0YrtMG4.js} +0 -0
- /package/dist/web/assets/{proto-CcK80lFt.js → proto-J0mM25aw.js} +0 -0
- /package/dist/web/assets/{puppet-BF60bZ4F.js → puppet-CUT0xkhh.js} +0 -0
- /package/dist/web/assets/{purescript-zL_69MzU.js → purescript-CoJWSGRp.js} +0 -0
- /package/dist/web/assets/{python-CIwKhfb_.js → python-BBrmuz6P.js} +0 -0
- /package/dist/web/assets/{qmldir-CXNe04Lo.js → qmldir-CUtzs4Q7.js} +0 -0
- /package/dist/web/assets/{qss-C8lAWe6G.js → qss-Dm1RZ8aL.js} +0 -0
- /package/dist/web/assets/{r-CL5ZVL3_.js → r-BGP7qZTe.js} +0 -0
- /package/dist/web/assets/{racket-BBfh0uh0.js → racket-K_6U1lqJ.js} +0 -0
- /package/dist/web/assets/{raku-o6E8R5Q3.js → raku-bNSiZeW_.js} +0 -0
- /package/dist/web/assets/{react-CBTnT4sY.js → react-X4jUntEf.js} +0 -0
- /package/dist/web/assets/{red-GomRF2Xp.js → red-uVeIEF0f.js} +0 -0
- /package/dist/web/assets/{refresh-cw-B06yaBrW.js → refresh-cw-B9iv7csb.js} +0 -0
- /package/dist/web/assets/{reg-_ffDIvx9.js → reg-DSm9GWjh.js} +0 -0
- /package/dist/web/assets/{regexp-Bl5sF65W.js → regexp-CljRm_j_.js} +0 -0
- /package/dist/web/assets/{rel-DgMdqDuj.js → rel-d3JcopZg.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CRt_7wpd.js → resolve-theme-DLKmm-lE.js} +0 -0
- /package/dist/web/assets/{riscv-8b3-Ye4X.js → riscv-CJmaELjT.js} +0 -0
- /package/dist/web/assets/{ron-DrmU1KHP.js → ron-D0g9B1rW.js} +0 -0
- /package/dist/web/assets/{rose-pine-C_-E2tF1.js → rose-pine-DOrD2BSH.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-DEfhNlFH.js → rose-pine-dawn-CzZQ-LCF.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-BdvX3gdn.js → rose-pine-moon-CdmsYp7C.js} +0 -0
- /package/dist/web/assets/{rosmsg-B6GhGpd5.js → rosmsg-CmDth6pO.js} +0 -0
- /package/dist/web/assets/{rotate-ccw-COXu_9oj.js → rotate-ccw-DnuPzJRE.js} +0 -0
- /package/dist/web/assets/{rust-DmC97M1-.js → rust-DGdP6jiR.js} +0 -0
- /package/dist/web/assets/{sass-B0tJOHLM.js → sass-Dkgt2FKZ.js} +0 -0
- /package/dist/web/assets/{save-CtIGOjmx.js → save-DxonD4y1.js} +0 -0
- /package/dist/web/assets/{scala-n8zaFOXv.js → scala-BQLTON6C.js} +0 -0
- /package/dist/web/assets/{scheme-BKD9ztBU.js → scheme-DKlxUGCi.js} +0 -0
- /package/dist/web/assets/{sdbl-D8gB5-rS.js → sdbl-C3iZJQeZ.js} +0 -0
- /package/dist/web/assets/{send-BlyNJAzy.js → send-B-t5Mz26.js} +0 -0
- /package/dist/web/assets/{shallow-DYo6goJ4.js → shallow-jwPzEunk.js} +0 -0
- /package/dist/web/assets/{shellscript-BfXlf_Ao.js → shellscript-BRu-ZQAx.js} +0 -0
- /package/dist/web/assets/{shield-check-B0iGUiOK.js → shield-check-CdTiK1j2.js} +0 -0
- /package/dist/web/assets/{shield-off-BNLWBhcq.js → shield-off-DuetkXKM.js} +0 -0
- /package/dist/web/assets/{slack-dark-CylEddse.js → slack-dark-qkm_u2zz.js} +0 -0
- /package/dist/web/assets/{slack-ochin-w8wg8vuC.js → slack-ochin-BP8_8e4m.js} +0 -0
- /package/dist/web/assets/{smalltalk-hiAXA64u.js → smalltalk-Bnz4gt07.js} +0 -0
- /package/dist/web/assets/{snazzy-light-BwlV3Xty.js → snazzy-light-fbPZg5io.js} +0 -0
- /package/dist/web/assets/{solarized-dark-vmNGdykz.js → solarized-dark-CM0vTRoi.js} +0 -0
- /package/dist/web/assets/{solarized-light-DGv_0JXq.js → solarized-light-C4_W9mvs.js} +0 -0
- /package/dist/web/assets/{solidity-Y7QSH0B1.js → solidity-Bz6v1PoD.js} +0 -0
- /package/dist/web/assets/{splunk-BtgpLjHi.js → splunk-DyNxu-0i.js} +0 -0
- /package/dist/web/assets/{sql-CRHYhOia.js → sql-DXRL1gw1.js} +0 -0
- /package/dist/web/assets/{ssh-config-DOqSs3au.js → ssh-config-2z1QHuOG.js} +0 -0
- /package/dist/web/assets/{stylus-crCAEaBT.js → stylus-D4I-VaeY.js} +0 -0
- /package/dist/web/assets/{swift-eE3rT7TC.js → swift-C_2G-tIc.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BWy14LBX.js → synthwave-84-osBjChRP.js} +0 -0
- /package/dist/web/assets/{system-verilog-DC0-DZM4.js → system-verilog-zQP2qPNU.js} +0 -0
- /package/dist/web/assets/{systemd-to5p6N8I.js → systemd-UGfjRSBM.js} +0 -0
- /package/dist/web/assets/{table-Bbc783ko.js → table-FLU8ei8b.js} +0 -0
- /package/dist/web/assets/{talonscript-DoTcU8wm.js → talonscript-C1XrFPwo.js} +0 -0
- /package/dist/web/assets/{tasl-cNZvUs95.js → tasl-B5raVn43.js} +0 -0
- /package/dist/web/assets/{tcl-mZE8sZH8.js → tcl-3-vbeMIc.js} +0 -0
- /package/dist/web/assets/{terraform-CqHPpIfu.js → terraform-a8VXZDm9.js} +0 -0
- /package/dist/web/assets/{text-wrap-CBT4Gxtg.js → text-wrap-B05VGRa-.js} +0 -0
- /package/dist/web/assets/{tokyo-night-Tp2rTIZd.js → tokyo-night-4SRhf-Hz.js} +0 -0
- /package/dist/web/assets/{toml-CU_vscgn.js → toml-B4N0i8wT.js} +0 -0
- /package/dist/web/assets/{trash-2-Bk4yYlck.js → trash-2-CTXlTflF.js} +0 -0
- /package/dist/web/assets/{triangle-alert-B8EYecpe.js → triangle-alert-BRq0VAfd.js} +0 -0
- /package/dist/web/assets/{tsv-ik7iFB9D.js → tsv-DA5k9Lvw.js} +0 -0
- /package/dist/web/assets/{tsx-BMvvMKiv.js → tsx-D9SZ8owC.js} +0 -0
- /package/dist/web/assets/{turtle-DBqKCq9o.js → turtle-BTHTvyY_.js} +0 -0
- /package/dist/web/assets/{typescript-DXJDmMZZ.js → typescript-DYzV9p5L.js} +0 -0
- /package/dist/web/assets/{typespec-CBseCMtZ.js → typespec-BC15eh7F.js} +0 -0
- /package/dist/web/assets/{typst-4oY25U9y.js → typst-BJUqzA6D.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-D1weanbj.js → use-is-mobile-DFWViA4S.js} +0 -0
- /package/dist/web/assets/{utils-BNRH507E.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-BXaZ_1oy.js → v-50Lps6oA.js} +0 -0
- /package/dist/web/assets/{vala-BImWJE7z.js → vala-8wiUvvrD.js} +0 -0
- /package/dist/web/assets/{vb-CLfj2_sy.js → vb-BlgPLt6B.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-_yyu5n32.js → vendor-xterm-BZOrwF_F.js} +0 -0
- /package/dist/web/assets/{verilog-BWa8Ed2b.js → verilog-CD9pVW5p.js} +0 -0
- /package/dist/web/assets/{vesper-BGj1cg6m.js → vesper-BrivW4PT.js} +0 -0
- /package/dist/web/assets/{vhdl-mByK7mlx.js → vhdl-Bunxfa7N.js} +0 -0
- /package/dist/web/assets/{viml-CugmlW5g.js → viml-DndUb2qM.js} +0 -0
- /package/dist/web/assets/{vitesse-black-vPdpy9TL.js → vitesse-black-BurZSkxW.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuQK3dUh.js → vitesse-dark-8UShspzC.js} +0 -0
- /package/dist/web/assets/{vitesse-light-B3nz1XL9.js → vitesse-light-DghCAZ90.js} +0 -0
- /package/dist/web/assets/{vyper-DH_GTIHG.js → vyper-b-Clr-8u.js} +0 -0
- /package/dist/web/assets/{wasm-DfVbVJlW.js → wasm-C5v2T06Y.js} +0 -0
- /package/dist/web/assets/{wasm-C-LF9GjX.js → wasm-CpcKStkw.js} +0 -0
- /package/dist/web/assets/{wenyan-CzQkHyHx.js → wenyan-Dz2srLZR.js} +0 -0
- /package/dist/web/assets/{wgsl-Du_M1DW9.js → wgsl-CeD1-dRb.js} +0 -0
- /package/dist/web/assets/{wikitext-BWN_w5S7.js → wikitext-CyeS-9FH.js} +0 -0
- /package/dist/web/assets/{wit-BR13v5HD.js → wit-Kr-q-qBQ.js} +0 -0
- /package/dist/web/assets/{wolfram-BoXxp_Xl.js → wolfram-BKm8bmln.js} +0 -0
- /package/dist/web/assets/{x-nhriOX9o.js → x-YDu4m2Iq.js} +0 -0
- /package/dist/web/assets/{yaml-DpR_EF-d.js → yaml-2XSxomjM.js} +0 -0
- /package/dist/web/assets/{zenscript-C-EdUtUd.js → zenscript-CKIS34vf.js} +0 -0
- /package/dist/web/assets/{zig-BD4eA5nU.js → zig-DdHGNllX.js} +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { Globe, Loader2, Plus, Square } from "lucide-react";
|
|
3
|
+
import { api } from "@/lib/api-client";
|
|
4
|
+
import { namedTunnelApi, type NamedTunnelStatus } from "@/lib/api-named-tunnel";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Called whenever the named-tunnel status is (re)loaded so the parent can adapt its copy. */
|
|
8
|
+
onStatus: (status: NamedTunnelStatus) => void;
|
|
9
|
+
/** Port the public URL is served on, so a temporary tunnel can target the same thing. */
|
|
10
|
+
publicPort: number | null;
|
|
11
|
+
/** Called when a temporary link appears or goes away. */
|
|
12
|
+
onTempUrl: (url: string | null) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TunnelEntry {
|
|
16
|
+
pid: number;
|
|
17
|
+
port: number | null;
|
|
18
|
+
url: string | null;
|
|
19
|
+
source: "app" | "ppm" | "external";
|
|
20
|
+
protected: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Custom-domain row of the share card.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately does NOT offer "switch back to a temporary link": that kills the
|
|
27
|
+
* connector serving the very page the button lives on, so the user never sees
|
|
28
|
+
* the replacement URL. A temporary link is offered as an ADDITION instead —
|
|
29
|
+
* a second quick tunnel onto the same port, for one-off sharing without handing
|
|
30
|
+
* out the permanent hostname. Turning the domain off entirely stays in the
|
|
31
|
+
* Tunnel Manager, where it can warn about exactly this.
|
|
32
|
+
*/
|
|
33
|
+
export function CloudShareNamedTunnelRow({ onStatus, publicPort, onTempUrl }: Props) {
|
|
34
|
+
const [status, setStatus] = useState<NamedTunnelStatus | null>(null);
|
|
35
|
+
const [temp, setTemp] = useState<TunnelEntry | null>(null);
|
|
36
|
+
const [busy, setBusy] = useState<"add" | "stop" | null>(null);
|
|
37
|
+
const [error, setError] = useState<string | null>(null);
|
|
38
|
+
|
|
39
|
+
const findTemp = useCallback((list: TunnelEntry[], namedUrl: string | null) => {
|
|
40
|
+
return list.find((t) =>
|
|
41
|
+
t.source === "ppm" && !t.protected && t.port === publicPort && t.url && t.url !== namedUrl,
|
|
42
|
+
) ?? null;
|
|
43
|
+
}, [publicPort]);
|
|
44
|
+
|
|
45
|
+
const load = useCallback(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const s = await namedTunnelApi.status();
|
|
48
|
+
setStatus(s);
|
|
49
|
+
onStatus(s);
|
|
50
|
+
const list = await api.get<TunnelEntry[]>("/api/tunnels").catch(() => [] as TunnelEntry[]);
|
|
51
|
+
const found = findTemp(list, s.hostname ? `https://${s.hostname}` : null);
|
|
52
|
+
setTemp(found);
|
|
53
|
+
onTempUrl(found?.url ?? null);
|
|
54
|
+
} catch { /* share card degrades to the plain view */ }
|
|
55
|
+
}, [onStatus, onTempUrl, findTemp]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => { void load(); }, [load]);
|
|
58
|
+
|
|
59
|
+
const addTemp = useCallback(async () => {
|
|
60
|
+
if (!publicPort) return;
|
|
61
|
+
setBusy("add"); setError(null);
|
|
62
|
+
try {
|
|
63
|
+
const res = await api.post<{ port: number; url: string }>("/api/tunnels", { port: publicPort });
|
|
64
|
+
setTemp({ pid: 0, port: publicPort, url: res.url, source: "ppm", protected: false });
|
|
65
|
+
onTempUrl(res.url);
|
|
66
|
+
void load(); // pick up the real pid so Stop works
|
|
67
|
+
} catch (e) {
|
|
68
|
+
setError(e instanceof Error ? e.message : "Could not create a temporary link");
|
|
69
|
+
} finally { setBusy(null); }
|
|
70
|
+
}, [publicPort, onTempUrl, load]);
|
|
71
|
+
|
|
72
|
+
const stopTemp = useCallback(async () => {
|
|
73
|
+
if (!temp?.pid) return;
|
|
74
|
+
setBusy("stop"); setError(null);
|
|
75
|
+
try {
|
|
76
|
+
await api.del(`/api/tunnels/${temp.pid}`);
|
|
77
|
+
setTemp(null);
|
|
78
|
+
onTempUrl(null);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
setError(e instanceof Error ? e.message : "Could not stop the temporary link");
|
|
81
|
+
} finally { setBusy(null); }
|
|
82
|
+
}, [temp?.pid, onTempUrl]);
|
|
83
|
+
|
|
84
|
+
if (!status || status.authEnabled === false || !status.hostname) return null;
|
|
85
|
+
if ((status.liveMode ?? status.mode) !== "named") return null;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="space-y-1.5">
|
|
89
|
+
<div className="flex items-center justify-between gap-2 text-xs">
|
|
90
|
+
<div className="flex items-center gap-1.5 min-w-0">
|
|
91
|
+
<Globe className="size-3 text-primary shrink-0" />
|
|
92
|
+
<span className="text-foreground truncate">Your domain · {status.hostname}</span>
|
|
93
|
+
</div>
|
|
94
|
+
<button
|
|
95
|
+
onClick={temp ? stopTemp : addTemp}
|
|
96
|
+
disabled={busy !== null || !publicPort}
|
|
97
|
+
className="shrink-0 min-h-11 px-2.5 flex items-center gap-1 text-xs rounded-md border border-border hover:bg-muted transition-colors disabled:opacity-50"
|
|
98
|
+
title={temp ? "Stop the temporary link" : "Also expose a temporary link for one-off sharing"}
|
|
99
|
+
>
|
|
100
|
+
{busy ? <Loader2 className="size-3.5 animate-spin" />
|
|
101
|
+
: temp ? <><Square className="size-3" /> Stop temp link</>
|
|
102
|
+
: <><Plus className="size-3" /> Temp link</>}
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
{status.tunnelWarning && (
|
|
106
|
+
<p className="text-[11px] text-amber-500 leading-relaxed">{status.tunnelWarning}</p>
|
|
107
|
+
)}
|
|
108
|
+
{error && <p className="text-[11px] text-destructive">{error}</p>}
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -4,6 +4,9 @@ import { QRCodeSVG } from "qrcode.react";
|
|
|
4
4
|
import { api } from "@/lib/api-client";
|
|
5
5
|
import { copyToClipboard } from "@/lib/clipboard";
|
|
6
6
|
import { CloudAliasRow } from "./cloud-alias-row";
|
|
7
|
+
import { CloudShareNamedTunnelRow } from "./cloud-share-named-tunnel-row";
|
|
8
|
+
import type { NamedTunnelStatus } from "@/lib/api-named-tunnel";
|
|
9
|
+
import { rememberLocalUrl } from "@/lib/last-known-local-url";
|
|
7
10
|
|
|
8
11
|
interface CloudStatus {
|
|
9
12
|
logged_in: boolean;
|
|
@@ -39,22 +42,40 @@ export function CloudSharePopover({ onClose, variant = "popover" }: Props) {
|
|
|
39
42
|
const [copied, setCopied] = useState<string | null>(null);
|
|
40
43
|
const [deviceCode, setDeviceCode] = useState<{ userCode: string; verifyUrl: string } | null>(null);
|
|
41
44
|
const [loginPolling, setLoginPolling] = useState(false);
|
|
45
|
+
// A live named tunnel means the public URL is already permanent — the
|
|
46
|
+
// "this link changes on restart" pitch would be false.
|
|
47
|
+
const [namedLive, setNamedLive] = useState(false);
|
|
48
|
+
const [tempUrl, setTempUrl] = useState<string | null>(null);
|
|
49
|
+
|
|
50
|
+
const reloadStatus = useCallback(async () => {
|
|
51
|
+
try {
|
|
52
|
+
const [cloudRes, tunnelRes] = await Promise.all([
|
|
53
|
+
api.get<CloudStatus>("/api/cloud/status"),
|
|
54
|
+
api.get<TunnelStatus>("/api/tunnel"),
|
|
55
|
+
]);
|
|
56
|
+
setCloud(cloudRes);
|
|
57
|
+
setTunnel(tunnelRes);
|
|
58
|
+
rememberLocalUrl(tunnelRes.localUrl);
|
|
59
|
+
} catch { /* ignore */ }
|
|
60
|
+
}, []);
|
|
42
61
|
|
|
43
62
|
// Load status on mount
|
|
44
63
|
useEffect(() => {
|
|
45
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]);
|
|
51
|
-
setCloud(cloudRes);
|
|
52
|
-
setTunnel(tunnelRes);
|
|
53
|
-
} catch { /* ignore */ }
|
|
54
|
-
setLoading(false);
|
|
55
|
-
})();
|
|
64
|
+
reloadStatus().finally(() => setLoading(false));
|
|
65
|
+
}, [reloadStatus]);
|
|
66
|
+
|
|
67
|
+
const handleNamedStatus = useCallback((s: NamedTunnelStatus) => {
|
|
68
|
+
setNamedLive((s.liveMode ?? s.mode) === "named");
|
|
56
69
|
}, []);
|
|
57
70
|
|
|
71
|
+
// The port the public URL is served on — a temporary tunnel must target the
|
|
72
|
+
// same one. `localUrl` is the only place the server reports it to this card.
|
|
73
|
+
const publicPort = (() => {
|
|
74
|
+
if (!tunnel?.localUrl) return null;
|
|
75
|
+
const port = Number(new URL(tunnel.localUrl).port);
|
|
76
|
+
return Number.isFinite(port) && port > 0 ? port : null;
|
|
77
|
+
})();
|
|
78
|
+
|
|
58
79
|
const handleCopy = useCallback((url: string) => {
|
|
59
80
|
void copyToClipboard(url);
|
|
60
81
|
setCopied(url);
|
|
@@ -254,8 +275,9 @@ export function CloudSharePopover({ onClose, variant = "popover" }: Props) {
|
|
|
254
275
|
<>
|
|
255
276
|
{shareUrl && (
|
|
256
277
|
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
|
257
|
-
|
|
258
|
-
|
|
278
|
+
{namedLive
|
|
279
|
+
? "This link is permanent — it lives on your own domain. Sign in to PPM Cloud to keep your devices in sync."
|
|
280
|
+
: "This link changes every time PPM restarts. Sign in to get a permanent one that always points here — private to your account."}
|
|
259
281
|
</p>
|
|
260
282
|
)}
|
|
261
283
|
<button
|
|
@@ -263,7 +285,7 @@ export function CloudSharePopover({ onClose, variant = "popover" }: Props) {
|
|
|
263
285
|
className="w-full flex items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium rounded-md border border-border hover:bg-muted transition-colors"
|
|
264
286
|
>
|
|
265
287
|
<Cloud className="size-3.5" />
|
|
266
|
-
{shareUrl ? "Get a permanent link" : "Sign in to PPM Cloud"}
|
|
288
|
+
{shareUrl && !namedLive ? "Get a permanent link" : "Sign in to PPM Cloud"}
|
|
267
289
|
</button>
|
|
268
290
|
</>
|
|
269
291
|
)}
|
|
@@ -309,6 +331,13 @@ export function CloudSharePopover({ onClose, variant = "popover" }: Props) {
|
|
|
309
331
|
{/* Separator */}
|
|
310
332
|
<div className="border-t border-border" />
|
|
311
333
|
|
|
334
|
+
{/* Custom domain: status + optional extra temporary link */}
|
|
335
|
+
<CloudShareNamedTunnelRow
|
|
336
|
+
onStatus={handleNamedStatus}
|
|
337
|
+
publicPort={publicPort}
|
|
338
|
+
onTempUrl={setTempUrl}
|
|
339
|
+
/>
|
|
340
|
+
|
|
312
341
|
{/* Local Network URL */}
|
|
313
342
|
{tunnel?.localUrl && (
|
|
314
343
|
<div className="space-y-1">
|
|
@@ -347,6 +376,19 @@ export function CloudSharePopover({ onClose, variant = "popover" }: Props) {
|
|
|
347
376
|
</div>
|
|
348
377
|
)}
|
|
349
378
|
|
|
379
|
+
{/* Extra one-off link that lives alongside the permanent one */}
|
|
380
|
+
{tempUrl && tempUrl !== shareUrl && (
|
|
381
|
+
<div className="space-y-1">
|
|
382
|
+
<span className="text-[10px] font-medium text-muted-foreground uppercase tracking-wide">
|
|
383
|
+
Temporary link
|
|
384
|
+
</span>
|
|
385
|
+
<UrlRow url={tempUrl} copied={copied} onCopy={handleCopy} />
|
|
386
|
+
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
|
387
|
+
Stops when you press Stop temp link, or when PPM restarts.
|
|
388
|
+
</p>
|
|
389
|
+
</div>
|
|
390
|
+
)}
|
|
391
|
+
|
|
350
392
|
{/* Cloud Dashboard Link */}
|
|
351
393
|
{cloud?.logged_in && cloud.linked && (
|
|
352
394
|
<a
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import { WifiOff, ServerOff, RefreshCw } from "lucide-react";
|
|
1
|
+
import { WifiOff, ServerOff, RefreshCw, Home } from "lucide-react";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { useConnectionStore } from "@/stores/connection-store";
|
|
4
|
+
import { getCachedNamedHostname } from "@/components/tunnels/named-tunnel/use-named-tunnel-setup";
|
|
5
|
+
import { getLastKnownLocalUrl } from "@/lib/last-known-local-url";
|
|
4
6
|
|
|
5
7
|
const CLOUD_URL = "https://cloud.ppm.sh";
|
|
6
8
|
|
|
9
|
+
/**
|
|
10
|
+
* True when this page is reached through *some* cloudflared tunnel (quick or
|
|
11
|
+
* named) rather than directly (localhost/LAN IP) — that distinction decides
|
|
12
|
+
* whether the "tunnel closed" or the generic "server unreachable" copy shows.
|
|
13
|
+
* The named hostname is cached in memory by the setup hook the last time
|
|
14
|
+
* `/status` answered while online; there is nothing else to ask once the API
|
|
15
|
+
* itself is the thing that is down.
|
|
16
|
+
*/
|
|
7
17
|
function isTunnelDomain(): boolean {
|
|
8
|
-
|
|
18
|
+
const host = window.location.hostname;
|
|
19
|
+
return host.endsWith(".trycloudflare.com") || host === getCachedNamedHostname();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Reached through the user's own permanent hostname, not a temporary one. */
|
|
23
|
+
function isNamedDomain(): boolean {
|
|
24
|
+
const cached = getCachedNamedHostname();
|
|
25
|
+
return !!cached && window.location.hostname === cached;
|
|
9
26
|
}
|
|
10
27
|
|
|
11
28
|
export function ConnectionLostOverlay() {
|
|
@@ -15,6 +32,8 @@ export function ConnectionLostOverlay() {
|
|
|
15
32
|
if (!showOverlay) return null;
|
|
16
33
|
|
|
17
34
|
const isTunnel = isTunnelDomain();
|
|
35
|
+
const isNamed = isNamedDomain();
|
|
36
|
+
const localUrl = getLastKnownLocalUrl();
|
|
18
37
|
|
|
19
38
|
async function handleRetry() {
|
|
20
39
|
setRetrying(true);
|
|
@@ -48,22 +67,42 @@ export function ConnectionLostOverlay() {
|
|
|
48
67
|
|
|
49
68
|
<div className="space-y-2">
|
|
50
69
|
<h2 className="text-xl font-semibold text-foreground">
|
|
51
|
-
{isTunnel ? "Connection Lost" : "Server Unreachable"}
|
|
70
|
+
{isNamed ? "Domain Not Responding" : isTunnel ? "Connection Lost" : "Server Unreachable"}
|
|
52
71
|
</h2>
|
|
53
72
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
|
54
|
-
{
|
|
73
|
+
{isNamed
|
|
74
|
+
// A named hostname never rotates, so "the URL changed" — true for a
|
|
75
|
+
// quick tunnel — would send the user hunting for a new link that
|
|
76
|
+
// does not exist. The address is fine; something behind it is not.
|
|
77
|
+
? "This address stays the same, so keep it. The machine may be asleep, offline, or restarting — it will answer again on this same URL."
|
|
78
|
+
: isTunnel
|
|
55
79
|
? "The tunnel appears to have closed. The server may have restarted with a new URL."
|
|
56
80
|
: "Cannot connect to the PPM server. It may have stopped or is restarting."}
|
|
57
81
|
</p>
|
|
58
82
|
</div>
|
|
59
83
|
|
|
60
84
|
<div className="flex flex-col gap-3">
|
|
85
|
+
{/* Same machine, same network: the LAN address bypasses Cloudflare
|
|
86
|
+
entirely, so it still works when only the hostname is broken. */}
|
|
87
|
+
{isTunnel && localUrl && (
|
|
88
|
+
<a
|
|
89
|
+
href={localUrl}
|
|
90
|
+
className="inline-flex items-center justify-center gap-2 rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
91
|
+
>
|
|
92
|
+
<Home className="h-4 w-4" />
|
|
93
|
+
Open on local network
|
|
94
|
+
</a>
|
|
95
|
+
)}
|
|
61
96
|
{isTunnel && (
|
|
62
97
|
<a
|
|
63
98
|
href={CLOUD_URL}
|
|
64
99
|
target="_blank"
|
|
65
100
|
rel="noopener noreferrer"
|
|
66
|
-
className=
|
|
101
|
+
className={`inline-flex items-center justify-center rounded-md px-4 py-2.5 text-sm font-medium transition-colors ${
|
|
102
|
+
localUrl
|
|
103
|
+
? "border border-border text-foreground hover:bg-accent"
|
|
104
|
+
: "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
105
|
+
}`}
|
|
67
106
|
>
|
|
68
107
|
Open PPM Cloud
|
|
69
108
|
</a>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client re-export of the server hostname rules, so the prefix field
|
|
3
|
+
* validates instantly without a round trip — and without a second regex that
|
|
4
|
+
* could drift from what the server actually enforces.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
validateHostname,
|
|
8
|
+
buildHostname,
|
|
9
|
+
proposeHostname,
|
|
10
|
+
} from "../../../../services/named-tunnel/hostname-rules";
|
|
11
|
+
export type { HostnameCheck } from "../../../../services/named-tunnel/hostname-rules";
|
|
12
|
+
|
|
13
|
+
/** Default prefix proposed to the user — mirrors the server's own `proposeHostname`. */
|
|
14
|
+
export const DEFAULT_HOSTNAME_PREFIX = "ppm";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All user-facing strings for the named-tunnel setup flow, kept in one file so
|
|
3
|
+
* a future i18n pass has a single place to swap. Vietnamese is the primary
|
|
4
|
+
* language here — no English fallback is implemented yet.
|
|
5
|
+
*/
|
|
6
|
+
export const namedTunnelCopy = {
|
|
7
|
+
askDomain: {
|
|
8
|
+
title: "Bạn đã có domain trên Cloudflare chưa?",
|
|
9
|
+
body: "Có domain riêng thì PPM có thể gắn một địa chỉ cố định (không đổi khi khởi động lại). Không có domain thì vẫn dùng bình thường với link tạm thời.",
|
|
10
|
+
yes: "Có",
|
|
11
|
+
no: "Chưa",
|
|
12
|
+
},
|
|
13
|
+
noDomain: {
|
|
14
|
+
title: "Không sao cả",
|
|
15
|
+
body: "PPM sẽ tiếp tục dùng link tạm thời (quick tunnel). Bạn có thể bật lại tính năng này bất cứ lúc nào trong mục Tunnel Manager.",
|
|
16
|
+
close: "Đóng",
|
|
17
|
+
},
|
|
18
|
+
login: {
|
|
19
|
+
title: "Đăng nhập Cloudflare",
|
|
20
|
+
hint: "Mở link bên dưới để đăng nhập — có thể làm trên điện thoại nếu tiện hơn.",
|
|
21
|
+
finishOnPhone: "Có thể hoàn tất bước này trên điện thoại nếu dễ hơn.",
|
|
22
|
+
copy: "Sao chép",
|
|
23
|
+
copied: "Đã sao chép",
|
|
24
|
+
open: "Mở link",
|
|
25
|
+
waiting: "Đang chờ đăng nhập…",
|
|
26
|
+
slowTitle: "Vẫn đang đăng nhập?",
|
|
27
|
+
slowBody: "Cloudflare đôi khi mất vài phút. Tiến trình vẫn đang chạy — bạn có thể tiếp tục chờ hoặc huỷ.",
|
|
28
|
+
keepWaiting: "Tiếp tục chờ",
|
|
29
|
+
cancel: "Huỷ",
|
|
30
|
+
},
|
|
31
|
+
timeout: {
|
|
32
|
+
title: "Link đăng nhập đã hết hạn",
|
|
33
|
+
body: "Link cũ hết hạn sau 5 phút không dùng. Bấm Thử lại để lấy link mới.",
|
|
34
|
+
retry: "Thử lại",
|
|
35
|
+
},
|
|
36
|
+
cancelled: {
|
|
37
|
+
title: "Đã huỷ đăng nhập",
|
|
38
|
+
body: "Bạn đã huỷ quá trình đăng nhập Cloudflare.",
|
|
39
|
+
retry: "Thử lại",
|
|
40
|
+
},
|
|
41
|
+
confirmZone: {
|
|
42
|
+
title: "Xác nhận vùng miền",
|
|
43
|
+
body: (zone: string) => `Máy này sẽ được gán một địa chỉ cố định dưới ${zone}. Tiếp tục?`,
|
|
44
|
+
confirm: "Tiếp tục",
|
|
45
|
+
startOver: "Bắt đầu lại",
|
|
46
|
+
},
|
|
47
|
+
needsRelogin: {
|
|
48
|
+
title: "Cần đăng nhập lại Cloudflare",
|
|
49
|
+
certInvalid: "Phiên đăng nhập Cloudflare đã hết hiệu lực.",
|
|
50
|
+
certMismatch: "Chứng chỉ này thuộc một tài khoản Cloudflare khác — cần đăng nhập lại.",
|
|
51
|
+
action: "Đăng nhập lại",
|
|
52
|
+
},
|
|
53
|
+
hostname: {
|
|
54
|
+
title: "Chọn địa chỉ",
|
|
55
|
+
prefixLabel: "Tiền tố",
|
|
56
|
+
suffixHint: "Phần đuôi được cố định theo tài khoản bạn vừa đăng nhập.",
|
|
57
|
+
submit: "Xác nhận",
|
|
58
|
+
},
|
|
59
|
+
applying: {
|
|
60
|
+
title: "Đang thiết lập…",
|
|
61
|
+
},
|
|
62
|
+
done: {
|
|
63
|
+
title: "Đã thiết lập xong",
|
|
64
|
+
body: (hostname: string) => `Địa chỉ cố định: https://${hostname}`,
|
|
65
|
+
close: "Đóng",
|
|
66
|
+
},
|
|
67
|
+
pending: {
|
|
68
|
+
title: "Đã lưu — đang chờ áp dụng",
|
|
69
|
+
restartHint: "Chạy lệnh `ppm restart` để áp dụng ngay.",
|
|
70
|
+
close: "Đóng",
|
|
71
|
+
},
|
|
72
|
+
error: {
|
|
73
|
+
title: "Có lỗi xảy ra",
|
|
74
|
+
retry: "Thử lại",
|
|
75
|
+
close: "Đóng",
|
|
76
|
+
},
|
|
77
|
+
section: {
|
|
78
|
+
title: "Named Tunnel",
|
|
79
|
+
modeQuick: "quick",
|
|
80
|
+
modeNamed: "named",
|
|
81
|
+
hostnameLabel: "Địa chỉ",
|
|
82
|
+
tokenLabel: "Token",
|
|
83
|
+
setup: "Thiết lập named tunnel",
|
|
84
|
+
retry: "Thử lại",
|
|
85
|
+
relogin: "Đăng nhập lại",
|
|
86
|
+
disable: "Chuyển về quick tunnel",
|
|
87
|
+
disableConfirm: "Bấm lần nữa để xác nhận",
|
|
88
|
+
disableSelfCut:
|
|
89
|
+
"Bạn đang mở PPM qua chính tên miền này — tắt nó sẽ ngắt trang ngay và bạn sẽ không kịp thấy link tạm. Mở PPM qua địa chỉ mạng nội bộ rồi tắt.",
|
|
90
|
+
certInvalid: "Cần đăng nhập lại Cloudflare",
|
|
91
|
+
certMismatch: "Chứng chỉ thuộc tài khoản Cloudflare khác — cần đăng nhập lại",
|
|
92
|
+
authDisabled: "Bật xác thực PPM để dùng tên miền riêng",
|
|
93
|
+
/** Small note next to the live-mode badge when the configured mode hasn't landed yet. */
|
|
94
|
+
configuredAs: (mode: "quick" | "named") => `đã cấu hình: ${mode}`,
|
|
95
|
+
},
|
|
96
|
+
} as const;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefix input + locked zone suffix for the hostname step. The suffix is
|
|
3
|
+
* plain text (not editable) — the zone is pinned by the Cloudflare account
|
|
4
|
+
* the user just logged into, not something a text field should let them
|
|
5
|
+
* casually rewrite.
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect, useRef } from "react";
|
|
8
|
+
import { namedTunnelCopy } from "./named-tunnel-copy";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
zone: string;
|
|
12
|
+
prefix: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
onChange: (prefix: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function NamedTunnelHostnameField({ zone, prefix, error, onChange }: Props) {
|
|
18
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
19
|
+
|
|
20
|
+
// Auto-focus on mount, as the spec asks — this component only mounts once
|
|
21
|
+
// per hostname step, so an effect with no deps is exactly "on mount".
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
inputRef.current?.focus();
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="space-y-1.5">
|
|
28
|
+
<label htmlFor="named-tunnel-prefix" className="block text-sm font-medium text-foreground">
|
|
29
|
+
{namedTunnelCopy.hostname.prefixLabel}
|
|
30
|
+
</label>
|
|
31
|
+
<div className="flex items-stretch rounded-lg border border-border bg-background focus-within:border-primary/50 transition-colors overflow-hidden">
|
|
32
|
+
<input
|
|
33
|
+
ref={inputRef}
|
|
34
|
+
id="named-tunnel-prefix"
|
|
35
|
+
type="text"
|
|
36
|
+
value={prefix}
|
|
37
|
+
onChange={(e) => onChange(e.target.value)}
|
|
38
|
+
spellCheck={false}
|
|
39
|
+
autoCapitalize="off"
|
|
40
|
+
autoCorrect="off"
|
|
41
|
+
className="min-w-0 flex-1 px-3 py-3 bg-transparent text-sm text-foreground outline-none"
|
|
42
|
+
/>
|
|
43
|
+
<span className="flex items-center px-3 py-3 text-sm text-text-subtle bg-surface-elevated border-l border-border select-none whitespace-nowrap">
|
|
44
|
+
.{zone}
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
<p className="text-xs text-text-subtle">{namedTunnelCopy.hostname.suffixHint}</p>
|
|
48
|
+
{error && <p className="text-xs text-destructive">{error}</p>}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `login-wait` step: shows the Cloudflare login URL (openable from any
|
|
3
|
+
* device, since the WS event reaches every connected client) and the 60s
|
|
4
|
+
* "still waiting" banner. No countdown-to-zero — the login process survives
|
|
5
|
+
* past 60s, so a countdown would wrongly imply an imminent kill.
|
|
6
|
+
*/
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Copy, Check, ExternalLink, Loader2, AlertTriangle } from "lucide-react";
|
|
9
|
+
import { copyToClipboard } from "@/lib/clipboard";
|
|
10
|
+
import { namedTunnelCopy } from "./named-tunnel-copy";
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
url: string | null;
|
|
14
|
+
slow: boolean;
|
|
15
|
+
onCancel: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function NamedTunnelLoginStep({ url, slow, onCancel }: Props) {
|
|
19
|
+
const [copied, setCopied] = useState(false);
|
|
20
|
+
const [bannerAcked, setBannerAcked] = useState(false);
|
|
21
|
+
const c = namedTunnelCopy.login;
|
|
22
|
+
|
|
23
|
+
const showBanner = slow && !bannerAcked;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="space-y-4">
|
|
27
|
+
<div>
|
|
28
|
+
<h2 className="text-base font-semibold text-foreground">{c.title}</h2>
|
|
29
|
+
<p className="mt-1 text-sm text-text-secondary leading-relaxed">{c.hint}</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
{url ? (
|
|
33
|
+
<div className="flex items-stretch gap-2">
|
|
34
|
+
<div className="min-w-0 flex-1 flex items-center px-3 py-3 rounded-lg border border-border bg-surface font-mono text-xs text-foreground truncate">
|
|
35
|
+
{url}
|
|
36
|
+
</div>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
onClick={async () => {
|
|
40
|
+
const ok = await copyToClipboard(url);
|
|
41
|
+
if (ok) { setCopied(true); setTimeout(() => setCopied(false), 2000); }
|
|
42
|
+
}}
|
|
43
|
+
aria-label={c.copy}
|
|
44
|
+
className="shrink-0 size-11 flex items-center justify-center rounded-lg border border-border hover:bg-surface-elevated transition-colors"
|
|
45
|
+
>
|
|
46
|
+
{copied ? <Check className="size-4 text-success" /> : <Copy className="size-4" />}
|
|
47
|
+
</button>
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
onClick={() => window.open(url, "_blank", "noopener")}
|
|
51
|
+
aria-label={c.open}
|
|
52
|
+
className="shrink-0 size-11 flex items-center justify-center rounded-lg border border-border hover:bg-surface-elevated transition-colors"
|
|
53
|
+
>
|
|
54
|
+
<ExternalLink className="size-4" />
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
) : (
|
|
58
|
+
<div className="flex items-center gap-2 text-sm text-text-secondary">
|
|
59
|
+
<Loader2 className="size-4 animate-spin" />
|
|
60
|
+
{c.waiting}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
<p className="text-xs text-text-subtle">{c.finishOnPhone}</p>
|
|
65
|
+
|
|
66
|
+
{showBanner && (
|
|
67
|
+
<div className="rounded-lg border border-warning/30 bg-warning/10 p-3 space-y-2">
|
|
68
|
+
<div className="flex items-start gap-2">
|
|
69
|
+
<AlertTriangle className="size-4 text-warning shrink-0 mt-0.5" />
|
|
70
|
+
<div>
|
|
71
|
+
<p className="text-sm font-medium text-foreground">{c.slowTitle}</p>
|
|
72
|
+
<p className="text-xs text-text-secondary leading-relaxed mt-0.5">{c.slowBody}</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="flex gap-2 pt-1">
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
onClick={() => setBannerAcked(true)}
|
|
79
|
+
className="flex-1 min-h-11 py-2.5 rounded-md bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 transition-colors"
|
|
80
|
+
>
|
|
81
|
+
{c.keepWaiting}
|
|
82
|
+
</button>
|
|
83
|
+
<button
|
|
84
|
+
type="button"
|
|
85
|
+
onClick={onCancel}
|
|
86
|
+
className="flex-1 min-h-11 py-2.5 rounded-md border border-border text-sm text-text-secondary hover:bg-surface-elevated transition-colors"
|
|
87
|
+
>
|
|
88
|
+
{c.cancel}
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{!showBanner && (
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
onClick={onCancel}
|
|
98
|
+
className="w-full min-h-11 py-3 rounded-lg border border-border text-sm text-text-secondary hover:bg-surface-elevated transition-colors"
|
|
99
|
+
>
|
|
100
|
+
{c.cancel}
|
|
101
|
+
</button>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|