@jmfederico/pi-web 1.202605.11 → 1.202605.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +23 -23
  2. package/dist/cli.js +10 -10
  3. package/dist/client/apple-touch-icon.png +0 -0
  4. package/dist/client/assets/{CodeViewer-0NuLmnBL.js → CodeViewer-Bg2WQKWF.js} +1 -1
  5. package/dist/client/assets/{TerminalPanel-BG6_9svt.js → TerminalPanel-CeT2Rp-S.js} +1 -1
  6. package/dist/client/assets/index-BH4t_Up6.js +1222 -0
  7. package/dist/client/favicon.svg +11 -0
  8. package/dist/client/index.html +6 -2
  9. package/dist/client/manifest.webmanifest +24 -0
  10. package/dist/client/pwa-icon-192.png +0 -0
  11. package/dist/client/pwa-icon-512.png +0 -0
  12. package/dist/config.js +4 -4
  13. package/dist/pi-web-plugins/pi-web/pi-web-plugin.js +13 -9
  14. package/dist/server/piWebPluginService.js +9 -9
  15. package/dist/server/piWebStatus.js +3 -3
  16. package/dist/server/sessions/piSessionService.js +147 -12
  17. package/dist/server/sessions/piSessionService.js.map +1 -1
  18. package/dist/server/sessions/sessionArchiveTree.js +35 -0
  19. package/dist/server/sessions/sessionArchiveTree.js.map +1 -0
  20. package/dist/server/sessions/sessionCommandService.js +88 -2
  21. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  22. package/dist/server/sessions/sessionRoutes.js +8 -0
  23. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  24. package/dist/server/workspaceExplorerRoutes.js +18 -0
  25. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  26. package/dist/server/workspaces/fileContentService.js +23 -3
  27. package/dist/server/workspaces/fileContentService.js.map +1 -1
  28. package/dist/server/workspaces/imagePreviewService.js +40 -0
  29. package/dist/server/workspaces/imagePreviewService.js.map +1 -0
  30. package/dist/shared/workspaceFiles.js +3 -0
  31. package/dist/shared/workspaceFiles.js.map +1 -0
  32. package/docs/assets/favicon.svg +11 -0
  33. package/docs/plugins.md +41 -41
  34. package/extensions/pi-web.ts +3 -3
  35. package/package.json +1 -1
  36. package/plugin-api.d.ts +2 -0
  37. package/dist/client/assets/index-DmVN7DQr.js +0 -1143
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Pi Web
1
+ # PI WEB
2
2
 
3
3
  [![CI](https://github.com/jmfederico/pi-web/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/jmfederico/pi-web/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/@jmfederico/pi-web)](https://www.npmjs.com/package/@jmfederico/pi-web)
@@ -8,15 +8,15 @@
8
8
 
9
9
  Website: <https://pi-web.dev/>
10
10
 
11
- ![Pi Web](docs/assets/pi-web-banner.png)
11
+ ![PI WEB](docs/assets/pi-web-banner.png)
12
12
 
13
13
  **Run AI coding agents on your own machine or server, keep them alive in real workspaces, and control everything from a browser.**
14
14
 
15
- Pi Web is a web control plane for [Pi Coding Agent](https://github.com/earendil-works/pi/tree/main/packages/coding-agent). Add your repositories once, open project workspaces and git worktrees, start agent sessions inside them, and come back later without losing the work. Your browser becomes the cockpit; your server becomes the persistent development environment. Start on your laptop, check in from your phone, and continue from an iPad or another machine whenever that is the device you have at hand.
15
+ PI WEB is a web control plane for [Pi Coding Agent](https://github.com/earendil-works/pi/tree/main/packages/coding-agent). Add your repositories once, open project workspaces and git worktrees, start agent sessions inside them, and come back later without losing the work. Your browser becomes the cockpit; your server becomes the persistent development environment. Start on your laptop, check in from your phone, and continue from an iPad or another machine whenever that is the device you have at hand.
16
16
 
17
- ![Pi Web demo](docs/assets/pi-web-demo.gif)
17
+ ![PI WEB demo](docs/assets/pi-web-demo.gif)
18
18
 
19
- With Pi Web you can:
19
+ With PI WEB you can:
20
20
 
21
21
  - launch and supervise multiple coding-agent sessions in parallel;
22
22
  - keep sessions running when your browser disconnects or the UI restarts;
@@ -26,15 +26,15 @@ With Pi Web you can:
26
26
  - move fluidly between laptop, phone, tablet, and desktop without moving the development environment;
27
27
  - turn any server, desktop, or remote dev box into an agent-first development hub.
28
28
 
29
- ## Why use Pi Web?
29
+ ## Why use PI WEB?
30
30
 
31
31
  Agentic development works best when agents are not trapped inside a single local terminal. They need stable environments, access to real repositories, and room to work across branches and tasks. Humans need the opposite: a clear place to supervise, redirect, review, and decide.
32
32
 
33
- Pi Web connects those two worlds. The work stays in the server-side environment while you move between devices: laptop for deep focus, phone for a quick check-in, tablet for review, desktop when you are back at a desk. It is not trying to recreate the old desktop IDE in a browser; it is a control surface for persistent, parallel, human-in-the-loop agent work.
33
+ PI WEB connects those two worlds. The work stays in the server-side environment while you move between devices: laptop for deep focus, phone for a quick check-in, tablet for review, desktop when you are back at a desk. It is not trying to recreate the old desktop IDE in a browser; it is a control surface for persistent, parallel, human-in-the-loop agent work.
34
34
 
35
35
  ## Core model
36
36
 
37
- Pi Web organizes work into three levels:
37
+ PI WEB organizes work into three levels:
38
38
 
39
39
  ```text
40
40
  Project a folder on the server
@@ -65,7 +65,7 @@ This maps naturally to real development work:
65
65
 
66
66
  ## Architecture
67
67
 
68
- Pi Web uses a split-process architecture so agent runtimes are not owned by the browser-facing dev server.
68
+ PI WEB uses a split-process architecture so agent runtimes are not owned by the browser-facing dev server.
69
69
 
70
70
  ```text
71
71
  Browser UI
@@ -90,7 +90,7 @@ The web process serves the API and browser UI. In development it can autoreload
90
90
 
91
91
  ## State model
92
92
 
93
- Pi Web keeps its own state intentionally small:
93
+ PI WEB keeps its own state intentionally small:
94
94
 
95
95
  - Projects: `~/.pi-web/projects.json`
96
96
  - Workspaces: discovered from git worktrees, not stored
@@ -99,18 +99,18 @@ Pi Web keeps its own state intentionally small:
99
99
 
100
100
  ## Plugins
101
101
 
102
- Pi Web production installs can load trusted local UI plugins without rebuilding Pi Web. Plugins are browser-side ES modules that can add action-palette actions, workspace panels, and workspace-label metadata. They do not run in the session daemon and are not sandboxed.
102
+ PI WEB production installs can load trusted local UI plugins without rebuilding PI WEB. Plugins are browser-side ES modules that can add action-palette actions, workspace panels, and workspace-label metadata. They do not run in the session daemon and are not sandboxed.
103
103
 
104
104
  The supported package shape is intentionally singular: `piWeb.plugins` entries with explicit `id` and `module`, plus a browser module that exports `{ apiVersion: 1, name, activate }`. The bundled `pi-web-plugins/info` TypeScript source is the canonical minimal real example, and `pi-web-plugins/pi-web` demonstrates a dynamic status panel.
105
105
 
106
106
  A useful prompt for AI agents:
107
107
 
108
108
  ```text
109
- Build a Pi Web plugin for this project. Goal: <describe the UI behavior>.
109
+ Build a PI WEB plugin for this project. Goal: <describe the UI behavior>.
110
110
  Before coding, read https://pi-web.dev/plugins.html and https://pi-web.dev/plugins.md.
111
- Create it under ~/.pi-web/plugins/<plugin-id> using the documented Pi Web v1 plugin API.
111
+ Create it under ~/.pi-web/plugins/<plugin-id> using the documented PI WEB v1 plugin API.
112
112
  Validate with /pi-web-plugins/manifest.json and explain reload/debug steps.
113
- Do not modify Pi Web itself.
113
+ Do not modify PI WEB itself.
114
114
  ```
115
115
 
116
116
  Reload the browser tab after adding or editing a plugin. If `PI_WEB_DATA_DIR` is set, use `$PI_WEB_DATA_DIR/plugins` instead of `~/.pi-web/plugins`. Check discovery with:
@@ -134,7 +134,7 @@ sudo loginctl enable-linger "$USER"
134
134
  pi-web install
135
135
  ```
136
136
 
137
- `loginctl enable-linger` is optional for local desktop use, but recommended on servers. It lets the user systemd manager start at boot and continue running after you log out, so Pi Web remains available without an active SSH/login session.
137
+ `loginctl enable-linger` is optional for local desktop use, but recommended on servers. It lets the user systemd manager start at boot and continue running after you log out, so PI WEB remains available without an active SSH/login session.
138
138
 
139
139
  This writes and starts:
140
140
 
@@ -167,7 +167,7 @@ One-line install is also available for users who prefer it:
167
167
  curl -fsSL https://raw.githubusercontent.com/jmfederico/pi-web/main/install.sh | sh
168
168
  ```
169
169
 
170
- Pi Web is also published as a Pi package. Installing it through Pi exposes a `/pi-web` command inside Pi:
170
+ PI WEB is also published as a Pi package. Installing it through Pi exposes a `/pi-web` command inside Pi:
171
171
 
172
172
  ```bash
173
173
  pi install npm:@jmfederico/pi-web
@@ -183,7 +183,7 @@ Then in Pi:
183
183
  /pi-web doctor
184
184
  ```
185
185
 
186
- The Pi command is a convenience wrapper around the same service installer. When installed this way, the service installer can use Pi Web's package-local server entrypoints, so `pi-web-server` and `pi-web-sessiond` do not need to be on your shell `PATH`. `/pi-web logs` shows the last 100 journal lines; use `pi-web logs` in a shell when you want to follow logs continuously.
186
+ The Pi command is a convenience wrapper around the same service installer. When installed this way, the service installer can use PI WEB's package-local server entrypoints, so `pi-web-server` and `pi-web-sessiond` do not need to be on your shell `PATH`. `/pi-web logs` shows the last 100 journal lines; use `pi-web logs` in a shell when you want to follow logs continuously.
187
187
 
188
188
  Advanced users may run the binaries however they prefer:
189
189
 
@@ -231,9 +231,9 @@ npm publish --access public
231
231
 
232
232
  `prepack` builds `dist/` and bundled plugin JavaScript before npm creates the tarball, and `prepublishOnly` runs verification before publishing. Releases can also be published by the GitHub Actions npm workflow when a GitHub release is published.
233
233
 
234
- Pi Web uses a single-line CalVer-inspired npm version: `MAJOR.YYYYMM.SEQUENCE`, for example `1.202605.1`. The major number signals breaking-change eras; the middle number is the release month; the final number increments for additional releases in that month. Older major eras may be deprecated rather than maintained in parallel.
234
+ PI WEB uses a single-line CalVer-inspired npm version: `MAJOR.YYYYMM.SEQUENCE`, for example `1.202605.1`. The major number signals breaking-change eras; the middle number is the release month; the final number increments for additional releases in that month. Older major eras may be deprecated rather than maintained in parallel.
235
235
 
236
- Pi Web declares `@earendil-works/pi-coding-agent` as a peer dependency (`>=0.74.0 <1`) and a development dependency for local builds. This keeps published installs flexible: npm 7+ installs the peer automatically, and users can upgrade the Pi package within the compatible range without Pi Web pinning a separate copy.
236
+ PI WEB declares `@earendil-works/pi-coding-agent` as a peer dependency (`>=0.74.0 <1`) and a development dependency for local builds. This keeps published installs flexible: npm 7+ installs the peer automatically, and users can upgrade the Pi package within the compatible range without PI WEB pinning a separate copy.
237
237
 
238
238
 
239
239
  The web server defaults to `127.0.0.1:8504`. Set `PI_WEB_HOST=0.0.0.0` only when you intentionally want to bind directly on all interfaces.
@@ -248,7 +248,7 @@ Environment variables:
248
248
 
249
249
  - `PI_WEB_PORT` / `PORT` — web server port. Defaults to `8504`.
250
250
  - `PI_WEB_HOST` — web server bind host. Defaults to `127.0.0.1`.
251
- - `PI_WEB_DATA_DIR` — Pi Web data directory. Defaults to `~/.pi-web`.
251
+ - `PI_WEB_DATA_DIR` — PI WEB data directory. Defaults to `~/.pi-web`.
252
252
  - `PI_WEB_SESSIOND_SOCKET` — Unix socket path used by both the daemon and web process when `PI_WEB_SESSIOND_URL` is not set. Defaults to `$PI_WEB_DATA_DIR/sessiond.sock`.
253
253
  - `PI_WEB_SESSIOND_PORT` — optional TCP port for the daemon. If unset, the daemon listens on the Unix socket instead.
254
254
  - `PI_WEB_SESSIOND_HOST` — daemon TCP bind host when `PI_WEB_SESSIOND_PORT` is set. Defaults to `127.0.0.1`.
@@ -267,7 +267,7 @@ Example units:
267
267
  ```ini
268
268
  # ~/.config/systemd/user/pi-web-sessiond.service
269
269
  [Unit]
270
- Description=Pi Web session daemon
270
+ Description=PI WEB session daemon
271
271
 
272
272
  [Service]
273
273
  Type=simple
@@ -282,7 +282,7 @@ WantedBy=default.target
282
282
  ```ini
283
283
  # ~/.config/systemd/user/pi-web-ui-dev.service
284
284
  [Unit]
285
- Description=Pi Web UI dev server
285
+ Description=PI WEB UI dev server
286
286
  After=pi-web-sessiond.service
287
287
  Wants=pi-web-sessiond.service
288
288
 
@@ -333,7 +333,7 @@ systemctl --user restart pi-web-sessiond.service
333
333
 
334
334
  ## Vision
335
335
 
336
- Pi Web is the beginning of an agent-first development environment:
336
+ PI WEB is the beginning of an agent-first development environment:
337
337
 
338
338
  - agents run persistently on servers;
339
339
  - humans connect through the browser;
package/dist/cli.js CHANGED
@@ -173,13 +173,13 @@ function describeServiceShell() {
173
173
  if (shell.fallback) {
174
174
  return shell.detected === undefined
175
175
  ? "could not detect a supported login shell; using bash"
176
- : `detected ${shell.detected}; using bash because Pi Web currently supports bash, zsh, and fish`;
176
+ : `detected ${shell.detected}; using bash because PI WEB currently supports bash, zsh, and fish`;
177
177
  }
178
178
  return shell.detected === undefined ? shell.name : `${shell.name} (${shell.detected})`;
179
179
  }
180
180
  function sessiondUnit(executables) {
181
181
  return `[Unit]
182
- Description=Pi Web session daemon
182
+ Description=PI WEB session daemon
183
183
 
184
184
  [Service]
185
185
  Type=simple
@@ -194,7 +194,7 @@ WantedBy=default.target
194
194
  function webUnit(options, executables) {
195
195
  const configEnvironment = options.config === undefined ? "" : `Environment="PI_WEB_CONFIG=${systemdEscape(resolve(options.config))}"\n`;
196
196
  return `[Unit]
197
- Description=Pi Web server
197
+ Description=PI WEB server
198
198
  After=${sessiondServiceName}
199
199
  Wants=${sessiondServiceName}
200
200
 
@@ -219,7 +219,7 @@ async function writeInitialConfig(options) {
219
219
  async function install(args) {
220
220
  const options = parseInstallOptions(args);
221
221
  const executables = resolveServiceExecutables();
222
- console.log("Running Pi Web install preflight checks...");
222
+ console.log("Running PI WEB install preflight checks...");
223
223
  console.log(`Service shell: ${describeServiceShell()}`);
224
224
  if (!runChecks(installPreflightChecks(executables))) {
225
225
  printPathSetupAdvice();
@@ -232,7 +232,7 @@ async function install(args) {
232
232
  run("systemctl", ["--user", "daemon-reload"], { check: true });
233
233
  run("systemctl", ["--user", "enable", "--now", sessiondServiceName], { check: true });
234
234
  run("systemctl", ["--user", "enable", "--now", webServiceName], { check: true });
235
- console.log(`\nPi Web is installed and starting.`);
235
+ console.log(`\nPI WEB is installed and starting.`);
236
236
  console.log(`Config: ${configPath}`);
237
237
  console.log(`Open: http://${options.host === "0.0.0.0" ? "127.0.0.1" : options.host}:${options.port}`);
238
238
  const linger = isLingerEnabled();
@@ -255,7 +255,7 @@ async function uninstall() {
255
255
  await rm(join(serviceDir, webServiceName), { force: true });
256
256
  await rm(join(serviceDir, sessiondServiceName), { force: true });
257
257
  run("systemctl", ["--user", "daemon-reload"]);
258
- console.log("Pi Web systemd user services removed.");
258
+ console.log("PI WEB systemd user services removed.");
259
259
  }
260
260
  function serviceAction(action) {
261
261
  run("systemctl", ["--user", action, sessiondServiceName, webServiceName], { check: action !== "status" });
@@ -337,11 +337,11 @@ function printPathSetupAdvice() {
337
337
  }
338
338
  else if (shell.name === "zsh") {
339
339
  console.log(" Detected zsh. Put PATH setup for node/version managers/tools in ~/.zprofile, not only ~/.zshrc.");
340
- console.log(" Avoid relying on prompt hooks; Pi Web services run non-interactive login shells.");
340
+ console.log(" Avoid relying on prompt hooks; PI WEB services run non-interactive login shells.");
341
341
  }
342
342
  else {
343
343
  console.log(" Detected fish. Prefer universal PATH setup such as `fish_add_path -U ...` for tools needed by services or agents.");
344
- console.log(" Avoid relying on prompt hooks; Pi Web services run non-interactive login shells.");
344
+ console.log(" Avoid relying on prompt hooks; PI WEB services run non-interactive login shells.");
345
345
  }
346
346
  }
347
347
  function doctor() {
@@ -361,13 +361,13 @@ function doctor() {
361
361
  }
362
362
  if (!ok) {
363
363
  console.log("\nIf a command works in your terminal but fails here, make sure your service shell login files set PATH the same way.");
364
- console.log("If a bundled entrypoint is not accessible, reinstall or update the Pi Web package.");
364
+ console.log("If a bundled entrypoint is not accessible, reinstall or update the PI WEB package.");
365
365
  printPathSetupAdvice();
366
366
  process.exitCode = 1;
367
367
  }
368
368
  }
369
369
  function help() {
370
- console.log(`Pi Web
370
+ console.log(`PI WEB
371
371
 
372
372
  Usage:
373
373
  pi-web install [--host 127.0.0.1] [--port 8504] [--config ~/.config/pi-web/config.json]
Binary file
@@ -1,4 +1,4 @@
1
- import{w as a,J as p,$ as h,A as m,X as g,U as f,_ as v,a0 as b}from"./vendor-editor-core-B4Sq6exx.js";import{g as y,r as w,p as C,h as x,c as j,m as _,j as k,a as u}from"./vendor-editor-languages-DznYbTkJ.js";import{d as E}from"./vendor-editor-legacy-B4QLsWF8.js";import{i as H,b as O,a as S,n as d,e as P,t as L}from"./index-DmVN7DQr.js";var D=Object.defineProperty,F=Object.getOwnPropertyDescriptor,i=(e,o,n,s)=>{for(var t=s>1?void 0:s?F(o,n):o,c=e.length-1,l;c>=0;c--)(l=e[c])&&(t=(s?l(o,n,t):l(t))||t);return s&&t&&D(o,n,t),t};let r=class extends H{constructor(){super(...arguments),this.content=""}firstUpdated(){this.recreateEditor()}updated(e){(e.has("content")||e.has("language"))&&this.recreateEditor()}disconnectedCallback(){this.view?.destroy(),this.view=void 0,super.disconnectedCallback()}render(){return O`<div class="host"></div>`}recreateEditor(){this.editorHost&&(this.view?.destroy(),this.view=new a({parent:this.editorHost,state:p.create({doc:this.content,extensions:[h(),m.of(g),f(v,{fallback:!0}),p.readOnly.of(!0),a.editable.of(!1),a.lineWrapping,M,...R(this.language)]})}))}};r.styles=S`
1
+ import{w as a,J as p,$ as h,A as m,X as g,U as f,_ as v,a0 as b}from"./vendor-editor-core-B4Sq6exx.js";import{g as y,r as w,p as C,h as x,c as j,m as _,j as k,a as u}from"./vendor-editor-languages-DznYbTkJ.js";import{d as E}from"./vendor-editor-legacy-B4QLsWF8.js";import{i as H,b as O,a as S,n as d,e as P,t as L}from"./index-BH4t_Up6.js";var D=Object.defineProperty,F=Object.getOwnPropertyDescriptor,i=(e,o,n,s)=>{for(var t=s>1?void 0:s?F(o,n):o,c=e.length-1,l;c>=0;c--)(l=e[c])&&(t=(s?l(o,n,t):l(t))||t);return s&&t&&D(o,n,t),t};let r=class extends H{constructor(){super(...arguments),this.content=""}firstUpdated(){this.recreateEditor()}updated(e){(e.has("content")||e.has("language"))&&this.recreateEditor()}disconnectedCallback(){this.view?.destroy(),this.view=void 0,super.disconnectedCallback()}render(){return O`<div class="host"></div>`}recreateEditor(){this.editorHost&&(this.view?.destroy(),this.view=new a({parent:this.editorHost,state:p.create({doc:this.content,extensions:[h(),m.of(g),f(v,{fallback:!0}),p.readOnly.of(!0),a.editable.of(!1),a.lineWrapping,M,...R(this.language)]})}))}};r.styles=S`
2
2
  :host { display: block; min-height: 0; height: 100%; }
3
3
  .host { height: 100%; min-height: 0; overflow: auto; }
4
4
  `;i([d()],r.prototype,"content",2);i([d()],r.prototype,"language",2);i([P(".host")],r.prototype,"editorHost",2);r=i([L("code-viewer")],r);const M=a.theme({"&":{height:"100%",color:"var(--pi-text)",backgroundColor:"var(--pi-bg)",fontSize:"12px"},".cm-scroller":{fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",lineHeight:"1.45"},".cm-gutters":{backgroundColor:"var(--pi-bg)",color:"var(--pi-dim)",borderRight:"1px solid var(--pi-border-muted)"},".cm-activeLineGutter":{backgroundColor:"transparent"},".cm-activeLine":{backgroundColor:"transparent"},".cm-content":{caretColor:"transparent"},"&.cm-focused":{outline:"none"}});function R(e){if(e===void 0)return[];switch(e){case"typescript":return[u({typescript:!0})];case"javascript":return[u()];case"json":return[k()];case"markdown":return[_()];case"css":return[j()];case"html":return[x()];case"python":return[C()];case"rust":return[w()];case"go":return[y()];case"diff":return[b.define(E)];default:return[]}}export{r as CodeViewer};
@@ -1,4 +1,4 @@
1
- import{i as T,c as h,s as x,d as u,f as y,b as c,a as I,n as p,e as k,r as d,t as S}from"./index-DmVN7DQr.js";import{D as f,o as v}from"./vendor-terminal-DjQ08hXu.js";import"./vendor-editor-core-B4Sq6exx.js";import"./vendor-editor-languages-DznYbTkJ.js";var C=Object.defineProperty,O=Object.getOwnPropertyDescriptor,a=(e,t,i,r)=>{for(var s=r>1?void 0:r?O(t,i):t,n=e.length-1,l;n>=0;n--)(l=e[n])&&(s=(r?l(t,i,s):l(s))||s);return r&&s&&C(t,i,s),s};const A={cursorBlink:!0,convertEol:!0,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",fontSize:13},z={cols:100,rows:30};let o=class extends T{constructor(){super(...arguments),this.autoStart=!1,this.onSelectTerminal=()=>{},this.terminals=[],this.loading=!1,this.visible=!1,this.suppressTerminalInput=!1}connectedCallback(){super.connectedCallback(),this.themeObserver=new MutationObserver(()=>{this.applyTerminalTheme()}),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["class","style","data-theme"]})}firstUpdated(){this.intersectionObserver=new IntersectionObserver(e=>{this.visible=e[0]?.isIntersecting===!0}),this.intersectionObserver.observe(this)}disconnectedCallback(){this.intersectionObserver?.disconnect(),this.intersectionObserver=void 0,this.themeObserver?.disconnect(),this.themeObserver=void 0,this.disposeTerminalView(),super.disconnectedCallback()}willUpdate(e){const t=this.workspace?.path;if(t!==this.observedCwd){this.observedCwd=t,this.loadedCwd=void 0,this.autoStartConsumedCwd=void 0,this.terminals=[],this.selectedId=void 0,this.disposeTerminalView();return}if(e.has("selectedTerminalId")){if(e.get("selectedTerminalId")!==void 0&&this.selectedTerminalId===void 0){this.loadedCwd=void 0,this.selectTerminalIdInView(void 0);return}this.applyRequestedTerminalSelection()}}updated(e){this.loadVisibleWorkspaceTerminals(),e.has("selectedTerminalId")&&this.shouldReloadForRequestedTerminal()&&this.loadTerminals(),this.ensureTerminalView()}loadVisibleWorkspaceTerminals(){const e=this.workspace?.path;!this.visible||e===void 0||e===this.loadedCwd||(this.loadedCwd=e,this.loadTerminals())}async loadTerminals(){this.loading=!0,this.error=void 0;try{if(this.workspace===void 0)return;const e=this.consumeAutoStart(),t=await h.terminals(this.workspace.projectId,this.workspace.id);this.terminals=t,this.selectPreferredLoadedTerminal({replaceUrl:!0}),t.length===0&&e&&await this.startTerminal()}catch(e){this.error=e instanceof Error?e.message:String(e)}finally{this.loading=!1}}applyRequestedTerminalSelection(){this.selectedTerminalId!==void 0&&!this.terminals.some(e=>e.id===this.selectedTerminalId)||this.selectPreferredLoadedTerminal({replaceUrl:!0})}consumeAutoStart(){const e=this.workspace?.path;return!this.autoStart||e===void 0||this.autoStartConsumedCwd===e?!1:(this.autoStartConsumedCwd=e,!0)}shouldReloadForRequestedTerminal(){const e=this.workspace?.path;return this.visible&&e!==void 0&&e===this.loadedCwd&&this.selectedTerminalId!==void 0&&!this.loading&&!this.terminals.some(t=>t.id===this.selectedTerminalId)}selectPreferredLoadedTerminal(e){let t=x(this.terminals,{targetTerminalId:this.selectedTerminalId});t===void 0&&this.selectedTerminalId!==void 0&&(t=u(this.terminals)),this.selectTerminalIdInView(t?.id),(t?.id!==this.selectedTerminalId||t===void 0&&this.selectedTerminalId!==void 0)&&this.onSelectTerminal(t?.id,{replace:e?.replaceUrl===!0})}selectTerminalIdInView(e){this.selectedId!==e&&(this.selectedId=e,this.disposeTerminalView())}async startTerminal(){if(this.workspace!==void 0){this.error=void 0;try{const e=this.measureTerminalSize()??z,t=await h.startTerminal(this.workspace.projectId,this.workspace.id,e);this.terminals=[...this.terminals,t],this.selectTerminal(t.id)}catch(e){this.error=e instanceof Error?e.message:String(e)}}}async closeTerminal(e,t){t.stopPropagation();try{if(this.workspace===void 0)return;await h.closeTerminal(this.workspace.projectId,this.workspace.id,e);const i=this.terminals.filter(r=>r.id!==e);if(this.terminals=i,this.selectedId===e||this.selectedTerminalId===e){const r=u(i)?.id;this.selectTerminalIdInView(r),this.onSelectTerminal(r,{replace:!0})}}catch(i){this.error=i instanceof Error?i.message:String(i)}}selectTerminal(e){this.selectedId!==e&&this.selectTerminalIdInView(e),this.onSelectTerminal(e)}ensureTerminalView(){const e=this.workspace,t=this.terminalHostElement();if(!this.visible||this.terminal!==void 0||this.selectedId===void 0||t===void 0||e===void 0)return;const i=new f(b(this)),r=new v;i.loadAddon(r),i.open(t),this.terminal=i,this.fitAddon=r,this.resizeObserver=new ResizeObserver(()=>{this.fitAndNotify()}),this.resizeObserver.observe(t),i.onData(n=>{if(this.suppressTerminalInput)return;const l=$(n);l!==""&&this.send({type:"input",data:l})});const s=this.fitTerminal();this.connectSocket(e.projectId,e.id,this.selectedId,i,s),requestAnimationFrame(()=>{this.fitAndNotify()}),i.focus()}connectSocket(e,t,i,r,s){const n=y(e,t,i,s);n.binaryType="arraybuffer",this.socket=n,n.addEventListener("open",()=>{this.fitAndNotify()}),n.addEventListener("message",l=>{this.handleSocketMessage(l.data,i,r)}),n.addEventListener("close",()=>{this.socket===n&&(this.socket=void 0)})}async handleSocketMessage(e,t,i){try{const r=E(await V(e));r.type==="output"&&this.writeTerminalOutput(i,r.data,r.replay===!0),r.type==="exit"&&(i.writeln(`\r
1
+ import{i as T,c as h,s as x,d as u,f as y,b as c,a as I,n as p,e as k,r as d,t as S}from"./index-BH4t_Up6.js";import{D as f,o as v}from"./vendor-terminal-DjQ08hXu.js";import"./vendor-editor-core-B4Sq6exx.js";import"./vendor-editor-languages-DznYbTkJ.js";var C=Object.defineProperty,O=Object.getOwnPropertyDescriptor,a=(e,t,i,r)=>{for(var s=r>1?void 0:r?O(t,i):t,n=e.length-1,l;n>=0;n--)(l=e[n])&&(s=(r?l(t,i,s):l(s))||s);return r&&s&&C(t,i,s),s};const A={cursorBlink:!0,convertEol:!0,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",fontSize:13},z={cols:100,rows:30};let o=class extends T{constructor(){super(...arguments),this.autoStart=!1,this.onSelectTerminal=()=>{},this.terminals=[],this.loading=!1,this.visible=!1,this.suppressTerminalInput=!1}connectedCallback(){super.connectedCallback(),this.themeObserver=new MutationObserver(()=>{this.applyTerminalTheme()}),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["class","style","data-theme"]})}firstUpdated(){this.intersectionObserver=new IntersectionObserver(e=>{this.visible=e[0]?.isIntersecting===!0}),this.intersectionObserver.observe(this)}disconnectedCallback(){this.intersectionObserver?.disconnect(),this.intersectionObserver=void 0,this.themeObserver?.disconnect(),this.themeObserver=void 0,this.disposeTerminalView(),super.disconnectedCallback()}willUpdate(e){const t=this.workspace?.path;if(t!==this.observedCwd){this.observedCwd=t,this.loadedCwd=void 0,this.autoStartConsumedCwd=void 0,this.terminals=[],this.selectedId=void 0,this.disposeTerminalView();return}if(e.has("selectedTerminalId")){if(e.get("selectedTerminalId")!==void 0&&this.selectedTerminalId===void 0){this.loadedCwd=void 0,this.selectTerminalIdInView(void 0);return}this.applyRequestedTerminalSelection()}}updated(e){this.loadVisibleWorkspaceTerminals(),e.has("selectedTerminalId")&&this.shouldReloadForRequestedTerminal()&&this.loadTerminals(),this.ensureTerminalView()}loadVisibleWorkspaceTerminals(){const e=this.workspace?.path;!this.visible||e===void 0||e===this.loadedCwd||(this.loadedCwd=e,this.loadTerminals())}async loadTerminals(){this.loading=!0,this.error=void 0;try{if(this.workspace===void 0)return;const e=this.consumeAutoStart(),t=await h.terminals(this.workspace.projectId,this.workspace.id);this.terminals=t,this.selectPreferredLoadedTerminal({replaceUrl:!0}),t.length===0&&e&&await this.startTerminal()}catch(e){this.error=e instanceof Error?e.message:String(e)}finally{this.loading=!1}}applyRequestedTerminalSelection(){this.selectedTerminalId!==void 0&&!this.terminals.some(e=>e.id===this.selectedTerminalId)||this.selectPreferredLoadedTerminal({replaceUrl:!0})}consumeAutoStart(){const e=this.workspace?.path;return!this.autoStart||e===void 0||this.autoStartConsumedCwd===e?!1:(this.autoStartConsumedCwd=e,!0)}shouldReloadForRequestedTerminal(){const e=this.workspace?.path;return this.visible&&e!==void 0&&e===this.loadedCwd&&this.selectedTerminalId!==void 0&&!this.loading&&!this.terminals.some(t=>t.id===this.selectedTerminalId)}selectPreferredLoadedTerminal(e){let t=x(this.terminals,{targetTerminalId:this.selectedTerminalId});t===void 0&&this.selectedTerminalId!==void 0&&(t=u(this.terminals)),this.selectTerminalIdInView(t?.id),(t?.id!==this.selectedTerminalId||t===void 0&&this.selectedTerminalId!==void 0)&&this.onSelectTerminal(t?.id,{replace:e?.replaceUrl===!0})}selectTerminalIdInView(e){this.selectedId!==e&&(this.selectedId=e,this.disposeTerminalView())}async startTerminal(){if(this.workspace!==void 0){this.error=void 0;try{const e=this.measureTerminalSize()??z,t=await h.startTerminal(this.workspace.projectId,this.workspace.id,e);this.terminals=[...this.terminals,t],this.selectTerminal(t.id)}catch(e){this.error=e instanceof Error?e.message:String(e)}}}async closeTerminal(e,t){t.stopPropagation();try{if(this.workspace===void 0)return;await h.closeTerminal(this.workspace.projectId,this.workspace.id,e);const i=this.terminals.filter(r=>r.id!==e);if(this.terminals=i,this.selectedId===e||this.selectedTerminalId===e){const r=u(i)?.id;this.selectTerminalIdInView(r),this.onSelectTerminal(r,{replace:!0})}}catch(i){this.error=i instanceof Error?i.message:String(i)}}selectTerminal(e){this.selectedId!==e&&this.selectTerminalIdInView(e),this.onSelectTerminal(e)}ensureTerminalView(){const e=this.workspace,t=this.terminalHostElement();if(!this.visible||this.terminal!==void 0||this.selectedId===void 0||t===void 0||e===void 0)return;const i=new f(b(this)),r=new v;i.loadAddon(r),i.open(t),this.terminal=i,this.fitAddon=r,this.resizeObserver=new ResizeObserver(()=>{this.fitAndNotify()}),this.resizeObserver.observe(t),i.onData(n=>{if(this.suppressTerminalInput)return;const l=$(n);l!==""&&this.send({type:"input",data:l})});const s=this.fitTerminal();this.connectSocket(e.projectId,e.id,this.selectedId,i,s),requestAnimationFrame(()=>{this.fitAndNotify()}),i.focus()}connectSocket(e,t,i,r,s){const n=y(e,t,i,s);n.binaryType="arraybuffer",this.socket=n,n.addEventListener("open",()=>{this.fitAndNotify()}),n.addEventListener("message",l=>{this.handleSocketMessage(l.data,i,r)}),n.addEventListener("close",()=>{this.socket===n&&(this.socket=void 0)})}async handleSocketMessage(e,t,i){try{const r=E(await V(e));r.type==="output"&&this.writeTerminalOutput(i,r.data,r.replay===!0),r.type==="exit"&&(i.writeln(`\r
2
2
  [process exited${r.exitCode===void 0?"":` with code ${String(r.exitCode)}`}]`),this.terminals=this.terminals.map(s=>s.id===t?{...s,exited:!0,...r.exitCode===void 0?{}:{exitCode:r.exitCode}}:s)),r.type==="error"&&i.writeln(`\r
3
3
  [terminal error: ${r.message}]`)}catch(r){i.writeln(`\r
4
4
  [terminal error: ${r instanceof Error?r.message:String(r)}]`)}}writeTerminalOutput(e,t,i){if(!i){e.write(t);return}this.suppressTerminalInput=!0,e.write(t,()=>{this.suppressTerminalInput=!1})}fitAndNotify(){const e=this.fitTerminal();e!==void 0&&this.send({type:"resize",...e})}fitTerminal(){if(this.fitAddon===void 0||this.terminal===void 0)return;const e=this.fitAddon.proposeDimensions(),t=w(e);if(t!==void 0)return this.fitAddon.fit(),t}measureTerminalSize(){const e=this.fitTerminal();if(e!==void 0)return e;const t=this.terminalHostElement();if(this.terminal!==void 0||t===void 0)return;const i=new f(b(this)),r=new v;i.loadAddon(r),i.open(t);const s=w(r.proposeDimensions());return i.dispose(),s}terminalHostElement(){const e=this.terminalHost;return e instanceof HTMLDivElement?e:void 0}applyTerminalTheme(){this.terminal!==void 0&&(this.terminal.options.theme=g(this))}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify(e))}disposeTerminalView(){this.resizeObserver?.disconnect(),this.resizeObserver=void 0,this.socket?.close(),this.socket=void 0,this.terminal?.dispose(),this.terminal=void 0,this.fitAddon=void 0}render(){return c`