pi-cc-extensions 0.8.36 → 0.8.38
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.
|
@@ -1126,6 +1126,13 @@ function useFixedEditorFeatures(tui: any): boolean {
|
|
|
1126
1126
|
return toolMouseFixedFeaturesEnabled && isFixedEditorTui(tui);
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
|
+
/** Mouse hover/click affordances need ccstyle rendering AND the fixed editor. Without
|
|
1130
|
+
* the fixed editor, Pi's native TUI has no mouse handling, so any reporting mode leaves
|
|
1131
|
+
* the wheel in a dead zone — native mode must leave reporting off entirely. */
|
|
1132
|
+
function toolMouseInteractionActive(): boolean {
|
|
1133
|
+
return config.mode !== "off" && toolMouseFixedFeaturesEnabled;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1129
1136
|
function formatShortcut(shortcut: string): string {
|
|
1130
1137
|
return shortcut
|
|
1131
1138
|
.split("+")
|
|
@@ -1249,7 +1256,12 @@ function hideScrollButton(tui: any): void {
|
|
|
1249
1256
|
}
|
|
1250
1257
|
|
|
1251
1258
|
function scheduleScrollButtonSync(tui: any, data: string): void {
|
|
1252
|
-
if (
|
|
1259
|
+
if (
|
|
1260
|
+
!useFixedEditorFeatures(tui) ||
|
|
1261
|
+
!toolMouseInteractionActive() ||
|
|
1262
|
+
!isScrollNavigationInput(data) ||
|
|
1263
|
+
scrollButtonSyncScheduled
|
|
1264
|
+
)
|
|
1253
1265
|
return;
|
|
1254
1266
|
scrollButtonSyncScheduled = true;
|
|
1255
1267
|
const previousLines = tui.previousLines;
|
|
@@ -1278,7 +1290,7 @@ function scheduleScrollButtonSync(tui: any, data: string): void {
|
|
|
1278
1290
|
}
|
|
1279
1291
|
|
|
1280
1292
|
function updateScrollButtonFromInput(tui: any, data: string): void {
|
|
1281
|
-
if (!useFixedEditorFeatures(tui)) return;
|
|
1293
|
+
if (!useFixedEditorFeatures(tui) || !toolMouseInteractionActive()) return;
|
|
1282
1294
|
if (matchesKey(data, "enter") || matchesKey(data, "return")) hideScrollButton(tui);
|
|
1283
1295
|
}
|
|
1284
1296
|
|
|
@@ -1521,7 +1533,7 @@ function patchToolMouseInputCapture(tui: any): void {
|
|
|
1521
1533
|
)
|
|
1522
1534
|
return;
|
|
1523
1535
|
const packets = parseSgrMousePackets(data);
|
|
1524
|
-
if (packets) {
|
|
1536
|
+
if (packets && toolMouseInteractionActive()) {
|
|
1525
1537
|
for (const packet of packets) {
|
|
1526
1538
|
// Fixed-editor owners may consume motion before extension listeners run,
|
|
1527
1539
|
// so hover must be handled at the root input boundary too.
|
|
@@ -1810,7 +1822,8 @@ function patchToolMouseMotionAfterRender(tui: any): void {
|
|
|
1810
1822
|
this.previousLines = extracted.lines;
|
|
1811
1823
|
placements = extracted.placements;
|
|
1812
1824
|
}
|
|
1813
|
-
if (!useFixedEditorFeatures(this)
|
|
1825
|
+
if (!useFixedEditorFeatures(this) && toolMouseInteractionActive())
|
|
1826
|
+
toolMouseRawWrite?.(TOOL_MOUSE_MOTION_ENABLE);
|
|
1814
1827
|
return result;
|
|
1815
1828
|
} finally {
|
|
1816
1829
|
activeIoViewFrame = previousFrame;
|
|
@@ -1833,7 +1846,8 @@ function patchToolMouseMotionAfterRender(tui: any): void {
|
|
|
1833
1846
|
toolMouseRenderPatchOriginal = original;
|
|
1834
1847
|
toolMouseRenderPatchWrapper = wrapper;
|
|
1835
1848
|
toolMouseRenderPatchState = patchState;
|
|
1836
|
-
if (!useFixedEditorFeatures(tui)
|
|
1849
|
+
if (!useFixedEditorFeatures(tui) && toolMouseInteractionActive())
|
|
1850
|
+
toolMouseRawWrite?.(TOOL_MOUSE_MOTION_ENABLE);
|
|
1837
1851
|
}
|
|
1838
1852
|
|
|
1839
1853
|
function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
@@ -1857,6 +1871,9 @@ function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
|
1857
1871
|
return { consume: true };
|
|
1858
1872
|
}
|
|
1859
1873
|
}
|
|
1874
|
+
// Off mode restores native input: wheel keeps scrolling through Pi's normal
|
|
1875
|
+
// dispatcher, while hover/click affordances are entirely inactive.
|
|
1876
|
+
if (!toolMouseInteractionActive()) return undefined;
|
|
1860
1877
|
const packets = parseSgrMousePackets(data);
|
|
1861
1878
|
if (!packets) {
|
|
1862
1879
|
scheduleScrollButtonSync(toolMouseTui, data);
|
|
@@ -1892,7 +1909,7 @@ function teardownToolMouseInteraction(nextFixedEditorFeatures = false): void {
|
|
|
1892
1909
|
setHoveredToolGroup(null);
|
|
1893
1910
|
setHoveredToolIo(null, null);
|
|
1894
1911
|
try {
|
|
1895
|
-
if (!
|
|
1912
|
+
if (!nextFixedEditorFeatures) {
|
|
1896
1913
|
toolMouseTui?.terminal?.write?.(TOOL_MOUSE_DISABLE);
|
|
1897
1914
|
}
|
|
1898
1915
|
} catch {
|
|
@@ -1932,13 +1949,17 @@ export function installToolMouseInteraction(
|
|
|
1932
1949
|
toolMouseFixedFeaturesEnabled = fixedEditorFeatures;
|
|
1933
1950
|
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any, theme: any) => {
|
|
1934
1951
|
toolMouseTui = tui;
|
|
1935
|
-
|
|
1936
|
-
//
|
|
1937
|
-
if (
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
//
|
|
1941
|
-
|
|
1952
|
+
// Root input capture follows the live compositor; onRebuild re-applies it
|
|
1953
|
+
// once the fixed-editor compositor actually owns the terminal.
|
|
1954
|
+
if (useFixedEditorFeatures(tui)) patchToolMouseInputCapture(tui);
|
|
1955
|
+
// Wrap doRender unconditionally: the fixed-editor rebuild replaces the
|
|
1956
|
+
// chain (setBeforeFixedEditorStart restores it first) and onRebuild
|
|
1957
|
+
// re-applies this wrapper once the compositor owns the terminal.
|
|
1958
|
+
patchToolMouseMotionAfterRender(tui);
|
|
1959
|
+
// The fixed-editor compositor owns its mouse mode; native Pi gets motion only
|
|
1960
|
+
// while mouse interaction is active (rendering mode on + fixed editor).
|
|
1961
|
+
if (!fixedEditorFeatures && toolMouseInteractionActive())
|
|
1962
|
+
tui?.terminal?.write?.(TOOL_MOUSE_MOTION_ENABLE);
|
|
1942
1963
|
const widget = {
|
|
1943
1964
|
render: (width: number) => renderScrollButton(width, theme),
|
|
1944
1965
|
invalidate() {},
|
|
@@ -1998,6 +2019,21 @@ function applyStyleMode(
|
|
|
1998
2019
|
): void {
|
|
1999
2020
|
config.mode = mode;
|
|
2000
2021
|
saveConfig();
|
|
2022
|
+
if (mode === "off") {
|
|
2023
|
+
// Native rendering mode: drop hover/click state and fully disable mouse
|
|
2024
|
+
// reporting so the terminal restores its default scrollback wheel scrolling.
|
|
2025
|
+
// (The fixed-editor compositor owns its own reporting when enabled.)
|
|
2026
|
+
hoveredToolCallId = null;
|
|
2027
|
+
setHoveredToolGroup(null);
|
|
2028
|
+
setHoveredToolIo(null, null);
|
|
2029
|
+
scrollButtonVisible = false;
|
|
2030
|
+
scrollButtonHovered = false;
|
|
2031
|
+
pendingScrollMessages = 0;
|
|
2032
|
+
assistantMessageActive = false;
|
|
2033
|
+
if (toolMouseTui && !useFixedEditorFeatures(toolMouseTui)) {
|
|
2034
|
+
toolMouseTui.terminal?.write?.(TOOL_MOUSE_DISABLE);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2001
2037
|
refreshCurrentTranscript(compactStyle, ctx, toolGrouping);
|
|
2002
2038
|
ctx.ui.notify(`Claude Code style: ${mode}`, "info");
|
|
2003
2039
|
}
|
|
@@ -2015,7 +2051,7 @@ function modeSettingDescription(mode: CompactStyleMode): string {
|
|
|
2015
2051
|
function fixedEditorSettingDescription(enabled: boolean): string {
|
|
2016
2052
|
return enabled
|
|
2017
2053
|
? "Pinned editor via @tifan/pi-fixed-editor. Mouse capture, 5-row wheel, tool clicks, back-to-bottom button, message count, and Ctrl+End."
|
|
2018
|
-
: "Native scrolling editor;
|
|
2054
|
+
: "Native scrolling editor; mouse hover/click and wheel reporting off. Ctrl+End remains enabled.";
|
|
2019
2055
|
}
|
|
2020
2056
|
|
|
2021
2057
|
function excludeRenderersDescription(names: readonly string[]): string {
|
|
@@ -3135,7 +3171,12 @@ export default function (
|
|
|
3135
3171
|
const tui = toolMouseTui;
|
|
3136
3172
|
if (!tui) return;
|
|
3137
3173
|
if (toolMouseFixedFeaturesEnabled) patchToolMouseInputCapture(tui);
|
|
3138
|
-
|
|
3174
|
+
// compositor.install captured the current chain as originalDoRender, so an
|
|
3175
|
+
// already-installed wrapper keeps receiving calls through it — re-wrapping
|
|
3176
|
+
// only when no wrapper is active avoids stacking one per rebuild/reload.
|
|
3177
|
+
if (!(toolMouseRenderPatchTui === tui && toolMouseRenderPatchState?.active)) {
|
|
3178
|
+
patchToolMouseMotionAfterRender(tui);
|
|
3179
|
+
}
|
|
3139
3180
|
tui.requestRender?.(true);
|
|
3140
3181
|
});
|
|
3141
3182
|
const writeExecutionMetadata = installWriteOverride(pi, new WriteExecutionMetadataStore());
|
|
@@ -170,7 +170,10 @@ export function installFixedEditorImePatch(compositorClass: {
|
|
|
170
170
|
const wasInstalled = Boolean(this.installed);
|
|
171
171
|
const result = Reflect.apply(originalInstall, this, args);
|
|
172
172
|
// Dependents re-wrap doRender only after the compositor actually owns it.
|
|
173
|
-
|
|
173
|
+
// Read the global each time: the patch survives /reload on the shared
|
|
174
|
+
// prototype while the owner callback belongs to the live extension module.
|
|
175
|
+
if (!wasInstalled && this.installed && !this.disposed)
|
|
176
|
+
getCompositorInstallNotify()?.callback();
|
|
174
177
|
return result;
|
|
175
178
|
};
|
|
176
179
|
prototype[MOUSE_WRITE_PATCH] = true;
|
|
@@ -211,9 +214,22 @@ type FixedEditorOwner = {
|
|
|
211
214
|
const FIXED_EDITOR_OWNER = Symbol.for("pi.ccstyle.fixed-editor-owner");
|
|
212
215
|
const FIXED_EDITOR_FOOTER_HOOK = Symbol.for("pi.ccstyle.fixed-editor-footer-hook");
|
|
213
216
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
// /reload keeps the same TerminalSplitCompositor.prototype, so the install hook
|
|
218
|
+
// patched by the pre-reload module must read a cross-reload global instead of a
|
|
219
|
+
// module-local variable; otherwise notifyRebuild never fires after reload and
|
|
220
|
+
// dependents (ccstyle mouse input capture) stay unbound.
|
|
221
|
+
const COMPOSITOR_INSTALL_NOTIFY_KEY = Symbol.for("pi.ccstyle.compositor-install-notify");
|
|
222
|
+
|
|
223
|
+
type CompositorInstallNotify = { owner: object; callback: () => void };
|
|
224
|
+
function getCompositorInstallNotify(): CompositorInstallNotify | undefined {
|
|
225
|
+
return (globalThis as Record<PropertyKey, unknown>)[COMPOSITOR_INSTALL_NOTIFY_KEY] as
|
|
226
|
+
| CompositorInstallNotify
|
|
227
|
+
| undefined;
|
|
228
|
+
}
|
|
229
|
+
function setCompositorInstallNotify(value: CompositorInstallNotify | undefined): void {
|
|
230
|
+
if (value) (globalThis as Record<PropertyKey, unknown>)[COMPOSITOR_INSTALL_NOTIFY_KEY] = value;
|
|
231
|
+
else delete (globalThis as Record<PropertyKey, unknown>)[COMPOSITOR_INSTALL_NOTIFY_KEY];
|
|
232
|
+
}
|
|
217
233
|
/** Runs before fixed-editor start/probe so dependents can unwrap doRender before capture. */
|
|
218
234
|
let beforeFixedEditorStart: (() => void) | undefined;
|
|
219
235
|
|
|
@@ -289,10 +305,7 @@ export function installFixedEditor(
|
|
|
289
305
|
});
|
|
290
306
|
};
|
|
291
307
|
function clearInstallNotify() {
|
|
292
|
-
if (
|
|
293
|
-
compositorInstallNotify = undefined;
|
|
294
|
-
compositorInstallNotifyOwner = undefined;
|
|
295
|
-
}
|
|
308
|
+
if (getCompositorInstallNotify()?.owner === owner) setCompositorInstallNotify(undefined);
|
|
296
309
|
}
|
|
297
310
|
function deactivate() {
|
|
298
311
|
fixedEditorState.scrollButtonHitbox = null;
|
|
@@ -311,12 +324,14 @@ export function installFixedEditor(
|
|
|
311
324
|
if (previous?.owner !== owner) previous?.stop();
|
|
312
325
|
const currentSession = session;
|
|
313
326
|
// Register before start/probe so the first real compositor.install notifies us.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
327
|
+
setCompositorInstallNotify({
|
|
328
|
+
owner,
|
|
329
|
+
callback: () => {
|
|
330
|
+
if (!active) return;
|
|
331
|
+
if ((globalThis as any)[FIXED_EDITOR_OWNER]?.owner !== owner) return;
|
|
332
|
+
notifyRebuild();
|
|
333
|
+
},
|
|
334
|
+
});
|
|
320
335
|
// Unwrap outer doRender patches before compositor construction captures it.
|
|
321
336
|
beforeFixedEditorStart?.();
|
|
322
337
|
start?.(currentSession.event, currentSession.ctx);
|
|
@@ -20,11 +20,12 @@ function formatCount(value: number): string {
|
|
|
20
20
|
|
|
21
21
|
function estimateTextLength(message: any): number {
|
|
22
22
|
if (!Array.isArray(message?.content)) return 0;
|
|
23
|
-
return message.content.reduce(
|
|
24
|
-
(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
return message.content.reduce((sum: number, block: any) => {
|
|
24
|
+
if (block?.type === "text" && typeof block.text === "string") return sum + block.text.length;
|
|
25
|
+
if (block?.type === "thinking" && block.thinkingSignature?.body)
|
|
26
|
+
return sum + (block.thinkingSignature.body as string).length;
|
|
27
|
+
return sum;
|
|
28
|
+
}, 0);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function textBlockLengths(message: any): number[] {
|
|
@@ -34,6 +35,8 @@ function textBlockLengths(message: any): number[] {
|
|
|
34
35
|
const block = message.content[index];
|
|
35
36
|
if (block?.type === "text" && typeof block.text === "string") {
|
|
36
37
|
lengths[index] = block.text.length;
|
|
38
|
+
} else if (block?.type === "thinking" && block.thinkingSignature?.body) {
|
|
39
|
+
lengths[index] = (block.thinkingSignature.body as string).length;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
return lengths;
|
|
@@ -122,8 +125,11 @@ export default function (pi: ExtensionAPI): void {
|
|
|
122
125
|
if (!turnActive || refreshTimer) return;
|
|
123
126
|
refreshTimer = setTimeout(() => {
|
|
124
127
|
refreshTimer = null;
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
try {
|
|
129
|
+
syncWorkingMessage();
|
|
130
|
+
} finally {
|
|
131
|
+
scheduleRefreshTick();
|
|
132
|
+
}
|
|
127
133
|
}, REFRESH_INTERVAL_MS);
|
|
128
134
|
refreshTimer.unref?.();
|
|
129
135
|
}
|
|
@@ -163,15 +169,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
163
169
|
|
|
164
170
|
if (evt.type === "start") {
|
|
165
171
|
resetResponseTracking(evt.partial);
|
|
166
|
-
} else if (evt.type === "text_start") {
|
|
172
|
+
} else if (evt.type === "thinking_start" || evt.type === "text_start") {
|
|
167
173
|
setTextBlockLength(evt.contentIndex, 0);
|
|
168
174
|
updateProviderUsage(evt.partial);
|
|
169
|
-
} else if (evt.type === "text_delta") {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
(responseTextBlockLengths[evt.contentIndex] ?? 0) +
|
|
173
|
-
(typeof evt.delta === "string" ? evt.delta.length : 0),
|
|
174
|
-
);
|
|
175
|
+
} else if (evt.type === "thinking_delta" || evt.type === "text_delta") {
|
|
176
|
+
const add = typeof evt.delta === "string" ? evt.delta.length : 0;
|
|
177
|
+
setTextBlockLength(evt.contentIndex, (responseTextBlockLengths[evt.contentIndex] ?? 0) + add);
|
|
175
178
|
updateProviderUsage(evt.partial);
|
|
176
179
|
} else if (evt.type === "text_end") {
|
|
177
180
|
setTextBlockLength(
|
package/package.json
CHANGED