pi-sdk-web 0.3.5 → 0.3.7
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 +25 -5
- package/dist/static/app.js +29 -0
- package/dist/static/style.css +23 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { execFileSync } from "node:child_process";
|
|
12
12
|
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
-
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { readFile, unlink } from "node:fs/promises";
|
|
14
14
|
import { createServer } from "node:http";
|
|
15
15
|
import { dirname, join, resolve, sep } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
@@ -567,6 +567,21 @@ export class PiWebServer {
|
|
|
567
567
|
await this.resumeSession(path);
|
|
568
568
|
break;
|
|
569
569
|
}
|
|
570
|
+
case "delete_session": {
|
|
571
|
+
const path = typeof data.path === "string" && data.path ? data.path : "";
|
|
572
|
+
if (!path)
|
|
573
|
+
throw new Error("Missing 'path'");
|
|
574
|
+
// Safety: only allow deleting known session files (never arbitrary paths)
|
|
575
|
+
const known = (await listSessions()).some((s) => s.path === path);
|
|
576
|
+
if (!known)
|
|
577
|
+
throw new Error("Not a known session file");
|
|
578
|
+
// Align with TUI: cannot delete the currently active session
|
|
579
|
+
if (this.session.sessionFile === path) {
|
|
580
|
+
throw new Error("Cannot delete the currently active session");
|
|
581
|
+
}
|
|
582
|
+
await unlink(path);
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
570
585
|
default:
|
|
571
586
|
throw new Error(`Unsupported command: ${cmdType}`);
|
|
572
587
|
}
|
|
@@ -678,16 +693,21 @@ export class PiWebServer {
|
|
|
678
693
|
unsubscribe();
|
|
679
694
|
}
|
|
680
695
|
// Show command output as a modal (TUI-like). The session entry remains
|
|
681
|
-
// for history; the modal is the transient presentation.
|
|
696
|
+
// for history; the modal is the transient presentation. Only entries
|
|
697
|
+
// carrying a text payload are shown as modals — entries without
|
|
698
|
+
// data.text are status traces (e.g. minimode-status) and stay
|
|
699
|
+
// file/event-stream only, matching TUI, which renders nothing for them.
|
|
682
700
|
for (const entry of captured) {
|
|
683
701
|
const data = entry.data;
|
|
684
|
-
|
|
702
|
+
if (!data || typeof data.text !== "string" || data.text.trim().length === 0) {
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
685
705
|
this.broadcast({
|
|
686
706
|
type: "extension_ui_request",
|
|
687
707
|
id: crypto.randomUUID(),
|
|
688
708
|
method: "notify",
|
|
689
|
-
title: data
|
|
690
|
-
message,
|
|
709
|
+
title: data.title ?? `/${name}`,
|
|
710
|
+
message: data.text,
|
|
691
711
|
notifyType: "info",
|
|
692
712
|
});
|
|
693
713
|
}
|
package/dist/static/app.js
CHANGED
|
@@ -554,6 +554,12 @@ class PiWebClient {
|
|
|
554
554
|
}
|
|
555
555
|
|
|
556
556
|
renderCustom(entry) {
|
|
557
|
+
// Status traces (appendEntry entries without data.text, e.g.
|
|
558
|
+
// minimode-status {mode, tools}) are file/event-stream only — TUI
|
|
559
|
+
// renders nothing for them, so neither do we.
|
|
560
|
+
if (entry.type === 'custom' && !(entry.data && typeof entry.data.text === 'string' && entry.data.text.trim().length > 0)) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
557
563
|
const customType = entry.customType || 'custom';
|
|
558
564
|
const label = `[${customType}]`;
|
|
559
565
|
const content = this.customEntryText(entry);
|
|
@@ -1630,12 +1636,35 @@ class PiWebClient {
|
|
|
1630
1636
|
this.modalList.innerHTML = '<div class="modal-message">No other sessions available</div>';
|
|
1631
1637
|
return;
|
|
1632
1638
|
}
|
|
1639
|
+
this.resumeSessions = sessions;
|
|
1633
1640
|
this.renderModalItems(
|
|
1634
1641
|
sessions.map((s) => ({ name: s.name || s.id, desc: s.cwd, value: s.path })),
|
|
1635
1642
|
(item) => {
|
|
1636
1643
|
this.send({ type: 'resume', path: item.value });
|
|
1637
1644
|
},
|
|
1638
1645
|
);
|
|
1646
|
+
// Add delete buttons to each item (stopPropagation so clicks don't resume)
|
|
1647
|
+
this.modalList.querySelectorAll('.modal-item').forEach((el, i) => {
|
|
1648
|
+
const session = sessions[i];
|
|
1649
|
+
el.classList.add('resume-item');
|
|
1650
|
+
const del = document.createElement('span');
|
|
1651
|
+
del.className = 'resume-delete';
|
|
1652
|
+
del.title = 'Delete this session';
|
|
1653
|
+
del.textContent = '🗑';
|
|
1654
|
+
del.addEventListener('click', (e) => {
|
|
1655
|
+
e.stopPropagation();
|
|
1656
|
+
this.deleteSession(session);
|
|
1657
|
+
});
|
|
1658
|
+
el.appendChild(del);
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
deleteSession(session) {
|
|
1663
|
+
const label = session.name || session.id;
|
|
1664
|
+
if (!window.confirm(`Delete session "${label}"?\nThis cannot be undone.`)) return;
|
|
1665
|
+
this.send({ type: 'delete_session', path: session.path });
|
|
1666
|
+
// Refresh the list after delete (errors surface via toast/error message)
|
|
1667
|
+
setTimeout(() => this.send({ type: 'get_sessions' }), 300);
|
|
1639
1668
|
}
|
|
1640
1669
|
|
|
1641
1670
|
// ------------------------------------------------------------------
|
package/dist/static/style.css
CHANGED
|
@@ -710,6 +710,29 @@ body {
|
|
|
710
710
|
font-size: 13px;
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
+
/* Resume picker items: delete icon pushed to the right, away from the path */
|
|
714
|
+
.modal-item.resume-item {
|
|
715
|
+
display: flex;
|
|
716
|
+
align-items: baseline;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.modal-item.resume-item .resume-delete {
|
|
720
|
+
margin-left: auto;
|
|
721
|
+
padding-left: 16px;
|
|
722
|
+
cursor: pointer;
|
|
723
|
+
opacity: 0.6;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.modal-item.resume-item .resume-delete:hover {
|
|
727
|
+
opacity: 1;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.modal-item.resume-item .modal-item-desc {
|
|
731
|
+
overflow: hidden;
|
|
732
|
+
text-overflow: ellipsis;
|
|
733
|
+
white-space: nowrap;
|
|
734
|
+
}
|
|
735
|
+
|
|
713
736
|
.modal-item:hover {
|
|
714
737
|
background: var(--accent-hover);
|
|
715
738
|
}
|