@sigrun/tui 0.11.4 → 0.11.5
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/CHANGELOG.md +4 -0
- package/dist/ui/app.js +43 -0
- package/dist/ui/conversation-pane.d.ts +4 -0
- package/dist/ui/conversation-pane.js +19 -0
- package/dist/ui/sidebar.d.ts +10 -3
- package/dist/ui/sidebar.js +17 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
package/dist/ui/app.js
CHANGED
|
@@ -166,6 +166,12 @@ export class TuiApp {
|
|
|
166
166
|
* nothing and swallow keys). Exported for the launcher's mount. */
|
|
167
167
|
mainContent;
|
|
168
168
|
#activeId;
|
|
169
|
+
/** Deleted-conversations Task 4: whether the ACTIVE conversation's
|
|
170
|
+
* RAW list row carries deleted:true. Re-checked on every sidebar
|
|
171
|
+
* refresh (sidebar.rows drops deleted rows, so the pane reads the
|
|
172
|
+
* raw listConversations() response via the sidebar). Drives the
|
|
173
|
+
* pane's dim line and the composer gate. */
|
|
174
|
+
#activeDeleted = false;
|
|
169
175
|
#unwatch;
|
|
170
176
|
/** Monotonic watch generation: a stale watch's frames must never land. */
|
|
171
177
|
#watchGen = 0;
|
|
@@ -564,6 +570,15 @@ export class TuiApp {
|
|
|
564
570
|
*/
|
|
565
571
|
async refresh() {
|
|
566
572
|
await this.sidebar.refresh();
|
|
573
|
+
// Deleted-conversations Task 4: the deleted state comes from the
|
|
574
|
+
// RAW list response (the sidebar drops deleted rows from its
|
|
575
|
+
// display rows but keeps the raw list). Re-checked on EVERY
|
|
576
|
+
// sidebar refresh, so a /delete, /restore, or another client's
|
|
577
|
+
// change lands on the next poll.
|
|
578
|
+
this.#activeDeleted =
|
|
579
|
+
this.#activeId !== undefined &&
|
|
580
|
+
this.sidebar.rawConversations.find((row) => row.id === this.#activeId)?.deleted === true;
|
|
581
|
+
this.pane.setDeleted(this.#activeDeleted);
|
|
567
582
|
// Notices Task 8: the count rides the boot refresh too (quiet —
|
|
568
583
|
// the chrome keeps its last list on a fetch failure).
|
|
569
584
|
void this.notices.refresh().catch(() => { });
|
|
@@ -595,6 +610,13 @@ export class TuiApp {
|
|
|
595
610
|
// Busy state is per-frame: unknown for the new conversation until
|
|
596
611
|
// its first frame arrives.
|
|
597
612
|
this.#busy = false;
|
|
613
|
+
// Deleted-conversations Task 4: the swap re-checks the deleted
|
|
614
|
+
// state from the raw rows the last sidebar refresh left (a
|
|
615
|
+
// refresh may be mid-flight; the next one re-checks again).
|
|
616
|
+
this.#activeDeleted =
|
|
617
|
+
this.sidebar.rawConversations.find((row) => row.id === id)
|
|
618
|
+
?.deleted === true;
|
|
619
|
+
this.pane.setDeleted(this.#activeDeleted);
|
|
598
620
|
this.sidebar.setActive(id);
|
|
599
621
|
// Notices are per-conversation context (fix wave F4): an error
|
|
600
622
|
// from the OLD conversation must never render in the new one.
|
|
@@ -832,6 +854,13 @@ export class TuiApp {
|
|
|
832
854
|
const id = this.#activeId;
|
|
833
855
|
if (id === undefined)
|
|
834
856
|
return;
|
|
857
|
+
// Deleted-conversations Task 4: a deleted conversation is
|
|
858
|
+
// read-only — the server 409s any submission; the client keeps
|
|
859
|
+
// the draft and says so instead of firing the doomed POST.
|
|
860
|
+
if (this.#activeDeleted) {
|
|
861
|
+
this.pane.showError("this conversation is deleted · /restore to restore it");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
835
864
|
this.editor.setText("");
|
|
836
865
|
const whenBusy = this.#busy ? "steer" : undefined;
|
|
837
866
|
this.#client.submit(id, text, whenBusy).catch((error) => {
|
|
@@ -926,6 +955,20 @@ export class TuiApp {
|
|
|
926
955
|
await this.sidebar.refresh();
|
|
927
956
|
return;
|
|
928
957
|
}
|
|
958
|
+
case "delete":
|
|
959
|
+
case "restore": {
|
|
960
|
+
const id = this.#activeId;
|
|
961
|
+
if (id === undefined) {
|
|
962
|
+
this.pane.showError(`/${parsed.name} needs an active conversation`);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
await this.#client.patchMeta(id, { deleted: parsed.name === "delete" });
|
|
966
|
+
// Re-read the RAW list row so the pane's dim line lands
|
|
967
|
+
// at once (the next poll would catch up, but the user
|
|
968
|
+
// just acted — same rule as the archive refresh above).
|
|
969
|
+
await this.refresh();
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
929
972
|
case "model":
|
|
930
973
|
case "thinking": {
|
|
931
974
|
const id = this.#activeId;
|
|
@@ -134,6 +134,10 @@ export declare class ConversationPane implements Component {
|
|
|
134
134
|
surfaceStateFor(entryId: number): SurfaceState | undefined;
|
|
135
135
|
/** Append an `[error]` line (a failed composer action's surface). */
|
|
136
136
|
showError(text: string): void;
|
|
137
|
+
/** Deleted-conversations Task 4: show (or clear) the dim deleted
|
|
138
|
+
* line. The app drives it from the raw list rows, never the view
|
|
139
|
+
* stream (which carries no meta). */
|
|
140
|
+
setDeleted(deleted: boolean): void;
|
|
137
141
|
/** Append a `[notice]` line (an outcome, e.g. an abort that settled). */
|
|
138
142
|
showNotice(text: string): void;
|
|
139
143
|
/**
|
|
@@ -169,6 +169,10 @@ export class ConversationPane {
|
|
|
169
169
|
/** The notices' mounted components, aligned with `#notices`. */
|
|
170
170
|
#noticeComponents = [];
|
|
171
171
|
#turns = [];
|
|
172
|
+
/** Deleted-conversations Task 4: the app sets this from the RAW list
|
|
173
|
+
* row (re-checked per sidebar refresh and on conversation swap);
|
|
174
|
+
* the dim line renders under the last turn until restored. */
|
|
175
|
+
#deleted = false;
|
|
172
176
|
/** The latest view's `run ended:` line (stream state — recomputed on
|
|
173
177
|
* every `showView`, unlike the composer-owned notices). */
|
|
174
178
|
#runFailureLine;
|
|
@@ -322,6 +326,15 @@ export class ConversationPane {
|
|
|
322
326
|
showError(text) {
|
|
323
327
|
this.#appendNotice({ kind: "error", text });
|
|
324
328
|
}
|
|
329
|
+
/** Deleted-conversations Task 4: show (or clear) the dim deleted
|
|
330
|
+
* line. The app drives it from the raw list rows, never the view
|
|
331
|
+
* stream (which carries no meta). */
|
|
332
|
+
setDeleted(deleted) {
|
|
333
|
+
if (deleted === this.#deleted)
|
|
334
|
+
return;
|
|
335
|
+
this.#deleted = deleted;
|
|
336
|
+
this.#rebuild();
|
|
337
|
+
}
|
|
325
338
|
/** Append a `[notice]` line (an outcome, e.g. an abort that settled). */
|
|
326
339
|
showNotice(text) {
|
|
327
340
|
this.#appendNotice({ kind: "notice", text });
|
|
@@ -433,6 +446,12 @@ export class ConversationPane {
|
|
|
433
446
|
if (this.#runFailureLine !== undefined) {
|
|
434
447
|
this.#container.addChild(new Text(this.#theme.dim(this.#runFailureLine), 0, 0));
|
|
435
448
|
}
|
|
449
|
+
// The deleted dim line (deleted-conversations Task 4) rides under
|
|
450
|
+
// the transcript, above the composer notices — verbatim copy from
|
|
451
|
+
// the plan's Global Constraints.
|
|
452
|
+
if (this.#deleted) {
|
|
453
|
+
this.#container.addChild(new Text(this.#theme.dim("this conversation is deleted · /restore to restore it"), 0, 0));
|
|
454
|
+
}
|
|
436
455
|
for (const notice of this.#notices) {
|
|
437
456
|
const component = noticeComponent(notice);
|
|
438
457
|
this.#noticeComponents.push(component);
|
package/dist/ui/sidebar.d.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* indent two spaces + `↳ `), names are single-line via B1's
|
|
5
5
|
* `displayName` + pi-tui's `truncateToWidth`, ages right-align via B1's
|
|
6
6
|
* `formatAge`, and the active row highlights accent-fg + bold. Archived
|
|
7
|
-
* conversations stay hidden (the tree's `includeArchived: false
|
|
8
|
-
*
|
|
7
|
+
* conversations stay hidden (the tree's `includeArchived: false`),
|
|
8
|
+
* deleted conversations stay hidden too (the tree's default deleted
|
|
9
|
+
* filter; {@link rawConversations} keeps the RAW list response so the
|
|
10
|
+
* app can still read the active conversation's deleted marker), and
|
|
11
|
+
* quiet job runs stay
|
|
9
12
|
* hidden (`includeJobRuns: false`; failed runs remain visible with their
|
|
10
13
|
* `displayName` prefix). `+ New conversation` is always the last row.
|
|
11
14
|
*
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
*/
|
|
25
28
|
import { type Component } from "@earendil-works/pi-tui";
|
|
26
29
|
import { type TuiTheme } from "../theme.js";
|
|
27
|
-
import type { SigrunClient } from "../api.js";
|
|
30
|
+
import type { ConversationSummary, SigrunClient } from "../api.js";
|
|
28
31
|
/** The sidebar selection: a conversation id, the `+ new` action, or an
|
|
29
32
|
* extension VIEW (UI-blocks U3 Task 5 — the app opens it in the main
|
|
30
33
|
* pane). */
|
|
@@ -64,6 +67,10 @@ export declare class Sidebar implements Component {
|
|
|
64
67
|
get rows(): readonly SidebarRow[];
|
|
65
68
|
/** The cursor's row index (0-based over {@link rows}). */
|
|
66
69
|
get cursor(): number;
|
|
70
|
+
/** The RAW list rows from the last refresh (deleted rows included).
|
|
71
|
+
* The app reads the active conversation's deleted marker here —
|
|
72
|
+
* `rows` above never carries deleted rows. */
|
|
73
|
+
get rawConversations(): readonly ConversationSummary[];
|
|
67
74
|
/**
|
|
68
75
|
* Re-fetch the listing and rebuild the rows from the tree (summary
|
|
69
76
|
* fields only — no per-row meta fetch). The cursor follows its
|
package/dist/ui/sidebar.js
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* indent two spaces + `↳ `), names are single-line via B1's
|
|
5
5
|
* `displayName` + pi-tui's `truncateToWidth`, ages right-align via B1's
|
|
6
6
|
* `formatAge`, and the active row highlights accent-fg + bold. Archived
|
|
7
|
-
* conversations stay hidden (the tree's `includeArchived: false
|
|
8
|
-
*
|
|
7
|
+
* conversations stay hidden (the tree's `includeArchived: false`),
|
|
8
|
+
* deleted conversations stay hidden too (the tree's default deleted
|
|
9
|
+
* filter; {@link rawConversations} keeps the RAW list response so the
|
|
10
|
+
* app can still read the active conversation's deleted marker), and
|
|
11
|
+
* quiet job runs stay
|
|
9
12
|
* hidden (`includeJobRuns: false`; failed runs remain visible with their
|
|
10
13
|
* `displayName` prefix). `+ New conversation` is always the last row.
|
|
11
14
|
*
|
|
@@ -36,6 +39,11 @@ export class Sidebar {
|
|
|
36
39
|
#rowComponents = [];
|
|
37
40
|
/** Conversation rows only (the action row is {@link #newRow}). */
|
|
38
41
|
#rows = [];
|
|
42
|
+
/** The RAW list response from the last refresh (deleted-conversations
|
|
43
|
+
* Task 4): the app's dim-line check reads it because the display
|
|
44
|
+
* rows below DROP deleted conversations (the tree's default filter).
|
|
45
|
+
* The listing is the same one request — no extra fetch. */
|
|
46
|
+
#rawConversations = [];
|
|
39
47
|
/** The extension VIEW rows (U3 Task 5): one per declared view surface,
|
|
40
48
|
* rendered under the VIEWS group header. */
|
|
41
49
|
#views = [];
|
|
@@ -71,6 +79,12 @@ export class Sidebar {
|
|
|
71
79
|
get cursor() {
|
|
72
80
|
return this.#cursor;
|
|
73
81
|
}
|
|
82
|
+
/** The RAW list rows from the last refresh (deleted rows included).
|
|
83
|
+
* The app reads the active conversation's deleted marker here —
|
|
84
|
+
* `rows` above never carries deleted rows. */
|
|
85
|
+
get rawConversations() {
|
|
86
|
+
return this.#rawConversations;
|
|
87
|
+
}
|
|
74
88
|
/**
|
|
75
89
|
* Re-fetch the listing and rebuild the rows from the tree (summary
|
|
76
90
|
* fields only — no per-row meta fetch). The cursor follows its
|
|
@@ -78,6 +92,7 @@ export class Sidebar {
|
|
|
78
92
|
*/
|
|
79
93
|
async refresh() {
|
|
80
94
|
const list = await this.#client.listConversations();
|
|
95
|
+
this.#rawConversations = list.conversations;
|
|
81
96
|
const previousId = this.#display[this.#cursor]?.id;
|
|
82
97
|
const tree = buildConversationTree(list.conversations, {
|
|
83
98
|
includeArchived: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigrun/tui",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"description": "sigrun terminal client (pi-tui based) over the
|
|
3
|
+
"version": "0.11.5",
|
|
4
|
+
"description": "sigrun terminal client (pi-tui based) over the §5 API — spawned by the sigrun CLI, or standalone against a server URL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sigrun-tui": "dist/launch.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@earendil-works/pi-tui": "^1.3.0",
|
|
24
|
-
"@sigrun/shared": "0.11.
|
|
24
|
+
"@sigrun/shared": "0.11.5"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"typescript": "^7.0.2",
|