@sagmans/dsh-tui 0.4.0 → 0.5.1
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/LICENSE +1 -1
- package/README.md +270 -68
- package/lib/agent/present.js +1 -1
- package/lib/agent/present.js.map +1 -1
- package/lib/agent/prompt-history.d.ts.map +1 -1
- package/lib/agent/prompt-history.js +5 -3
- package/lib/agent/prompt-history.js.map +1 -1
- package/lib/agent/status.d.ts +1 -1
- package/lib/cards.d.ts +83 -13
- package/lib/cards.d.ts.map +1 -1
- package/lib/cards.js +96 -28
- package/lib/cards.js.map +1 -1
- package/lib/export.d.ts +3 -3
- package/lib/export.d.ts.map +1 -1
- package/lib/export.js +23 -13
- package/lib/export.js.map +1 -1
- package/lib/fold-cursor.d.ts +12 -5
- package/lib/fold-cursor.d.ts.map +1 -1
- package/lib/fold-cursor.js +16 -5
- package/lib/fold-cursor.js.map +1 -1
- package/lib/gates.d.ts.map +1 -1
- package/lib/gates.js +42 -9
- package/lib/gates.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +459 -72
- package/lib/index.js.map +1 -1
- package/lib/injection.d.ts +9 -0
- package/lib/injection.d.ts.map +1 -0
- package/lib/injection.js +128 -0
- package/lib/injection.js.map +1 -0
- package/lib/input/actions.d.ts +19 -2
- package/lib/input/actions.d.ts.map +1 -1
- package/lib/input/actions.js +57 -11
- package/lib/input/actions.js.map +1 -1
- package/lib/input/cancel.d.ts +55 -0
- package/lib/input/cancel.d.ts.map +1 -0
- package/lib/input/cancel.js +47 -0
- package/lib/input/cancel.js.map +1 -0
- package/lib/input/completion.d.ts +5 -2
- package/lib/input/completion.d.ts.map +1 -1
- package/lib/input/completion.js +79 -4
- package/lib/input/completion.js.map +1 -1
- package/lib/input/file-search.d.ts +105 -0
- package/lib/input/file-search.d.ts.map +1 -0
- package/lib/input/file-search.js +665 -0
- package/lib/input/file-search.js.map +1 -0
- package/lib/input/fuzzy.d.ts +42 -0
- package/lib/input/fuzzy.d.ts.map +1 -0
- package/lib/input/fuzzy.js +289 -0
- package/lib/input/fuzzy.js.map +1 -0
- package/lib/input/keymap.d.ts +3 -1
- package/lib/input/keymap.d.ts.map +1 -1
- package/lib/input/keymap.js +14 -3
- package/lib/input/keymap.js.map +1 -1
- package/lib/input/match.d.ts +12 -4
- package/lib/input/match.d.ts.map +1 -1
- package/lib/input/match.js +50 -51
- package/lib/input/match.js.map +1 -1
- package/lib/input/submission.d.ts +10 -0
- package/lib/input/submission.d.ts.map +1 -1
- package/lib/input/submission.js +7 -6
- package/lib/input/submission.js.map +1 -1
- package/lib/keys-command.d.ts +19 -3
- package/lib/keys-command.d.ts.map +1 -1
- package/lib/keys-command.js +34 -31
- package/lib/keys-command.js.map +1 -1
- package/lib/settings-notice.d.ts +5 -0
- package/lib/settings-notice.d.ts.map +1 -1
- package/lib/settings-notice.js +9 -7
- package/lib/settings-notice.js.map +1 -1
- package/lib/stash/paths.d.ts +8 -8
- package/lib/stash/paths.d.ts.map +1 -1
- package/lib/stash/paths.js +19 -18
- package/lib/stash/paths.js.map +1 -1
- package/lib/stash/schema.d.ts +11 -12
- package/lib/stash/schema.d.ts.map +1 -1
- package/lib/stash/schema.js +10 -29
- package/lib/stash/schema.js.map +1 -1
- package/lib/stash/store.js +10 -10
- package/lib/stash/store.js.map +1 -1
- package/lib/stash.d.ts +37 -6
- package/lib/stash.d.ts.map +1 -1
- package/lib/stash.js +93 -36
- package/lib/stash.js.map +1 -1
- package/lib/terminal/external-editor.d.ts +93 -0
- package/lib/terminal/external-editor.d.ts.map +1 -0
- package/lib/terminal/external-editor.js +263 -0
- package/lib/terminal/external-editor.js.map +1 -0
- package/lib/terminal/host-writes.d.ts +39 -0
- package/lib/terminal/host-writes.d.ts.map +1 -0
- package/lib/terminal/host-writes.js +136 -0
- package/lib/terminal/host-writes.js.map +1 -0
- package/lib/terminal/signals.d.ts +25 -0
- package/lib/terminal/signals.d.ts.map +1 -0
- package/lib/terminal/signals.js +39 -0
- package/lib/terminal/signals.js.map +1 -0
- package/lib/terminal/warning-screen.d.ts +19 -1
- package/lib/terminal/warning-screen.d.ts.map +1 -1
- package/lib/terminal/warning-screen.js +37 -1
- package/lib/terminal/warning-screen.js.map +1 -1
- package/lib/terminal-text.d.ts +72 -0
- package/lib/terminal-text.d.ts.map +1 -0
- package/lib/terminal-text.js +603 -0
- package/lib/terminal-text.js.map +1 -0
- package/lib/text.d.ts +45 -6
- package/lib/text.d.ts.map +1 -1
- package/lib/text.js +106 -46
- package/lib/text.js.map +1 -1
- package/lib/theme-command.d.ts +7 -4
- package/lib/theme-command.d.ts.map +1 -1
- package/lib/theme-command.js +32 -12
- package/lib/theme-command.js.map +1 -1
- package/lib/theme-files.d.ts +110 -0
- package/lib/theme-files.d.ts.map +1 -0
- package/lib/theme-files.js +352 -0
- package/lib/theme-files.js.map +1 -0
- package/lib/theme-schema.d.ts +121 -0
- package/lib/theme-schema.d.ts.map +1 -0
- package/lib/theme-schema.js +87 -0
- package/lib/theme-schema.js.map +1 -0
- package/lib/theme-settings.d.ts +43 -10
- package/lib/theme-settings.d.ts.map +1 -1
- package/lib/theme-settings.js +103 -43
- package/lib/theme-settings.js.map +1 -1
- package/lib/theme-tokens.d.ts +39 -7
- package/lib/theme-tokens.d.ts.map +1 -1
- package/lib/theme-tokens.js +96 -13
- package/lib/theme-tokens.js.map +1 -1
- package/lib/theme.d.ts +24 -2
- package/lib/theme.d.ts.map +1 -1
- package/lib/theme.js +49 -6
- package/lib/theme.js.map +1 -1
- package/lib/tool-display.d.ts +57 -0
- package/lib/tool-display.d.ts.map +1 -0
- package/lib/tool-display.js +51 -0
- package/lib/tool-display.js.map +1 -0
- package/lib/transcript.d.ts +28 -0
- package/lib/transcript.d.ts.map +1 -1
- package/lib/transcript.js +126 -37
- package/lib/transcript.js.map +1 -1
- package/lib/ui/diff.d.ts +49 -0
- package/lib/ui/diff.d.ts.map +1 -0
- package/lib/ui/diff.js +208 -0
- package/lib/ui/diff.js.map +1 -0
- package/lib/ui/dock.d.ts.map +1 -1
- package/lib/ui/dock.js +9 -6
- package/lib/ui/dock.js.map +1 -1
- package/lib/ui/editor.d.ts.map +1 -1
- package/lib/ui/editor.js +4 -2
- package/lib/ui/editor.js.map +1 -1
- package/lib/ui/frame.d.ts +16 -5
- package/lib/ui/frame.d.ts.map +1 -1
- package/lib/ui/frame.js +32 -13
- package/lib/ui/frame.js.map +1 -1
- package/lib/ui/keymap-picker.d.ts +16 -0
- package/lib/ui/keymap-picker.d.ts.map +1 -0
- package/lib/ui/keymap-picker.js +41 -0
- package/lib/ui/keymap-picker.js.map +1 -0
- package/lib/ui/layout.d.ts +33 -0
- package/lib/ui/layout.d.ts.map +1 -0
- package/lib/ui/layout.js +33 -0
- package/lib/ui/layout.js.map +1 -0
- package/lib/ui/markdown.d.ts +25 -3
- package/lib/ui/markdown.d.ts.map +1 -1
- package/lib/ui/markdown.js +12 -8
- package/lib/ui/markdown.js.map +1 -1
- package/lib/ui/picker-card.d.ts +42 -0
- package/lib/ui/picker-card.d.ts.map +1 -0
- package/lib/ui/picker-card.js +148 -0
- package/lib/ui/picker-card.js.map +1 -0
- package/lib/ui/picker.d.ts +40 -2
- package/lib/ui/picker.d.ts.map +1 -1
- package/lib/ui/picker.js +50 -6
- package/lib/ui/picker.js.map +1 -1
- package/lib/ui/prompt.d.ts +10 -2
- package/lib/ui/prompt.d.ts.map +1 -1
- package/lib/ui/prompt.js +21 -2
- package/lib/ui/prompt.js.map +1 -1
- package/lib/ui/rows.d.ts +8 -6
- package/lib/ui/rows.d.ts.map +1 -1
- package/lib/ui/rows.js +10 -18
- package/lib/ui/rows.js.map +1 -1
- package/lib/ui/stash-picker.d.ts +2 -2
- package/lib/ui/stash-picker.d.ts.map +1 -1
- package/lib/ui/stash-picker.js +4 -4
- package/lib/ui/stash-picker.js.map +1 -1
- package/lib/ui/status.d.ts.map +1 -1
- package/lib/ui/status.js +18 -16
- package/lib/ui/status.js.map +1 -1
- package/lib/ui/theme-picker.d.ts +18 -0
- package/lib/ui/theme-picker.d.ts.map +1 -0
- package/lib/ui/theme-picker.js +68 -0
- package/lib/ui/theme-picker.js.map +1 -0
- package/lib/ui/view.d.ts +146 -22
- package/lib/ui/view.d.ts.map +1 -1
- package/lib/ui/view.js +461 -154
- package/lib/ui/view.js.map +1 -1
- package/package.json +45 -11
- package/themes/deepseek-blue.yaml +183 -0
- package/themes/violet-orbit.yaml +178 -0
package/lib/stash/schema.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export declare const STASH_SCHEMA_VERSION =
|
|
1
|
+
export declare const STASH_SCHEMA_VERSION = 2;
|
|
2
2
|
/** A stash longer than this is refused rather than stored. */
|
|
3
3
|
export declare const MAX_STASH_ENTRY_BYTES = 1048576;
|
|
4
|
-
export declare function stripControlCharacters(text: string): string;
|
|
5
4
|
export declare function isSafeEntryId(value: string): boolean;
|
|
6
5
|
export declare function assertSafeEntryId(value: string): void;
|
|
7
6
|
/** Refuse one draft that no amount of storage capacity could justify holding. */
|
|
@@ -16,14 +15,14 @@ export interface StashEntry {
|
|
|
16
15
|
export interface StashFile {
|
|
17
16
|
readonly version: typeof STASH_SCHEMA_VERSION;
|
|
18
17
|
/**
|
|
19
|
-
* The exact
|
|
18
|
+
* The exact session this bank belongs to.
|
|
20
19
|
*
|
|
21
20
|
* The key alone cannot prove ownership: it is a flattened label that two
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* sessions could once have shared. The exact id is compared on every read, so
|
|
22
|
+
* a file that belongs to another session is quarantined rather than read or
|
|
23
|
+
* deleted as this one's.
|
|
25
24
|
*/
|
|
26
|
-
readonly
|
|
25
|
+
readonly sessionId: string;
|
|
27
26
|
readonly createdAt: number;
|
|
28
27
|
readonly updatedAt: number;
|
|
29
28
|
/** Newest first: `entries[0]` is `stash@{0}`. */
|
|
@@ -31,16 +30,16 @@ export interface StashFile {
|
|
|
31
30
|
}
|
|
32
31
|
export type Clock = () => number;
|
|
33
32
|
export declare const createNewId: () => string;
|
|
34
|
-
export declare function createEmptyStashFile(
|
|
33
|
+
export declare function createEmptyStashFile(sessionId: string, now: number): StashFile;
|
|
35
34
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
36
35
|
export declare function normalizeEntry(raw: unknown): StashEntry | undefined;
|
|
37
36
|
/**
|
|
38
37
|
* Read one stash file, or nothing when it is not this surface's current format.
|
|
39
38
|
*
|
|
40
|
-
* Strict on purpose: a duplicate id, a missing timestamp, or a foreign
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* Strict on purpose: a duplicate id, a missing timestamp, or a foreign session
|
|
40
|
+
* is corruption the store quarantines rather than a shape to repair, because a
|
|
41
|
+
* silently repaired file can resurrect entries the reader thought they dropped —
|
|
42
|
+
* or hand another session's drafts to this one.
|
|
44
43
|
*/
|
|
45
44
|
export declare function parseStashFile(raw: unknown): StashFile | undefined;
|
|
46
45
|
export interface ResolvedEntry {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/stash/schema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/stash/schema.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,8DAA8D;AAC9D,eAAO,MAAM,qBAAqB,UAAY,CAAA;AAQ9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEtD;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,oBAAoB,CAAA;IAC7C;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAA;CACxC;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,CAAA;AAEhC,eAAO,MAAM,WAAW,QAAO,MAAsB,CAAA;AAErD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAE9E;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAOnE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAclE;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,aAAa,GAAG,SAAS,CAc3B"}
|
package/lib/stash/schema.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// quarantined by the store. Keeping this module free of fs dependencies makes
|
|
6
6
|
// the schema fully unit-testable.
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
|
-
|
|
8
|
+
import { stripControlCharacters } from "../text.js";
|
|
9
|
+
export const STASH_SCHEMA_VERSION = 2;
|
|
9
10
|
/** A stash longer than this is refused rather than stored. */
|
|
10
11
|
export const MAX_STASH_ENTRY_BYTES = 1_048_576;
|
|
11
12
|
const ENTRY_ID_MAX_LENGTH = 128;
|
|
@@ -13,26 +14,6 @@ const ENTRY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
|
13
14
|
const NUMERIC_SELECTOR_PATTERN = /^\d+$/u;
|
|
14
15
|
const INVALID_ENTRY_ID_MESSAGE = 'invalid stash entry id';
|
|
15
16
|
const ENTRY_TOO_LARGE_MESSAGE = 'stashed draft is too large';
|
|
16
|
-
/**
|
|
17
|
-
* Control characters a draft can never legitimately need: everything below the
|
|
18
|
-
* printable range except the tab and line feed that lay text out, plus DEL and
|
|
19
|
-
* the C1 block.
|
|
20
|
-
*
|
|
21
|
-
* A draft is written straight into a terminal, so a sequence that reached the
|
|
22
|
-
* bank through a hand-edited or hostile file would be executed as a command —
|
|
23
|
-
* clearing the screen, or replacing the reader's clipboard over OSC 52 — instead
|
|
24
|
-
* of appearing as the text they asked for. Stripping at the storage boundary
|
|
25
|
-
* covers every path a draft can take back to the screen.
|
|
26
|
-
*
|
|
27
|
-
* The bidi overrides are stripped with them: they draw nothing, so they cannot
|
|
28
|
-
* be seen and cannot be removed by hand, and what they reorder is what the reader
|
|
29
|
-
* is about to run. A terminal that shapes right-to-left text still has the
|
|
30
|
-
* letters themselves.
|
|
31
|
-
*/
|
|
32
|
-
const DISALLOWED_CONTROL_CHARACTERS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/gu;
|
|
33
|
-
export function stripControlCharacters(text) {
|
|
34
|
-
return text.replace(DISALLOWED_CONTROL_CHARACTERS, '');
|
|
35
|
-
}
|
|
36
17
|
export function isSafeEntryId(value) {
|
|
37
18
|
return value.length <= ENTRY_ID_MAX_LENGTH && ENTRY_ID_PATTERN.test(value);
|
|
38
19
|
}
|
|
@@ -50,8 +31,8 @@ export function sanitizeStashText(text) {
|
|
|
50
31
|
return stripControlCharacters(text);
|
|
51
32
|
}
|
|
52
33
|
export const createNewId = () => randomUUID();
|
|
53
|
-
export function createEmptyStashFile(
|
|
54
|
-
return { version: STASH_SCHEMA_VERSION,
|
|
34
|
+
export function createEmptyStashFile(sessionId, now) {
|
|
35
|
+
return { version: STASH_SCHEMA_VERSION, sessionId, createdAt: now, updatedAt: now, entries: [] };
|
|
55
36
|
}
|
|
56
37
|
export function isRecord(value) {
|
|
57
38
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -74,15 +55,15 @@ export function normalizeEntry(raw) {
|
|
|
74
55
|
/**
|
|
75
56
|
* Read one stash file, or nothing when it is not this surface's current format.
|
|
76
57
|
*
|
|
77
|
-
* Strict on purpose: a duplicate id, a missing timestamp, or a foreign
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
58
|
+
* Strict on purpose: a duplicate id, a missing timestamp, or a foreign session
|
|
59
|
+
* is corruption the store quarantines rather than a shape to repair, because a
|
|
60
|
+
* silently repaired file can resurrect entries the reader thought they dropped —
|
|
61
|
+
* or hand another session's drafts to this one.
|
|
81
62
|
*/
|
|
82
63
|
export function parseStashFile(raw) {
|
|
83
64
|
if (!isRecord(raw) || raw.version !== STASH_SCHEMA_VERSION)
|
|
84
65
|
return undefined;
|
|
85
|
-
if (typeof raw.
|
|
66
|
+
if (typeof raw.sessionId !== 'string')
|
|
86
67
|
return undefined;
|
|
87
68
|
if (!isValidTimestamp(raw.createdAt) || !isValidTimestamp(raw.updatedAt))
|
|
88
69
|
return undefined;
|
|
@@ -97,7 +78,7 @@ export function parseStashFile(raw) {
|
|
|
97
78
|
ids.add(entry.id);
|
|
98
79
|
entries.push(entry);
|
|
99
80
|
}
|
|
100
|
-
return { version: STASH_SCHEMA_VERSION,
|
|
81
|
+
return { version: STASH_SCHEMA_VERSION, sessionId: raw.sessionId, createdAt: raw.createdAt, updatedAt: raw.updatedAt, entries };
|
|
101
82
|
}
|
|
102
83
|
/**
|
|
103
84
|
* Resolve a selector against newest-first entries.
|
package/lib/stash/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/stash/schema.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,iFAAiF;AACjF,0EAA0E;AAC1E,8EAA8E;AAC9E,kCAAkC;AAElC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/stash/schema.ts"],"names":[],"mappings":"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"}
|
package/lib/stash/store.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// StashStore: on-disk CRUD for stash entries, scoped to one
|
|
1
|
+
// StashStore: on-disk CRUD for stash entries, scoped to one session.
|
|
2
2
|
//
|
|
3
3
|
// Entries are stored newest-first (`entries[0]` is `stash@{0}`), mirroring git's
|
|
4
4
|
// index-0-is-tip convention. Every mutation re-reads the file inside an
|
|
@@ -19,7 +19,7 @@ import { assertSafeEntryId, assertSafeStashText, createEmptyStashFile, createNew
|
|
|
19
19
|
export const MAX_STASH_FILE_BYTES = 16_777_216;
|
|
20
20
|
const DUPLICATE_STASH_ID_MESSAGE = 'duplicate stash id';
|
|
21
21
|
const UNSUPPORTED_SCHEMA_GUIDANCE = 'Upgrade dsh-tui before using this stash, or move the stash file aside for safe recovery.';
|
|
22
|
-
const FILE_TOO_LARGE_MESSAGE = 'this
|
|
22
|
+
const FILE_TOO_LARGE_MESSAGE = 'this session has no room left for another draft; drop one first';
|
|
23
23
|
/**
|
|
24
24
|
* A bank that cannot be written because it would no longer be readable.
|
|
25
25
|
*
|
|
@@ -57,7 +57,7 @@ export class StashStore {
|
|
|
57
57
|
this.paths = paths;
|
|
58
58
|
this.now = now;
|
|
59
59
|
this.write = write;
|
|
60
|
-
this.file = loaded.kind === 'ready' ? loaded.file : createEmptyStashFile(paths.
|
|
60
|
+
this.file = loaded.kind === 'ready' ? loaded.file : createEmptyStashFile(paths.sessionId, now());
|
|
61
61
|
this.corruptRecovery =
|
|
62
62
|
loaded.kind === 'corrupt' && loaded.quarantinedTo !== undefined
|
|
63
63
|
? { path: loaded.quarantinedTo, syncFailed: loaded.quarantineSyncFailed }
|
|
@@ -142,7 +142,7 @@ export class StashStore {
|
|
|
142
142
|
if (removed === 0)
|
|
143
143
|
return { didPersist: false, result: 0 };
|
|
144
144
|
const next = {
|
|
145
|
-
...createEmptyStashFile(this.paths.
|
|
145
|
+
...createEmptyStashFile(this.paths.sessionId, this.now()),
|
|
146
146
|
updatedAt: this.now(),
|
|
147
147
|
entries: kept,
|
|
148
148
|
};
|
|
@@ -170,7 +170,7 @@ export class StashStore {
|
|
|
170
170
|
return { didPersist: true, result };
|
|
171
171
|
}
|
|
172
172
|
async reloadFresh() {
|
|
173
|
-
const loaded = await readCurrentStashFile(this.filePath, this.paths.
|
|
173
|
+
const loaded = await readCurrentStashFile(this.filePath, this.paths.sessionId, this.now());
|
|
174
174
|
if (loaded.kind === 'ready') {
|
|
175
175
|
this.file = loaded.file;
|
|
176
176
|
return;
|
|
@@ -179,7 +179,7 @@ export class StashStore {
|
|
|
179
179
|
throw new UnsupportedStashSchemaError(loaded.version);
|
|
180
180
|
// Corrupt input is quarantined and replaced in memory, so later writes
|
|
181
181
|
// cannot resurrect entries from the invalidated snapshot.
|
|
182
|
-
this.file = createEmptyStashFile(this.paths.
|
|
182
|
+
this.file = createEmptyStashFile(this.paths.sessionId, this.now());
|
|
183
183
|
this.corruptRecovery =
|
|
184
184
|
loaded.quarantinedTo === undefined
|
|
185
185
|
? undefined
|
|
@@ -187,12 +187,12 @@ export class StashStore {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
export async function loadStashStore(paths, now = Date.now, write = writeStashFile) {
|
|
190
|
-
const loaded = await withStashFileLock(paths.file, () => readCurrentStashFile(paths.file, paths.
|
|
190
|
+
const loaded = await withStashFileLock(paths.file, () => readCurrentStashFile(paths.file, paths.sessionId, now()));
|
|
191
191
|
if (loaded.kind === 'unsupported')
|
|
192
192
|
throw new UnsupportedStashSchemaError(loaded.version);
|
|
193
193
|
return new StashStore(paths, loaded, now, write);
|
|
194
194
|
}
|
|
195
|
-
async function readCurrentStashFile(filePath,
|
|
195
|
+
async function readCurrentStashFile(filePath, sessionId, now) {
|
|
196
196
|
let source;
|
|
197
197
|
try {
|
|
198
198
|
source = await readPrivateTextFile(filePath, 'stash file', MAX_STASH_FILE_BYTES);
|
|
@@ -201,7 +201,7 @@ async function readCurrentStashFile(filePath, cwd, now) {
|
|
|
201
201
|
// A missing bank is an empty one; an oversized or unreadable bank is a real
|
|
202
202
|
// failure the caller must report rather than silently start over from.
|
|
203
203
|
if (hasErrorCode(error, 'ENOENT'))
|
|
204
|
-
return { kind: 'ready', file: createEmptyStashFile(
|
|
204
|
+
return { kind: 'ready', file: createEmptyStashFile(sessionId, now) };
|
|
205
205
|
throw error;
|
|
206
206
|
}
|
|
207
207
|
let raw;
|
|
@@ -212,7 +212,7 @@ async function readCurrentStashFile(filePath, cwd, now) {
|
|
|
212
212
|
return await quarantineCorrupt(filePath, source.identity);
|
|
213
213
|
}
|
|
214
214
|
const parsed = parseStashFile(raw);
|
|
215
|
-
if (parsed !== undefined && parsed.
|
|
215
|
+
if (parsed !== undefined && parsed.sessionId === sessionId)
|
|
216
216
|
return { kind: 'ready', file: parsed };
|
|
217
217
|
if (isRecord(raw) &&
|
|
218
218
|
typeof raw.version === 'number' &&
|
package/lib/stash/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/stash/store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/stash/store.ts"],"names":[],"mappings":"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"}
|
package/lib/stash.d.ts
CHANGED
|
@@ -17,13 +17,22 @@ export interface StashHost {
|
|
|
17
17
|
editorIsAvailable(): boolean;
|
|
18
18
|
notice(message: string): void;
|
|
19
19
|
/** Ask the reader which entry to take, by id; undefined when they leave. */
|
|
20
|
-
pick(entries: readonly ResolvedEntry[],
|
|
20
|
+
pick(entries: readonly ResolvedEntry[], sessionLabel: string): Promise<string | undefined>;
|
|
21
21
|
/** Ask the reader to confirm clearing `count` drafts. */
|
|
22
22
|
confirm(count: number): Promise<boolean>;
|
|
23
23
|
render(): void;
|
|
24
24
|
}
|
|
25
25
|
export interface PromptStashOptions {
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The session the surface is on now.
|
|
28
|
+
*
|
|
29
|
+
* A command reads this once, when the reader asks for it, and holds that
|
|
30
|
+
* session for the whole operation: the surface can move on while a command
|
|
31
|
+
* waits in the queue, and a command that re-read the session afterwards would
|
|
32
|
+
* drop or clear the drafts of the session it moved to. `open` is the one
|
|
33
|
+
* caller that re-reads it, because following the surface is its whole job.
|
|
34
|
+
*/
|
|
35
|
+
readonly sessionId: () => string;
|
|
27
36
|
/** Override the storage root; tests keep it inside a scratch directory. */
|
|
28
37
|
readonly baseDir?: string | undefined;
|
|
29
38
|
readonly now?: (() => number) | undefined;
|
|
@@ -32,17 +41,23 @@ export interface PromptStashOptions {
|
|
|
32
41
|
export declare class PromptStash {
|
|
33
42
|
private readonly host;
|
|
34
43
|
private readonly options;
|
|
35
|
-
private readonly
|
|
44
|
+
private readonly baseDir;
|
|
36
45
|
private readonly now;
|
|
37
46
|
private readonly write;
|
|
38
47
|
private store;
|
|
48
|
+
/** The session `store` was read for, so a switch can be told from a re-read. */
|
|
49
|
+
private scope;
|
|
39
50
|
private count;
|
|
40
51
|
/** The queue a second command waits behind, so writes never interleave. */
|
|
41
52
|
private tail;
|
|
42
53
|
constructor(host: StashHost, options: PromptStashOptions);
|
|
43
|
-
/** How many drafts this
|
|
54
|
+
/** How many drafts this session holds, as last read. */
|
|
44
55
|
get entryCount(): number;
|
|
45
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Read the bank of the session in force, so the status count is real before a
|
|
58
|
+
* command and after the surface moves to another session. A read creates
|
|
59
|
+
* nothing on disk, so a session that never stashes leaves no file behind.
|
|
60
|
+
*/
|
|
46
61
|
open(): Promise<void>;
|
|
47
62
|
/**
|
|
48
63
|
* Persist the draft the reader is looking at, or the one named on the command
|
|
@@ -60,7 +75,7 @@ export declare class PromptStash {
|
|
|
60
75
|
/** Put a draft in an empty editor and remove it from the bank. */
|
|
61
76
|
pop(selector: string | undefined): Promise<void>;
|
|
62
77
|
/** Open the list and pop whatever the reader takes. */
|
|
63
|
-
list(
|
|
78
|
+
list(sessionLabel: string): Promise<void>;
|
|
64
79
|
/** Delete one draft without using it. */
|
|
65
80
|
drop(selector: string | undefined): Promise<void>;
|
|
66
81
|
/** Confirm, then delete every draft the confirmation named. */
|
|
@@ -82,8 +97,24 @@ export declare class PromptStash {
|
|
|
82
97
|
*/
|
|
83
98
|
private editorIsAvailable;
|
|
84
99
|
private missingMessage;
|
|
100
|
+
/**
|
|
101
|
+
* The store of one session, loaded on first use.
|
|
102
|
+
*
|
|
103
|
+
* The session is passed in rather than read here: a command must act on the
|
|
104
|
+
* session the reader was on when they asked for it, even when the surface
|
|
105
|
+
* moves on before the queue reaches that command.
|
|
106
|
+
*/
|
|
85
107
|
private ensure;
|
|
86
108
|
private run;
|
|
109
|
+
/** Whether the surface still shows the session a command was issued in. */
|
|
110
|
+
private isCurrentSession;
|
|
111
|
+
/**
|
|
112
|
+
* Stop a command whose session left the screen before the bar could be reached.
|
|
113
|
+
*
|
|
114
|
+
* The bank half has already run for the session that asked for it; writing its
|
|
115
|
+
* draft into whatever bar is on screen now would hand it to the wrong reader.
|
|
116
|
+
*/
|
|
117
|
+
private abandonIfMoved;
|
|
87
118
|
private enqueue;
|
|
88
119
|
/** Publish the bank's size and any quarantine the last read produced. */
|
|
89
120
|
private sync;
|
package/lib/stash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAuBzB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,aAAa,IAAI,MAAM,CAAA;IACvB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,8DAA8D;IAC9D,iBAAiB,IAAI,OAAO,CAAA;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,4EAA4E;IAC5E,IAAI,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1F,yDAAyD;IACzD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,MAAM,IAAI,IAAI,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,MAAM,CAAA;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CACzC;AASD,qBAAa,WAAW;IAYpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,KAAK,CAAwB;IACrC,gFAAgF;IAChF,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,KAAK,CAAI;IACjB,2EAA2E;IAC3E,OAAO,CAAC,IAAI,CAAmC;gBAG5B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,kBAAkB;IAO9C,wDAAwD;IACxD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrB;;;;;;;;;OASG;IACH,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C1C,0DAA0D;IAC1D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,kEAAkE;IAClE,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhD,uDAAuD;IACvD,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzC,yCAAyC;IACzC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBjD,+DAA+D;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BtB;;;;;OAKG;YACW,OAAO;IAqBrB,OAAO,CAAC,aAAa;IAOrB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;IAOtB;;;;;;OAMG;YACW,MAAM;IAQpB,OAAO,CAAC,GAAG;IAYX,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,OAAO;IAMf,yEAAyE;IACzE,OAAO,CAAC,IAAI;CAab"}
|
package/lib/stash.js
CHANGED
|
@@ -15,6 +15,7 @@ const NOTHING_TO_STASH_MESSAGE = 'nothing to stash';
|
|
|
15
15
|
const NO_DRAFTS_MESSAGE = 'no stashed drafts';
|
|
16
16
|
const CORRUPT_RECOVERY_MESSAGE = 'corrupt stash data was quarantined to';
|
|
17
17
|
const CORRUPT_RECOVERY_UNSYNCED_MESSAGE = ' (its directory could not be synced, so the copy may not survive a crash)';
|
|
18
|
+
const SESSION_CHANGED_MESSAGE = 'the session changed; the stash stayed with the session it belonged to';
|
|
18
19
|
const stashedMessage = (index) => `Stashed [${index}]`;
|
|
19
20
|
const appliedMessage = (index) => `Applied [${index}]`;
|
|
20
21
|
const poppedMessage = (index) => `Popped [${index}]`;
|
|
@@ -34,33 +35,41 @@ function describeFailure(error) {
|
|
|
34
35
|
export class PromptStash {
|
|
35
36
|
host;
|
|
36
37
|
options;
|
|
37
|
-
|
|
38
|
+
baseDir;
|
|
38
39
|
now;
|
|
39
40
|
write;
|
|
40
41
|
store;
|
|
42
|
+
/** The session `store` was read for, so a switch can be told from a re-read. */
|
|
43
|
+
scope;
|
|
41
44
|
count = 0;
|
|
42
45
|
/** The queue a second command waits behind, so writes never interleave. */
|
|
43
46
|
tail = Promise.resolve();
|
|
44
47
|
constructor(host, options) {
|
|
45
48
|
this.host = host;
|
|
46
49
|
this.options = options;
|
|
47
|
-
this.
|
|
50
|
+
this.baseDir = options.baseDir ?? stashBaseDir();
|
|
48
51
|
this.now = options.now;
|
|
49
52
|
this.write = options.write;
|
|
50
53
|
}
|
|
51
|
-
/** How many drafts this
|
|
54
|
+
/** How many drafts this session holds, as last read. */
|
|
52
55
|
get entryCount() {
|
|
53
56
|
return this.count;
|
|
54
57
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Read the bank of the session in force, so the status count is real before a
|
|
60
|
+
* command and after the surface moves to another session. A read creates
|
|
61
|
+
* nothing on disk, so a session that never stashes leaves no file behind.
|
|
62
|
+
*/
|
|
63
|
+
open() {
|
|
64
|
+
return this.enqueue(async () => {
|
|
65
|
+
try {
|
|
66
|
+
await this.ensure(this.options.sessionId());
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
this.host.notice(describeFailure(error));
|
|
70
|
+
}
|
|
71
|
+
this.sync();
|
|
72
|
+
});
|
|
64
73
|
}
|
|
65
74
|
/**
|
|
66
75
|
* Persist the draft the reader is looking at, or the one named on the command
|
|
@@ -73,16 +82,24 @@ export class PromptStash {
|
|
|
73
82
|
* chord, or a bare `/stash` — parks whatever the bar is holding.
|
|
74
83
|
*/
|
|
75
84
|
stashEditor(typed) {
|
|
85
|
+
const sessionId = this.options.sessionId();
|
|
76
86
|
const named = typed !== undefined && typed.trim() !== '';
|
|
77
87
|
// The draft goes back into the bar before anything can fail, including
|
|
78
88
|
// opening the bank: the command line it was typed on is already gone, so
|
|
79
89
|
// this is the only copy until a write lands.
|
|
80
|
-
|
|
90
|
+
const available = this.host.editorIsAvailable();
|
|
91
|
+
if (named && available)
|
|
81
92
|
this.host.setEditorText(typed);
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
// The bar is read now, not when the queue reaches this command: a session
|
|
94
|
+
// switch in between would otherwise park the next session's draft in this
|
|
95
|
+
// bank and clear it from the bar the reader is typing in.
|
|
96
|
+
const captured = available ? (named ? typed : this.host.getEditorText()) : undefined;
|
|
97
|
+
return this.run(sessionId, async (store) => {
|
|
98
|
+
if (captured === undefined) {
|
|
99
|
+
this.host.notice(EDITOR_BUSY_MESSAGE);
|
|
84
100
|
return;
|
|
85
|
-
|
|
101
|
+
}
|
|
102
|
+
const text = captured;
|
|
86
103
|
if (text.trim() === '') {
|
|
87
104
|
this.host.notice(NOTHING_TO_STASH_MESSAGE);
|
|
88
105
|
return;
|
|
@@ -101,17 +118,21 @@ export class PromptStash {
|
|
|
101
118
|
warning = committedMessage(error, stashedMessage(resolved.index));
|
|
102
119
|
}
|
|
103
120
|
// Only clear what was actually persisted, and only while the bar is still
|
|
104
|
-
// the reader's: a question can borrow
|
|
105
|
-
//
|
|
106
|
-
|
|
121
|
+
// the reader's and still this session's: a question can borrow the bar
|
|
122
|
+
// during the write, and a session switch moves the whole surface on.
|
|
123
|
+
const current = this.isCurrentSession(sessionId);
|
|
124
|
+
if (current && this.host.editorIsAvailable() && this.host.getEditorText() === text)
|
|
107
125
|
this.host.setEditorText('');
|
|
108
|
-
this.host.notice(warning ?? stashedMessage(resolved.index));
|
|
126
|
+
this.host.notice(current ? (warning ?? stashedMessage(resolved.index)) : SESSION_CHANGED_MESSAGE);
|
|
109
127
|
});
|
|
110
128
|
}
|
|
111
129
|
/** Put a draft in an empty editor without removing it. */
|
|
112
130
|
apply(selector) {
|
|
113
|
-
|
|
131
|
+
const sessionId = this.options.sessionId();
|
|
132
|
+
return this.run(sessionId, async (store) => {
|
|
114
133
|
await store.refresh();
|
|
134
|
+
if (this.abandonIfMoved(sessionId))
|
|
135
|
+
return;
|
|
115
136
|
if (!this.editorIsReady())
|
|
116
137
|
return;
|
|
117
138
|
const resolved = store.find(selector);
|
|
@@ -125,17 +146,21 @@ export class PromptStash {
|
|
|
125
146
|
}
|
|
126
147
|
/** Put a draft in an empty editor and remove it from the bank. */
|
|
127
148
|
pop(selector) {
|
|
128
|
-
|
|
149
|
+
const sessionId = this.options.sessionId();
|
|
150
|
+
return this.run(sessionId, async (store) => {
|
|
129
151
|
// Re-read before resolving so "newest" is newest on disk, not newest as of
|
|
130
152
|
// the last command; the removal re-reads again under the lock.
|
|
131
153
|
await store.refresh();
|
|
132
|
-
await this.popFrom(store, selector);
|
|
154
|
+
await this.popFrom(store, sessionId, selector);
|
|
133
155
|
});
|
|
134
156
|
}
|
|
135
157
|
/** Open the list and pop whatever the reader takes. */
|
|
136
|
-
list(
|
|
137
|
-
|
|
158
|
+
list(sessionLabel) {
|
|
159
|
+
const sessionId = this.options.sessionId();
|
|
160
|
+
return this.run(sessionId, async (store) => {
|
|
138
161
|
await store.refresh();
|
|
162
|
+
if (this.abandonIfMoved(sessionId))
|
|
163
|
+
return;
|
|
139
164
|
if (store.entryCount === 0) {
|
|
140
165
|
this.host.notice(NO_DRAFTS_MESSAGE);
|
|
141
166
|
return;
|
|
@@ -143,15 +168,16 @@ export class PromptStash {
|
|
|
143
168
|
// The list is handed each entry with the index the bank gives it, so the
|
|
144
169
|
// row a reader picks and the selector they could have typed agree.
|
|
145
170
|
const rows = store.entries.map((entry, index) => ({ entry, index }));
|
|
146
|
-
const picked = await this.host.pick(rows,
|
|
171
|
+
const picked = await this.host.pick(rows, sessionLabel);
|
|
147
172
|
if (picked === undefined)
|
|
148
173
|
return;
|
|
149
|
-
await this.popFrom(store, picked);
|
|
174
|
+
await this.popFrom(store, sessionId, picked);
|
|
150
175
|
});
|
|
151
176
|
}
|
|
152
177
|
/** Delete one draft without using it. */
|
|
153
178
|
drop(selector) {
|
|
154
|
-
|
|
179
|
+
const sessionId = this.options.sessionId();
|
|
180
|
+
return this.run(sessionId, async (store) => {
|
|
155
181
|
let resolved;
|
|
156
182
|
let warning;
|
|
157
183
|
try {
|
|
@@ -174,8 +200,11 @@ export class PromptStash {
|
|
|
174
200
|
}
|
|
175
201
|
/** Confirm, then delete every draft the confirmation named. */
|
|
176
202
|
clear() {
|
|
177
|
-
|
|
203
|
+
const sessionId = this.options.sessionId();
|
|
204
|
+
return this.run(sessionId, async (store) => {
|
|
178
205
|
await store.refresh();
|
|
206
|
+
if (this.abandonIfMoved(sessionId))
|
|
207
|
+
return;
|
|
179
208
|
const count = store.entryCount;
|
|
180
209
|
if (count === 0) {
|
|
181
210
|
this.host.notice(NO_DRAFTS_MESSAGE);
|
|
@@ -207,7 +236,9 @@ export class PromptStash {
|
|
|
207
236
|
* The editor is filled before the entry is removed, so a crash between the two
|
|
208
237
|
* leaves the draft in the bank rather than only in a terminal that is gone.
|
|
209
238
|
*/
|
|
210
|
-
async popFrom(store, selector) {
|
|
239
|
+
async popFrom(store, sessionId, selector) {
|
|
240
|
+
if (this.abandonIfMoved(sessionId))
|
|
241
|
+
return;
|
|
211
242
|
if (!this.editorIsReady())
|
|
212
243
|
return;
|
|
213
244
|
const resolved = store.find(selector);
|
|
@@ -255,17 +286,25 @@ export class PromptStash {
|
|
|
255
286
|
? NO_DRAFTS_MESSAGE
|
|
256
287
|
: selectorMissingMessage(trimmed);
|
|
257
288
|
}
|
|
258
|
-
|
|
259
|
-
|
|
289
|
+
/**
|
|
290
|
+
* The store of one session, loaded on first use.
|
|
291
|
+
*
|
|
292
|
+
* The session is passed in rather than read here: a command must act on the
|
|
293
|
+
* session the reader was on when they asked for it, even when the surface
|
|
294
|
+
* moves on before the queue reaches that command.
|
|
295
|
+
*/
|
|
296
|
+
async ensure(sessionId) {
|
|
297
|
+
if (this.store !== undefined && this.scope === sessionId)
|
|
260
298
|
return this.store;
|
|
261
|
-
const store = await loadStashStore(this.
|
|
299
|
+
const store = await loadStashStore(resolveStashPaths(sessionId, this.baseDir), this.now, this.write);
|
|
262
300
|
this.store = store;
|
|
301
|
+
this.scope = sessionId;
|
|
263
302
|
return store;
|
|
264
303
|
}
|
|
265
|
-
run(operation) {
|
|
304
|
+
run(sessionId, operation) {
|
|
266
305
|
return this.enqueue(async () => {
|
|
267
306
|
try {
|
|
268
|
-
await operation(await this.ensure());
|
|
307
|
+
await operation(await this.ensure(sessionId));
|
|
269
308
|
}
|
|
270
309
|
catch (error) {
|
|
271
310
|
this.host.notice(describeFailure(error));
|
|
@@ -275,6 +314,22 @@ export class PromptStash {
|
|
|
275
314
|
}
|
|
276
315
|
});
|
|
277
316
|
}
|
|
317
|
+
/** Whether the surface still shows the session a command was issued in. */
|
|
318
|
+
isCurrentSession(sessionId) {
|
|
319
|
+
return this.options.sessionId() === sessionId;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Stop a command whose session left the screen before the bar could be reached.
|
|
323
|
+
*
|
|
324
|
+
* The bank half has already run for the session that asked for it; writing its
|
|
325
|
+
* draft into whatever bar is on screen now would hand it to the wrong reader.
|
|
326
|
+
*/
|
|
327
|
+
abandonIfMoved(sessionId) {
|
|
328
|
+
if (this.isCurrentSession(sessionId))
|
|
329
|
+
return false;
|
|
330
|
+
this.host.notice(SESSION_CHANGED_MESSAGE);
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
278
333
|
enqueue(operation) {
|
|
279
334
|
const run = this.tail.then(operation, operation);
|
|
280
335
|
this.tail = run.catch(() => undefined);
|
|
@@ -283,7 +338,9 @@ export class PromptStash {
|
|
|
283
338
|
/** Publish the bank's size and any quarantine the last read produced. */
|
|
284
339
|
sync() {
|
|
285
340
|
if (this.store !== undefined) {
|
|
286
|
-
|
|
341
|
+
// A store read for a session the surface has left is not the count to
|
|
342
|
+
// draw; the open that follows the switch publishes the right one.
|
|
343
|
+
this.count = this.scope === this.options.sessionId() ? this.store.entryCount : 0;
|
|
287
344
|
const quarantine = this.store.takeQuarantine();
|
|
288
345
|
if (quarantine !== undefined) {
|
|
289
346
|
const durability = quarantine.syncFailed ? CORRUPT_RECOVERY_UNSYNCED_MESSAGE : '';
|