pi-sdk-web 0.4.10 → 0.4.11

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/dist/server.js CHANGED
@@ -764,10 +764,38 @@ export class PiWebServer {
764
764
  }
765
765
  };
766
766
  const unsubscribe = this.session.subscribe(listener);
767
+ // hasUI by extension: most extensions degrade gracefully to notify()
768
+ // (our notify -> browser dialog/toast), but a few (magic-context) use
769
+ // ctx.ui.custom() - an extension-drawn TUI panel we can't render. For
770
+ // those, hasUI:false routes them to their own text fallback
771
+ // (appendEntry -> modal), same as before.
772
+ const cmdPath = (cmd.sourceInfo && cmd.sourceInfo.path) || "";
773
+ const customOnly = /pi-magic-context|magic-context/.test(cmdPath);
774
+ const ui = this.uiContext;
775
+ const originalSink = ui.sink;
767
776
  try {
768
- await cmd.handler(args, this.buildCommandContext());
777
+ // While the command runs, notify() broadcasts are tagged with the
778
+ // command name as title, so the browser shows them as a titled modal
779
+ // (colored, Markdown-rendered) instead of a plain stream line.
780
+ ui.sink = (obj) => {
781
+ const req = obj;
782
+ if (req && req.method === "notify") {
783
+ // Tag command-time notifies with the command name as title so the
784
+ // browser renders them as a titled modal (colored, Markdown).
785
+ originalSink({ ...obj, title: `/${name}` });
786
+ }
787
+ else {
788
+ originalSink(obj);
789
+ }
790
+ };
791
+ await cmd.handler(args, this.buildCommandContext(customOnly ? false : true));
792
+ }
793
+ catch (e) {
794
+ ui.sink = originalSink;
795
+ throw e;
769
796
  }
770
797
  finally {
798
+ ui.sink = originalSink;
771
799
  unsubscribe();
772
800
  }
773
801
  // Show command output as a modal (TUI-like). The session entry remains
@@ -790,13 +818,15 @@ export class PiWebServer {
790
818
  });
791
819
  }
792
820
  }
793
- buildCommandContext() {
821
+ buildCommandContext(hasUI) {
794
822
  const sm = this.session.sessionManager;
795
823
  return {
796
824
  ui: this.uiContext,
797
- // No dialog-capable UI: extension commands fall back to text output
825
+ // hasUI is decided per command (see executeCommand): true for
826
+ // extensions that degrade to notify(), false for custom-panel-only
827
+ // extensions (magic-context) so they use their text fallback.
798
828
  mode: "print",
799
- hasUI: false,
829
+ hasUI,
800
830
  cwd: sm.getCwd(),
801
831
  sessionManager: sm,
802
832
  modelRegistry: new ModelRegistry(this.session.modelRuntime),
@@ -1915,8 +1915,9 @@ class PiWebClient {
1915
1915
 
1916
1916
  openExtensionNotify(req) {
1917
1917
  // Command output notifications (title starts with "/", e.g. /ctx-status) keep
1918
- // the modal. Other extension notifies are lightweight toasts (TUI shows
1919
- // notify as a transient status message, not a dialog).
1918
+ // the modal. Other extension notifies are appended to the chat stream as
1919
+ // persistent status lines, matching TUI's notify -> showStatus (a dim text
1920
+ // appended to the chat container; NOT a transient toast).
1920
1921
  if (req.title && String(req.title).startsWith('/')) {
1921
1922
  this.openModal(req.title || 'Notification', 'extension-notify');
1922
1923
  this.currentExtRequest = req;
@@ -1924,7 +1925,15 @@ class PiWebClient {
1924
1925
  this.modalList.innerHTML = `<div class="modal-message body-text">${this.renderMarkdown(req.message || '')}</div>`;
1925
1926
  return;
1926
1927
  }
1927
- this.showToast(req.message || '', req.notifyType);
1928
+ this.appendNotifyLine(req.message || '', req.notifyType);
1929
+ }
1930
+
1931
+ appendNotifyLine(message, type) {
1932
+ const div = document.createElement('div');
1933
+ div.className = 'notify-line' + (type ? ` notify-${type}` : '');
1934
+ div.innerHTML = ansiToHtml(String(message));
1935
+ this.contentEl.appendChild(div);
1936
+ this.scrollToBottom();
1928
1937
  }
1929
1938
 
1930
1939
  showToast(message, type) {
@@ -907,6 +907,7 @@ body {
907
907
  color: var(--text);
908
908
  font-size: 13px;
909
909
  line-height: 1.5;
910
+ white-space: pre-wrap;
910
911
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
911
912
  opacity: 1;
912
913
  transition: opacity 0.3s;
@@ -920,6 +921,27 @@ body {
920
921
  border-left-color: var(--error);
921
922
  }
922
923
 
924
+ /* Extension notify lines appended to the chat stream (TUI showStatus
925
+ equivalent): persistent dim status text, colored by type. */
926
+ .notify-line {
927
+ color: var(--dim);
928
+ font-size: 12px;
929
+ padding: 2px 12px;
930
+ white-space: pre-wrap;
931
+ overflow-wrap: anywhere;
932
+ border-left: 3px solid transparent;
933
+ }
934
+
935
+ .notify-warning {
936
+ color: var(--warning);
937
+ border-left-color: var(--warning);
938
+ }
939
+
940
+ .notify-error {
941
+ color: var(--error);
942
+ border-left-color: var(--error);
943
+ }
944
+
923
945
  .toast-hide {
924
946
  opacity: 0;
925
947
  }
@@ -199,6 +199,13 @@ export class WebUIContext {
199
199
  setFooter() { }
200
200
  setHeader() { }
201
201
  custom() {
202
+ // Pi's custom() shows an extension-drawn TUI component. Web has no TUI
203
+ // renderer, so a full component can't be displayed. Same headless stub
204
+ // as Pi's RPC mode (rpc-mode.ts: "Custom UI not supported in RPC mode"):
205
+ // settle immediately so commands awaiting the panel don't hang.
206
+ // Commands whose extensions branch on hasUI (magic-context ctx-status)
207
+ // are routed to their text fallback by executeCommand (hasUI:false);
208
+ // any other custom() caller just gets a no-op close.
202
209
  return Promise.resolve(undefined);
203
210
  }
204
211
  pasteToEditor() { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "Browser Web access for Pi (AI coding agent) via the Pi SDK - standalone module, zero modification to Pi itself",
5
5
  "type": "module",
6
6
  "bin": {