@jmfederico/pi-web 1.202605.8 → 1.202605.10

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 (39) hide show
  1. package/README.md +4 -4
  2. package/dist/client/assets/CodeViewer-UK7QM1M9.js +4 -0
  3. package/dist/client/assets/TerminalPanel-BTAllNY6.js +47 -0
  4. package/dist/client/assets/index-RNmbpUx6.js +1056 -0
  5. package/dist/client/index.html +40 -2
  6. package/dist/pi-web-plugins/info/pi-web-plugin.js +44 -0
  7. package/dist/pi-web-plugins/pi-web/package.json +9 -0
  8. package/dist/pi-web-plugins/pi-web/pi-web-plugin.js +144 -0
  9. package/dist/server/activity/workspaceActivityRoutes.js +4 -0
  10. package/dist/server/activity/workspaceActivityRoutes.js.map +1 -0
  11. package/dist/server/activity/workspaceActivityService.js +84 -0
  12. package/dist/server/activity/workspaceActivityService.js.map +1 -0
  13. package/dist/server/app.js +2 -0
  14. package/dist/server/app.js.map +1 -1
  15. package/dist/server/piWebPluginService.js +5 -1
  16. package/dist/server/piWebPluginService.js.map +1 -1
  17. package/dist/server/piWebStatus.js +371 -0
  18. package/dist/server/piWebStatus.js.map +1 -0
  19. package/dist/server/sessiond/sessionProxyRoutes.js +1 -0
  20. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  21. package/dist/server/sessiond.js +13 -3
  22. package/dist/server/sessiond.js.map +1 -1
  23. package/dist/server/sessions/editPreview.js +196 -0
  24. package/dist/server/sessions/editPreview.js.map +1 -0
  25. package/dist/server/sessions/piSessionService.js +159 -25
  26. package/dist/server/sessions/piSessionService.js.map +1 -1
  27. package/dist/server/sessions/sessionArchiveStore.js +168 -20
  28. package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
  29. package/dist/server/terminals/terminalService.js +7 -2
  30. package/dist/server/terminals/terminalService.js.map +1 -1
  31. package/dist/shared/activity.js +26 -0
  32. package/dist/shared/activity.js.map +1 -0
  33. package/docs/plugins.md +14 -4
  34. package/package.json +9 -7
  35. package/dist/client/assets/CodeViewer-Cp0QKepT.js +0 -4
  36. package/dist/client/assets/TerminalPanel-C7TcN1cv.js +0 -47
  37. package/dist/client/assets/index-b1ytby08.js +0 -871
  38. package/pi-web-plugins/info/pi-web-plugin.js +0 -42
  39. /package/{pi-web-plugins → dist/pi-web-plugins}/info/package.json +0 -0
package/README.md CHANGED
@@ -101,7 +101,7 @@ Pi Web keeps its own state intentionally small:
101
101
 
102
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
- 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` plugin is the canonical real example.
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
 
@@ -211,7 +211,7 @@ npm run dev:web
211
211
  npm run dev:client
212
212
  ```
213
213
 
214
- You can restart `dev:web` or `dev:client` without stopping active Pi sessions.
214
+ `dev:web` also watches bundled plugin TypeScript and rebuilds the browser-loaded plugin JavaScript under `dist/pi-web-plugins/`. You can restart `dev:web` or `dev:client` without stopping active Pi sessions.
215
215
 
216
216
  ## Production-style run from a checkout
217
217
 
@@ -229,7 +229,7 @@ npm run pack:dry
229
229
  npm publish --access public
230
230
  ```
231
231
 
232
- `prepack` builds `dist/` 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.
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
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
 
@@ -260,7 +260,7 @@ Environment variables:
260
260
  A practical local or server setup is two user services:
261
261
 
262
262
  - `pi-web-sessiond.service` runs `npm run start:sessiond` without autoreload.
263
- - `pi-web-ui-dev.service` runs `npm run dev:web` and `npm run dev:client` for API reloads and Vite HMR.
263
+ - `pi-web-ui-dev.service` runs `npm run dev:web` and `npm run dev:client` for API reloads, bundled plugin rebuilds, and Vite HMR.
264
264
 
265
265
  Example units:
266
266
 
@@ -0,0 +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-RNmbpUx6.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
+ :host { display: block; min-height: 0; height: 100%; }
3
+ .host { height: 100%; min-height: 0; overflow: auto; }
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};
@@ -0,0 +1,47 @@
1
+ import{i as w,c as m,d as x,b as c,a as y,n as b,e as T,r as d,t as k}from"./index-RNmbpUx6.js";import{D as h,o as u}from"./vendor-terminal-DjQ08hXu.js";import"./vendor-editor-core-B4Sq6exx.js";import"./vendor-editor-languages-DznYbTkJ.js";var S=Object.defineProperty,O=Object.getOwnPropertyDescriptor,a=(e,t,i,r)=>{for(var s=r>1?void 0:r?O(t,i):t,o=e.length-1,l;o>=0;o--)(l=e[o])&&(s=(r?l(t,i,s):l(s))||s);return r&&s&&S(t,i,s),s};const I={cursorBlink:!0,convertEol:!0,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",fontSize:13},z={cols:100,rows:30};let n=class extends w{constructor(){super(...arguments),this.autoStart=!1,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(){const e=this.workspace?.path;e!==this.observedCwd&&(this.observedCwd=e,this.loadedCwd=void 0,this.terminals=[],this.selectedId=void 0,this.disposeTerminalView())}updated(){this.loadVisibleWorkspaceTerminals(),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=await m.terminals(this.workspace.projectId,this.workspace.id);this.terminals=e,this.selectedId=e.find(t=>!t.exited)?.id??e[0]?.id,e.length===0&&this.autoStart&&await this.startTerminal()}catch(e){this.error=e instanceof Error?e.message:String(e)}finally{this.loading=!1}}async startTerminal(){if(this.workspace!==void 0){this.error=void 0;try{const e=this.measureTerminalSize()??z,t=await m.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 m.closeTerminal(this.workspace.projectId,this.workspace.id,e);const i=this.terminals.filter(r=>r.id!==e);this.terminals=i,this.selectedId===e&&(this.selectedId=i[0]?.id,this.disposeTerminalView())}catch(i){this.error=i instanceof Error?i.message:String(i)}}selectTerminal(e){this.selectedId!==e&&(this.selectedId=e,this.disposeTerminalView())}ensureTerminalView(){const e=this.workspace;if(!this.visible||this.terminal!==void 0||this.selectedId===void 0||this.terminalHost===void 0||e===void 0)return;const t=new h(f(this)),i=new u;t.loadAddon(i),t.open(this.terminalHost),this.terminal=t,this.fitAddon=i,this.resizeObserver=new ResizeObserver(()=>{this.fitAndNotify()}),this.resizeObserver.observe(this.terminalHost),t.onData(s=>{if(this.suppressTerminalInput)return;const o=C(s);o!==""&&this.send({type:"input",data:o})});const r=this.fitTerminal();this.connectSocket(e.projectId,e.id,this.selectedId,t,r),requestAnimationFrame(()=>{this.fitAndNotify()}),t.focus()}connectSocket(e,t,i,r,s){const o=x(e,t,i,s);o.binaryType="arraybuffer",this.socket=o,o.addEventListener("open",()=>{this.fitAndNotify()}),o.addEventListener("message",l=>{this.handleSocketMessage(l.data,i,r)}),o.addEventListener("close",()=>{this.socket===o&&(this.socket=void 0)})}async handleSocketMessage(e,t,i){try{const r=A(await $(e));r.type==="output"&&this.writeTerminalOutput(i,r.data,r.replay===!0),r.type==="exit"&&(i.writeln(`\r
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
+ [terminal error: ${r.message}]`)}catch(r){i.writeln(`\r
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=v(e);if(t!==void 0)return this.fitAddon.fit(),t}measureTerminalSize(){const e=this.fitTerminal();if(e!==void 0)return e;if(this.terminal!==void 0||this.terminalHost===void 0)return;const t=new h(f(this)),i=new u;t.loadAddon(i),t.open(this.terminalHost);const r=v(i.proposeDimensions());return t.dispose(),r}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`
5
+ <section class="terminal-shell">
6
+ <div class="terminal-tabs">
7
+ ${this.terminals.map(e=>c`
8
+ <button class=${this.selectedId===e.id?"selected":""} @click=${()=>{this.selectTerminal(e.id)}}>
9
+ <span>${e.name}${e.exited?" · exited":""}</span>
10
+ <small @click=${t=>{this.closeTerminal(e.id,t)}}>×</small>
11
+ </button>
12
+ `)}
13
+ <button class="new" ?disabled=${this.workspace===void 0} @click=${()=>{this.startTerminal()}}>+ Shell</button>
14
+ </div>
15
+ ${this.error===void 0?null:c`<p class="error">${this.error}</p>`}
16
+ ${this.loading?c`<p class="muted">Loading terminals…</p>`:null}
17
+ <div class="terminal-host"></div>
18
+ </section>
19
+ `}};n.styles=y`
20
+ :host { flex: 1 1 auto; min-height: 0; display: flex; }
21
+ .terminal-shell { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--pi-terminal-bg); }
22
+ .terminal-tabs { flex: 0 0 auto; display: flex; gap: 6px; align-items: center; padding: 6px; border-bottom: 1px solid var(--pi-border-muted); background: var(--pi-bg); overflow: auto; }
23
+ button { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 180px; border: 1px solid var(--pi-border); border-radius: 7px; background: var(--pi-surface); color: var(--pi-text); padding: 5px 7px; cursor: pointer; }
24
+ button.selected { border-color: var(--pi-accent); background: var(--pi-selection-bg); }
25
+ button.new { flex: 0 0 auto; color: var(--pi-muted); }
26
+ button span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
27
+ button small { color: var(--pi-muted); font-size: 14px; line-height: 1; }
28
+ button small:hover { color: var(--pi-danger); }
29
+ button:disabled { opacity: .5; cursor: not-allowed; }
30
+ .terminal-host { flex: 1 1 auto; min-height: 0; padding: 6px; box-sizing: border-box; overflow: hidden; }
31
+ .terminal-host .xterm { height: 100%; cursor: text; position: relative; user-select: none; }
32
+ .terminal-host .xterm.focus, .terminal-host .xterm:focus { outline: none; }
33
+ .terminal-host .xterm-helpers { position: absolute; top: 0; z-index: 5; }
34
+ .terminal-host .xterm-helper-textarea { position: absolute !important; left: -9999em !important; top: 0 !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; padding: 0 !important; border: 0 !important; margin: 0 !important; opacity: 0 !important; z-index: -5 !important; white-space: nowrap !important; overflow: hidden !important; resize: none !important; outline: 0 !important; appearance: none !important; }
35
+ .terminal-host .xterm-viewport { position: absolute; inset: 0; overflow-y: scroll; cursor: default; background-color: var(--pi-terminal-bg); }
36
+ .terminal-host .xterm-screen { position: relative; }
37
+ .terminal-host .xterm-screen canvas { position: absolute; left: 0; top: 0; }
38
+ .terminal-host .xterm-char-measure-element { display: inline-block; visibility: hidden; position: absolute; top: 0; left: -9999em; line-height: normal; }
39
+ .terminal-host .xterm-accessibility:not(.debug), .terminal-host .xterm-message { position: absolute; inset: 0; z-index: 10; color: transparent; pointer-events: none; }
40
+ .terminal-host .xterm-accessibility-tree:not(.debug) *::selection { color: transparent; }
41
+ .terminal-host .xterm-accessibility-tree { font-family: monospace; user-select: text; white-space: pre; }
42
+ .terminal-host .xterm-accessibility-tree > div { transform-origin: left; width: fit-content; }
43
+ .terminal-host .live-region { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
44
+ .error { flex: 0 0 auto; margin: 0; padding: 8px; color: var(--pi-danger); border-bottom: 1px solid var(--pi-border); background: var(--pi-surface); }
45
+ .muted { margin: 10px; color: var(--pi-muted); }
46
+ .xterm { height: 100%; }
47
+ `;a([b({attribute:!1})],n.prototype,"workspace",2);a([b({type:Boolean})],n.prototype,"autoStart",2);a([T(".terminal-host")],n.prototype,"terminalHost",2);a([d()],n.prototype,"terminals",2);a([d()],n.prototype,"selectedId",2);a([d()],n.prototype,"loading",2);a([d()],n.prototype,"error",2);a([d()],n.prototype,"visible",2);n=a([k("terminal-panel")],n);function A(e){const t=JSON.parse(e);if(!N(t))return{type:"error",message:"Invalid terminal message"};const i=t;return i.type==="output"&&typeof i.data=="string"?{type:"output",data:i.data,...typeof i.replay=="boolean"?{replay:i.replay}:{}}:i.type==="exit"?{type:"exit",...typeof i.exitCode=="number"?{exitCode:i.exitCode}:{}}:i.type==="error"&&typeof i.message=="string"?{type:"error",message:i.message}:{type:"error",message:"Invalid terminal message"}}function C(e){return e.replaceAll("\x1B[I","").replaceAll("\x1B[O","")}async function $(e){return typeof e=="string"?e:e instanceof ArrayBuffer?new TextDecoder().decode(e):e instanceof Blob?await e.text():String(e)}function f(e){return{...I,theme:g(e)}}function g(e){return{background:p(e,"--pi-terminal-bg","#05070a"),foreground:p(e,"--pi-terminal-text","#e6edf3"),cursor:p(e,"--pi-accent","#58a6ff"),selectionBackground:p(e,"--pi-terminal-selection","#264f78")}}function p(e,t,i){const r=getComputedStyle(e).getPropertyValue(t).trim();return r===""?i:r}function v(e){if(!(e===void 0||!E(e.cols,e.rows)))return{cols:Math.floor(e.cols),rows:Math.floor(e.rows)}}function E(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e>0&&t>0}function N(e){return typeof e=="object"&&e!==null}export{n as TerminalPanel,C as filterTerminalInput};