pi-sdk-web 0.4.4 → 0.4.6

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
@@ -411,6 +411,11 @@ export class PiWebServer {
411
411
  case "abort":
412
412
  await this.session.abort();
413
413
  break;
414
+ case "abort_bash":
415
+ // Kill only the running bash (TUI Ctrl+C equivalent), leaving any
416
+ // agent activity untouched.
417
+ this.session.abortBash();
418
+ break;
414
419
  case "get_stats":
415
420
  this.broadcastStats();
416
421
  break;
@@ -1331,6 +1331,43 @@ class PiWebClient {
1331
1331
  if (!div) {
1332
1332
  div = this.createToolBlock('bash', { command: this.pendingBashCommand || '' }, key);
1333
1333
  div.className = 'tool-block pending';
1334
+ // Running indicator (spinner + label) - makes it obvious the stream
1335
+ // is still live even when output is quiet; removed on bash_result.
1336
+ const run = document.createElement('span');
1337
+ run.className = 'bash-running';
1338
+ run.textContent = '⟳ running';
1339
+ div.querySelector('.tool-title')?.appendChild(run);
1340
+ // Stop button in the title row (TUI Ctrl+C equivalent): abort_bash
1341
+ // kills only this running bash. Removed when the block finalizes.
1342
+ const stopBtn = document.createElement('button');
1343
+ stopBtn.className = 'bash-stop-btn';
1344
+ stopBtn.textContent = '⏹ stop';
1345
+ stopBtn.title = 'Stop this bash command (Ctrl+C in TUI)';
1346
+ stopBtn.addEventListener('click', (e) => {
1347
+ e.stopPropagation(); // don't toggle expand
1348
+ this.send({ type: 'abort_bash' });
1349
+ stopBtn.disabled = true;
1350
+ stopBtn.textContent = 'stopping...';
1351
+ });
1352
+ div.querySelector('.tool-title')?.appendChild(stopBtn);
1353
+ // The running bash block lives in the #bash-pending area (between the
1354
+ // message flow and the input box) so later messages don't push it
1355
+ // out of view - same as TUI's pendingMessagesContainer. bash_result
1356
+ // moves it into the normal message flow when done.
1357
+ const bashPending = document.getElementById('bash-pending');
1358
+ if (bashPending) {
1359
+ bashPending.appendChild(div);
1360
+ // Area-level status line at the TAIL (always visible even when the
1361
+ // block's own title scrolled out of view while output streams).
1362
+ const statusLine = document.createElement('div');
1363
+ statusLine.className = 'bash-stream-status';
1364
+ statusLine.textContent = '⟳ running…';
1365
+ bashPending.appendChild(statusLine);
1366
+ bashPending.style.display = 'block';
1367
+ bashPending.scrollTop = bashPending.scrollHeight; // keep tail visible
1368
+ } else {
1369
+ this.contentEl.appendChild(div);
1370
+ }
1334
1371
  this.bashEls.set(key, div);
1335
1372
  }
1336
1373
  // Append the delta to the full text and re-apply the preview (respects
@@ -1338,6 +1375,12 @@ class PiWebClient {
1338
1375
  const full = (div.dataset.fullOutput || '') + delta;
1339
1376
  div.dataset.fullOutput = full;
1340
1377
  this.applyToolPreview(div);
1378
+ // Keep the stream tail (and its running status line) visible inside the
1379
+ // pending area while output is coming in.
1380
+ const bashPending = document.getElementById('bash-pending');
1381
+ if (bashPending && bashPending.contains(div)) {
1382
+ bashPending.scrollTop = bashPending.scrollHeight;
1383
+ }
1341
1384
  if (this.wasAtBottom()) this.scrollToBottom();
1342
1385
  }
1343
1386
 
@@ -1355,6 +1398,22 @@ class PiWebClient {
1355
1398
  if (!(div.dataset.fullOutput && div.dataset.fullOutput.length > 0)) {
1356
1399
  this.setToolOutput(div, output);
1357
1400
  }
1401
+ // Move the finished block from the bash-pending (fixed) area into the
1402
+ // normal message flow, keeping its accumulated output.
1403
+ const bashPending = document.getElementById('bash-pending');
1404
+ if (bashPending && bashPending.contains(div)) {
1405
+ this.contentEl.appendChild(div);
1406
+ // Remove the area-level running status line, then hide the empty area.
1407
+ bashPending.querySelectorAll('.bash-stream-status').forEach((el) => el.remove());
1408
+ if (bashPending.children.length === 0) {
1409
+ bashPending.style.display = 'none';
1410
+ }
1411
+ this.scrollToBottom();
1412
+ }
1413
+ // Bash done: the running indicator and stop button are no longer
1414
+ // relevant.
1415
+ div.querySelector('.bash-running')?.remove();
1416
+ div.querySelector('.bash-stop-btn')?.remove();
1358
1417
 
1359
1418
  const isError = result.exitCode !== undefined && result.exitCode !== 0;
1360
1419
  div.className = isError ? 'tool-block error' : 'tool-block success';
@@ -31,6 +31,7 @@
31
31
  </div>
32
32
  </div>
33
33
  <div id="pending"></div>
34
+ <div id="bash-pending" style="display:none"></div>
34
35
  <div id="widgets"></div>
35
36
  <div id="status"></div>
36
37
  <div id="footer">
@@ -544,6 +544,75 @@ body {
544
544
  font-size: 12px;
545
545
  }
546
546
 
547
+ /* Running !/!! bash output (streaming) - fixed area between messages and
548
+ input, so later messages don't push it out of view (TUI analog:
549
+ pendingMessagesContainer). Moved into the message flow on completion. */
550
+ #bash-pending {
551
+ display: none;
552
+ padding: 8px 12px;
553
+ margin: 6px 8px 4px;
554
+ border: 1px solid var(--border);
555
+ border-radius: 6px;
556
+ background: var(--code-bg);
557
+ font-size: 13px;
558
+ /* ~10 lines of output visible; scrolling inside the block for more */
559
+ max-height: calc(10 * 1.5em);
560
+ overflow-y: auto;
561
+ }
562
+
563
+ .bash-stop-btn {
564
+ margin-left: 8px;
565
+ padding: 1px 8px;
566
+ font-size: 11px;
567
+ color: var(--error);
568
+ border: 1px solid var(--error);
569
+ border-radius: 4px;
570
+ background: transparent;
571
+ cursor: pointer;
572
+ vertical-align: middle;
573
+ }
574
+
575
+ .bash-running {
576
+ margin-left: 8px;
577
+ font-size: 11px;
578
+ color: var(--accent);
579
+ vertical-align: middle;
580
+ }
581
+
582
+ .bash-running::before {
583
+ content: '⟳';
584
+ display: inline-block;
585
+ margin-right: 4px;
586
+ animation: bash-spin 1s linear infinite;
587
+ }
588
+
589
+ @keyframes bash-spin {
590
+ from { transform: rotate(0deg); }
591
+ to { transform: rotate(360deg); }
592
+ }
593
+
594
+ .bash-stream-status {
595
+ margin-top: 2px;
596
+ font-size: 11px;
597
+ color: var(--accent);
598
+ }
599
+
600
+ .bash-stream-status::before {
601
+ content: '⟳';
602
+ display: inline-block;
603
+ margin-right: 4px;
604
+ animation: bash-spin 1s linear infinite;
605
+ }
606
+
607
+ .bash-stop-btn:hover {
608
+ background: color-mix(in srgb, var(--error) 15%, transparent);
609
+ }
610
+
611
+ .bash-stop-btn:disabled {
612
+ opacity: 0.6;
613
+ cursor: default;
614
+ }
615
+
547
616
  .widget-block {
548
617
  margin: 2px 0;
549
618
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
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": {