pi-double-esc 1.0.16 → 1.0.18
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/README.md +1 -1
- package/double-esc.ts +36 -5
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ MIT
|
|
|
104
104
|
|
|
105
105
|
## Pi 1.0 compatibility (1.0.16)
|
|
106
106
|
|
|
107
|
-
Tested against Pi **1.
|
|
107
|
+
Tested against Pi **1.2.0** with exact SDK development pins and wildcard host peers.
|
|
108
108
|
Editor composition, autocomplete Escape, timer cleanup and shutdown restoration remain intact.
|
|
109
109
|
|
|
110
110
|
Run `bun run test:host` for the offline real-host regression. Set `PI1_HOST_PACKAGE` to an installed Pi package directory and `PI1_HOST_ENTRY=bundle` to test its bundled CLI runtime.
|
package/double-esc.ts
CHANGED
|
@@ -134,24 +134,53 @@ function decorateDoubleEscapeEditor(
|
|
|
134
134
|
const DECORATED_FACTORY = Symbol("pi-double-esc.decoratedFactory");
|
|
135
135
|
|
|
136
136
|
type EditorFactory = NonNullable<ReturnType<ExtensionContext["ui"]["getEditorComponent"]>>;
|
|
137
|
-
|
|
137
|
+
// Shared with cooperating editor decorators: inactive factories are skipped
|
|
138
|
+
// when an outer owner later unwinds, regardless of shutdown order.
|
|
139
|
+
const EDITOR_FACTORY_PATCH = Symbol.for("pi.editor-factory.patch.v1");
|
|
140
|
+
type DecoratedEditorFactory = EditorFactory & {
|
|
141
|
+
[DECORATED_FACTORY]?: true;
|
|
142
|
+
[EDITOR_FACTORY_PATCH]?: { active: boolean; original?: EditorFactory };
|
|
143
|
+
};
|
|
144
|
+
function unwrapEditorFactory(factory: EditorFactory | undefined): EditorFactory | undefined {
|
|
145
|
+
while (factory) {
|
|
146
|
+
const state = (factory as DecoratedEditorFactory)[EDITOR_FACTORY_PATCH];
|
|
147
|
+
if (!state || state.active) return factory;
|
|
148
|
+
factory = state.original;
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function hasDoubleEscape(factory: EditorFactory | undefined): boolean {
|
|
154
|
+
while (factory) {
|
|
155
|
+
const wrapped = factory as DecoratedEditorFactory;
|
|
156
|
+
const state = wrapped[EDITOR_FACTORY_PATCH];
|
|
157
|
+
if (state?.active !== false && wrapped[DECORATED_FACTORY]) return true;
|
|
158
|
+
factory = state?.original;
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
138
162
|
|
|
139
163
|
export default function (pi: ExtensionAPI) {
|
|
140
164
|
const cleanups = new Set<() => void>();
|
|
141
165
|
let installed: DecoratedEditorFactory | undefined;
|
|
142
166
|
let previous: EditorFactory | undefined;
|
|
167
|
+
const factoryStates: { active: boolean; original?: EditorFactory }[] = [];
|
|
143
168
|
pi.on("session_start", (_event, ctx) => {
|
|
144
169
|
if (ctx.mode !== "tui") return;
|
|
145
|
-
const previousEditorFactory = ctx.ui.getEditorComponent() as DecoratedEditorFactory | undefined;
|
|
146
|
-
if (previousEditorFactory
|
|
170
|
+
const previousEditorFactory = unwrapEditorFactory(ctx.ui.getEditorComponent()) as DecoratedEditorFactory | undefined;
|
|
171
|
+
if (hasDoubleEscape(previousEditorFactory)) return;
|
|
147
172
|
|
|
173
|
+
const state = { active: true, original: previousEditorFactory };
|
|
148
174
|
const editorFactory: DecoratedEditorFactory = (tui, editorTheme, keybindings) => {
|
|
149
175
|
const editor =
|
|
150
|
-
previousEditorFactory?.(tui, editorTheme, keybindings) ??
|
|
176
|
+
unwrapEditorFactory(previousEditorFactory)?.(tui, editorTheme, keybindings) ??
|
|
151
177
|
new CustomEditor(tui, editorTheme, keybindings);
|
|
178
|
+
if (!state.active) return editor;
|
|
152
179
|
return decorateDoubleEscapeEditor(editor, tui, ctx.ui.theme, () => ctx.isIdle(), (cleanup) => cleanups.add(cleanup));
|
|
153
180
|
};
|
|
154
181
|
editorFactory[DECORATED_FACTORY] = true;
|
|
182
|
+
editorFactory[EDITOR_FACTORY_PATCH] = state;
|
|
183
|
+
factoryStates.push(state);
|
|
155
184
|
|
|
156
185
|
previous = previousEditorFactory;
|
|
157
186
|
installed = editorFactory;
|
|
@@ -160,7 +189,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
160
189
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
161
190
|
for (const cleanup of cleanups) cleanup();
|
|
162
191
|
cleanups.clear();
|
|
163
|
-
|
|
192
|
+
for (const state of factoryStates) state.active = false;
|
|
193
|
+
factoryStates.length = 0;
|
|
194
|
+
if (installed && ctx.ui.getEditorComponent() === installed) ctx.ui.setEditorComponent(unwrapEditorFactory(previous));
|
|
164
195
|
installed = undefined;
|
|
165
196
|
previous = undefined;
|
|
166
197
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-double-esc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Prevent accidental Escape from aborting the LLM — requires double-press to interrupt while streaming",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Tom X Nguyen",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"lint:dead": "knip --no-gitignore"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
42
|
-
"@earendil-works/pi-tui": "1.
|
|
41
|
+
"@earendil-works/pi-coding-agent": "1.2.0",
|
|
42
|
+
"@earendil-works/pi-tui": "1.2.0",
|
|
43
43
|
"@types/node": "25.9.1",
|
|
44
44
|
"@vitest/coverage-v8": "4.1.7",
|
|
45
45
|
"knip": "6.14.1",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
54
|
"overrides": {
|
|
55
|
-
"brace-expansion": "5.0.
|
|
55
|
+
"brace-expansion": "5.0.12"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@earendil-works/pi-coding-agent": "*",
|