pi-cc-extensions 0.8.37 → 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.
@@ -3171,7 +3171,12 @@ export default function (
3171
3171
  const tui = toolMouseTui;
3172
3172
  if (!tui) return;
3173
3173
  if (toolMouseFixedFeaturesEnabled) patchToolMouseInputCapture(tui);
3174
- patchToolMouseMotionAfterRender(tui);
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
+ }
3175
3180
  tui.requestRender?.(true);
3176
3181
  });
3177
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
- if (!wasInstalled && this.installed && !this.disposed) compositorInstallNotify?.();
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
- /** Active owner callback; fired from compositor.install after a fresh install succeeds. */
215
- let compositorInstallNotify: (() => void) | undefined;
216
- let compositorInstallNotifyOwner: object | undefined;
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 (compositorInstallNotifyOwner === owner) {
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
- compositorInstallNotify = () => {
315
- if (!active) return;
316
- if ((globalThis as any)[FIXED_EDITOR_OWNER]?.owner !== owner) return;
317
- notifyRebuild();
318
- };
319
- compositorInstallNotifyOwner = owner;
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
- (sum: number, block: any) =>
25
- sum + (block?.type === "text" && typeof block.text === "string" ? block.text.length : 0),
26
- 0,
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;
@@ -166,15 +169,12 @@ export default function (pi: ExtensionAPI): void {
166
169
 
167
170
  if (evt.type === "start") {
168
171
  resetResponseTracking(evt.partial);
169
- } else if (evt.type === "text_start") {
172
+ } else if (evt.type === "thinking_start" || evt.type === "text_start") {
170
173
  setTextBlockLength(evt.contentIndex, 0);
171
174
  updateProviderUsage(evt.partial);
172
- } else if (evt.type === "text_delta") {
173
- setTextBlockLength(
174
- evt.contentIndex,
175
- (responseTextBlockLengths[evt.contentIndex] ?? 0) +
176
- (typeof evt.delta === "string" ? evt.delta.length : 0),
177
- );
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);
178
178
  updateProviderUsage(evt.partial);
179
179
  } else if (evt.type === "text_end") {
180
180
  setTextBlockLength(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cc-extensions",
3
- "version": "0.8.37",
3
+ "version": "0.8.38",
4
4
  "description": "A Pi productivity suite with Claude Code-style UI, fixed editor, context inspection, and agent/session references",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -40,7 +40,7 @@
40
40
 
41
41
  "selectedBg": "#13233a",
42
42
  "userMessageBg": "surface",
43
- "userMessageText": "text",
43
+ "userMessageText": "",
44
44
  "customMessageBg": "surface",
45
45
  "customMessageText": "text",
46
46
  "customMessageLabel": "accent",