pi-zentui 0.29.0 → 0.29.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.
|
@@ -81,6 +81,7 @@ import {
|
|
|
81
81
|
renderTurnSummaryEntry,
|
|
82
82
|
TURN_SUMMARY_ENTRY_TYPE,
|
|
83
83
|
} from "./interaction-summary";
|
|
84
|
+
import { LayeredEditorConsumer } from "./layered-editor";
|
|
84
85
|
import { LiveContextController } from "./live-context";
|
|
85
86
|
import { readPackageVersionResult } from "./package-version";
|
|
86
87
|
import { getComponentPreset } from "./presets";
|
|
@@ -277,7 +278,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
277
278
|
let stopSessionTimer: () => void = () => {};
|
|
278
279
|
let stopMinimalistDurationUpdates: () => void = () => {};
|
|
279
280
|
let minimalistDurationUpdatesActive = false;
|
|
280
|
-
let
|
|
281
|
+
let minimalistDecorationEditor: PolishedEditor | WrappedPolishedEditor | undefined;
|
|
281
282
|
let customVariableSessionReady = false;
|
|
282
283
|
let sessionTimerRequirements = "";
|
|
283
284
|
let lastDurationLabel = "";
|
|
@@ -324,14 +325,41 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
324
325
|
}
|
|
325
326
|
};
|
|
326
327
|
|
|
328
|
+
const layeredEditor = new LayeredEditorConsumer<
|
|
329
|
+
EditorFactory,
|
|
330
|
+
PolishedEditor | WrappedPolishedEditor
|
|
331
|
+
>((generation) => sessionLifecycle.isCurrent(generation));
|
|
332
|
+
const currentEditorFactory = (): EditorFactory | undefined => {
|
|
333
|
+
try {
|
|
334
|
+
return activeTuiContext?.ui.getEditorComponent();
|
|
335
|
+
} catch {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
// Layered visibility is read-only recognition; installation ownership above stays exclusive.
|
|
340
|
+
// Data, demand, and timer consumers read the visible editor through these two helpers; anything
|
|
341
|
+
// that installs, restores, or mutates host UI must keep using ownsInstalledEditorFactory().
|
|
342
|
+
// Ownership counts before Pi constructs its editor; layered visibility requires the recorded
|
|
343
|
+
// instance to remain reachable through the current factory.
|
|
344
|
+
const layeredVisibleEditor = () => layeredEditor.editorVisibleThrough(currentEditorFactory());
|
|
345
|
+
const editorConsumerActive = () =>
|
|
346
|
+
ownsInstalledEditorFactory() || layeredVisibleEditor() !== undefined;
|
|
347
|
+
const visibleEditor = () =>
|
|
348
|
+
ownsInstalledEditorFactory() ? activeEditor?.editor : layeredVisibleEditor();
|
|
349
|
+
const requestEditorRepaint = () => {
|
|
350
|
+
requestEditorRender?.();
|
|
351
|
+
// The layered record intentionally outlives ownership clearing, so disabling the editor is
|
|
352
|
+
// gated here; a live wrapper keeps repainting while the editor remains enabled.
|
|
353
|
+
if (effectiveEditorEnabled()) layeredEditor.requestRender(currentEditorFactory());
|
|
354
|
+
};
|
|
355
|
+
|
|
327
356
|
const customVariables = new CustomVariables(
|
|
328
357
|
pi.events,
|
|
329
358
|
(key) => {
|
|
330
359
|
if (!customVariableSessionReady || !sessionLifecycle.isCurrent()) return false;
|
|
331
360
|
return (
|
|
332
361
|
(effectiveEditorEnabled() &&
|
|
333
|
-
|
|
334
|
-
activeEditor?.editor.isMetadataDecorated() === true &&
|
|
362
|
+
visibleEditor()?.isMetadataDecorated() === true &&
|
|
335
363
|
editorDemandsCustomVariable(currentConfig, key)) ||
|
|
336
364
|
(installedFooterKind === "starship" &&
|
|
337
365
|
ownsInstalledFooter() &&
|
|
@@ -340,7 +368,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
340
368
|
},
|
|
341
369
|
() => {
|
|
342
370
|
if (!customVariableSessionReady || !sessionLifecycle.isCurrent()) return;
|
|
343
|
-
|
|
371
|
+
requestEditorRepaint();
|
|
344
372
|
requestFooterRender?.();
|
|
345
373
|
},
|
|
346
374
|
);
|
|
@@ -349,7 +377,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
349
377
|
customVariables.reconcile();
|
|
350
378
|
codexQuota.reconcile();
|
|
351
379
|
requestFooterRender?.();
|
|
352
|
-
|
|
380
|
+
requestEditorRepaint();
|
|
353
381
|
};
|
|
354
382
|
const thinkingExperimental = new ThinkingExperimentalController(
|
|
355
383
|
() => currentConfig.components.thinkingSteps,
|
|
@@ -391,7 +419,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
391
419
|
if (!host.skinOnly)
|
|
392
420
|
workingLine.setRequestRender(() => {
|
|
393
421
|
if (sessionLifecycle.isCurrent()) {
|
|
394
|
-
|
|
422
|
+
requestEditorRepaint();
|
|
395
423
|
}
|
|
396
424
|
});
|
|
397
425
|
let workingLineSessionReady = false;
|
|
@@ -468,7 +496,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
468
496
|
return usageTotals.resolve(
|
|
469
497
|
ctx,
|
|
470
498
|
(effectiveEditorEnabled() &&
|
|
471
|
-
|
|
499
|
+
editorConsumerActive() &&
|
|
472
500
|
["tokens", "input_tokens", "output_tokens", "cost", "cache_hit"].some((name) =>
|
|
473
501
|
editorMetadataReferences(currentConfig).has(name),
|
|
474
502
|
)) ||
|
|
@@ -504,9 +532,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
504
532
|
)
|
|
505
533
|
return undefined;
|
|
506
534
|
const editorDemand =
|
|
507
|
-
effectiveEditorEnabled() &&
|
|
508
|
-
ownsInstalledEditorFactory() &&
|
|
509
|
-
editorWantsCodexQuota(currentConfig);
|
|
535
|
+
effectiveEditorEnabled() && editorConsumerActive() && editorWantsCodexQuota(currentConfig);
|
|
510
536
|
const footerDemand =
|
|
511
537
|
effectiveFooterStyle() === "starship" &&
|
|
512
538
|
currentConfig.components.footer.codexQuota &&
|
|
@@ -516,7 +542,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
516
542
|
() => {
|
|
517
543
|
if (!sessionLifecycle.isCurrent()) return;
|
|
518
544
|
requestFooterRender?.();
|
|
519
|
-
|
|
545
|
+
requestEditorRepaint();
|
|
520
546
|
},
|
|
521
547
|
);
|
|
522
548
|
const getEditorQuota = () =>
|
|
@@ -598,7 +624,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
598
624
|
currentConfig,
|
|
599
625
|
installedFooterReferences(),
|
|
600
626
|
effectiveEditorEnabled() &&
|
|
601
|
-
|
|
627
|
+
editorConsumerActive() &&
|
|
602
628
|
currentConfig.components.editor.style === "minimalist",
|
|
603
629
|
);
|
|
604
630
|
|
|
@@ -647,6 +673,8 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
647
673
|
if (!sessionLifecycle.isCurrent() || !ctx.hasUI) return;
|
|
648
674
|
if (activeTuiContext?.ui === ctx.ui) activeTuiContext = ctx;
|
|
649
675
|
if (editorInstalled && !ownsInstalledEditorFactory()) reconcileObservedEditorOwnership(ctx);
|
|
676
|
+
// A layered editor can stop being visible without any ownership transition to observe.
|
|
677
|
+
if (projectRefreshActive && !needsProjectRefresh()) stopProjectRefresh();
|
|
650
678
|
syncFooterState(ctx);
|
|
651
679
|
if (project && needsProjectRefresh()) scheduleProjectRefresh(ctx);
|
|
652
680
|
refresh();
|
|
@@ -698,9 +726,8 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
698
726
|
sessionLifecycle.isCurrent() &&
|
|
699
727
|
agentRunActive &&
|
|
700
728
|
agentDurationClock.isActive() &&
|
|
701
|
-
minimalistDecorationActive &&
|
|
729
|
+
minimalistDecorationActive() &&
|
|
702
730
|
effectiveEditorEnabled() &&
|
|
703
|
-
ownsInstalledEditorFactory() &&
|
|
704
731
|
currentConfig.components.editor.style === "minimalist" &&
|
|
705
732
|
editorMetadataReferences(currentConfig).has("turn_duration");
|
|
706
733
|
if (!needed) {
|
|
@@ -721,13 +748,25 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
721
748
|
});
|
|
722
749
|
};
|
|
723
750
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
751
|
+
// Decoration is bound to the editor instance that reported it, so it stays valid while that
|
|
752
|
+
// editor is visible (owned or layered) and cannot leak to a replacement editor.
|
|
753
|
+
const minimalistDecorationActive = () =>
|
|
754
|
+
sessionLifecycle.isCurrent() &&
|
|
755
|
+
minimalistDecorationEditor !== undefined &&
|
|
756
|
+
visibleEditor() === minimalistDecorationEditor;
|
|
757
|
+
const reconcileMinimalistDecoration = () => {
|
|
728
758
|
customVariables.reconcile();
|
|
729
759
|
reconcileAgentTimer();
|
|
730
760
|
};
|
|
761
|
+
const setMinimalistDecorationActive = (
|
|
762
|
+
active: boolean,
|
|
763
|
+
editor?: PolishedEditor | WrappedPolishedEditor,
|
|
764
|
+
) => {
|
|
765
|
+
const next = active ? editor : undefined;
|
|
766
|
+
if (minimalistDecorationEditor === next) return;
|
|
767
|
+
minimalistDecorationEditor = next;
|
|
768
|
+
reconcileMinimalistDecoration();
|
|
769
|
+
};
|
|
731
770
|
|
|
732
771
|
const startAgentTurn = (interactionStarted: boolean) => {
|
|
733
772
|
agentRunActive = true;
|
|
@@ -852,12 +891,12 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
852
891
|
if (activeTuiContext && workingLineSessionReady) {
|
|
853
892
|
if (!host.skinOnly) workingLine.reconcile(activeTuiContext);
|
|
854
893
|
}
|
|
855
|
-
setMinimalistDecorationActive(false);
|
|
856
894
|
requestEditorRender = undefined;
|
|
857
895
|
wrappedEditorFactory = undefined;
|
|
858
896
|
installedEditorFactory = undefined;
|
|
859
897
|
editorInstallMode = "none";
|
|
860
898
|
editorInstalled = false;
|
|
899
|
+
reconcileMinimalistDecoration();
|
|
861
900
|
codexQuota.reconcile();
|
|
862
901
|
};
|
|
863
902
|
|
|
@@ -900,8 +939,12 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
900
939
|
return observed;
|
|
901
940
|
};
|
|
902
941
|
|
|
942
|
+
// Exclusive: gates mutation of host UI such as the Working row.
|
|
903
943
|
const isActiveEditor = (editor: PolishedEditor | WrappedPolishedEditor, generation: number) =>
|
|
904
944
|
sessionLifecycle.isCurrent(generation) && activeEditor?.editor === editor;
|
|
945
|
+
// Read-only: also true for an editor still visible beneath a foreign wrapper.
|
|
946
|
+
const isVisibleEditor = (editor: PolishedEditor | WrappedPolishedEditor, generation: number) =>
|
|
947
|
+
sessionLifecycle.isCurrent(generation) && visibleEditor() === editor;
|
|
905
948
|
|
|
906
949
|
const editorBorderCapabilityChanged = (
|
|
907
950
|
editor: PolishedEditor | WrappedPolishedEditor,
|
|
@@ -910,14 +953,14 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
910
953
|
if (!isActiveEditor(editor, generation) || !activeTuiContext || !workingLineSessionReady)
|
|
911
954
|
return;
|
|
912
955
|
if (!host.skinOnly) workingLine.reconcile(activeTuiContext);
|
|
913
|
-
if (workingLine.isAvailable())
|
|
956
|
+
if (workingLine.isAvailable()) requestEditorRepaint();
|
|
914
957
|
};
|
|
915
958
|
|
|
916
959
|
const editorMetadataDecorationChanged = (
|
|
917
960
|
editor: PolishedEditor | WrappedPolishedEditor,
|
|
918
961
|
generation: number,
|
|
919
962
|
) => {
|
|
920
|
-
if (
|
|
963
|
+
if (isVisibleEditor(editor, generation)) customVariables.reconcile();
|
|
921
964
|
};
|
|
922
965
|
|
|
923
966
|
const editorWorkingLineFrame = (
|
|
@@ -928,6 +971,31 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
928
971
|
? workingLine.currentWorkingLineFrame()
|
|
929
972
|
: undefined;
|
|
930
973
|
|
|
974
|
+
/** Records a freshly constructed editor as owned (exclusive) or visible beneath a foreign wrapper. */
|
|
975
|
+
const registerConstructedEditor = (
|
|
976
|
+
ctx: ExtensionContext,
|
|
977
|
+
factory: ZentuiEditorFactory,
|
|
978
|
+
generation: number,
|
|
979
|
+
editor: PolishedEditor | WrappedPolishedEditor,
|
|
980
|
+
tui: TUI,
|
|
981
|
+
) => {
|
|
982
|
+
const observed = observeEditorFactory(ctx);
|
|
983
|
+
if (
|
|
984
|
+
!sessionLifecycle.isCurrent(generation) ||
|
|
985
|
+
activeTuiContext?.ui !== ctx.ui ||
|
|
986
|
+
!observed.known
|
|
987
|
+
) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
if (observed.factory === factory) {
|
|
991
|
+
activeEditor = { editor, factory, generation };
|
|
992
|
+
requestEditorRender = () => tui.requestRender();
|
|
993
|
+
} else if (observed.factory && !isZentuiEditorFactory(observed.factory)) {
|
|
994
|
+
// A foreign wrapper built this editor beneath its own outer factory.
|
|
995
|
+
layeredEditor.attach(observed.factory, generation, editor, () => tui.requestRender());
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
|
|
931
999
|
const makeEditorFactory = (
|
|
932
1000
|
ctx: ExtensionContext,
|
|
933
1001
|
baseFactory: EditorFactory | undefined,
|
|
@@ -977,21 +1045,12 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
977
1045
|
};
|
|
978
1046
|
},
|
|
979
1047
|
(active) => {
|
|
980
|
-
if (
|
|
1048
|
+
if (isVisibleEditor(editor, generation)) setMinimalistDecorationActive(active, editor);
|
|
981
1049
|
},
|
|
982
1050
|
() => editorBorderCapabilityChanged(editor, generation),
|
|
983
1051
|
() => editorMetadataDecorationChanged(editor, generation),
|
|
984
1052
|
);
|
|
985
|
-
|
|
986
|
-
if (
|
|
987
|
-
sessionLifecycle.isCurrent(generation) &&
|
|
988
|
-
activeTuiContext?.ui === ctx.ui &&
|
|
989
|
-
observed.known &&
|
|
990
|
-
observed.factory === factory
|
|
991
|
-
) {
|
|
992
|
-
activeEditor = { editor, factory, generation };
|
|
993
|
-
requestEditorRender = () => tui.requestRender();
|
|
994
|
-
}
|
|
1053
|
+
registerConstructedEditor(ctx, factory, generation, editor, tui);
|
|
995
1054
|
return editor;
|
|
996
1055
|
}) as ZentuiEditorFactory;
|
|
997
1056
|
factory[ZENTUI_EDITOR_FACTORY] = true;
|
|
@@ -1048,21 +1107,12 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
1048
1107
|
};
|
|
1049
1108
|
},
|
|
1050
1109
|
(active) => {
|
|
1051
|
-
if (
|
|
1110
|
+
if (isVisibleEditor(editor, generation)) setMinimalistDecorationActive(active, editor);
|
|
1052
1111
|
},
|
|
1053
1112
|
() => editorBorderCapabilityChanged(editor, generation),
|
|
1054
1113
|
() => editorMetadataDecorationChanged(editor, generation),
|
|
1055
1114
|
);
|
|
1056
|
-
|
|
1057
|
-
if (
|
|
1058
|
-
sessionLifecycle.isCurrent(generation) &&
|
|
1059
|
-
activeTuiContext?.ui === ctx.ui &&
|
|
1060
|
-
observed.known &&
|
|
1061
|
-
observed.factory === factory
|
|
1062
|
-
) {
|
|
1063
|
-
activeEditor = { editor, factory, generation };
|
|
1064
|
-
requestEditorRender = () => tui.requestRender();
|
|
1065
|
-
}
|
|
1115
|
+
registerConstructedEditor(ctx, factory, generation, editor, tui);
|
|
1066
1116
|
return editor;
|
|
1067
1117
|
}) as ZentuiEditorFactory;
|
|
1068
1118
|
factory[ZENTUI_EDITOR_FACTORY] = true;
|
|
@@ -1415,6 +1465,8 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
1415
1465
|
if (!ctx || !sessionLifecycle.isCurrent()) return;
|
|
1416
1466
|
sessionLifecycle.shutdown();
|
|
1417
1467
|
activeEditor = undefined;
|
|
1468
|
+
minimalistDecorationEditor = undefined;
|
|
1469
|
+
layeredEditor.detach();
|
|
1418
1470
|
extensionStatuses.dispose();
|
|
1419
1471
|
codexQuota.stop();
|
|
1420
1472
|
stopSessionTimer();
|
|
@@ -1474,6 +1526,7 @@ export default function (pi: ExtensionAPI, host: ZentuiHost = {}) {
|
|
|
1474
1526
|
customVariables.clear();
|
|
1475
1527
|
const lifecycleGeneration = sessionLifecycle.start();
|
|
1476
1528
|
activeEditor = undefined;
|
|
1529
|
+
minimalistDecorationEditor = undefined;
|
|
1477
1530
|
// A new generation must not expose or route extension segments through the previous
|
|
1478
1531
|
// session while TUI startup is in progress.
|
|
1479
1532
|
workingLineSessionReady = false;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records that a Zentui editor instance is still visible beneath a foreign predecessor-preserving
|
|
3
|
+
* wrapper (`setEditorComponent((tui, theme, kb) => predecessor(tui, theme, kb))`).
|
|
4
|
+
*
|
|
5
|
+
* This is deliberately NOT installation ownership: Zentui does not own the outer factory, must not
|
|
6
|
+
* replace or restore it, and keeps this record across ownership clearing. It only answers
|
|
7
|
+
* "may a surface Zentui rendered still be on screen?" for read-only consumers such as usage demand
|
|
8
|
+
* and redraw requests. The record is valid only while the outer factory observed at construction is
|
|
9
|
+
* still the current editor factory, so any replacement of the chain invalidates it.
|
|
10
|
+
*
|
|
11
|
+
* Visibility is only evidence that a wrapper constructed the editor, not that its rows reach the
|
|
12
|
+
* screen, so surfaces that mutate host UI (for example hiding the native Working row) must keep
|
|
13
|
+
* requiring exclusive installation ownership instead of consulting this record.
|
|
14
|
+
*/
|
|
15
|
+
export class LayeredEditorConsumer<Factory, Editor = unknown> {
|
|
16
|
+
private record?: {
|
|
17
|
+
outerFactory: Factory;
|
|
18
|
+
generation: number;
|
|
19
|
+
editor: Editor;
|
|
20
|
+
requestRender: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
constructor(private readonly isCurrentGeneration: (generation: number) => boolean) {}
|
|
24
|
+
|
|
25
|
+
attach(
|
|
26
|
+
outerFactory: Factory,
|
|
27
|
+
generation: number,
|
|
28
|
+
editor: Editor,
|
|
29
|
+
requestRender: () => void,
|
|
30
|
+
): void {
|
|
31
|
+
this.record = { outerFactory, generation, editor, requestRender };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
detach(): void {
|
|
35
|
+
this.record = undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
editorVisibleThrough(currentFactory: Factory | undefined): Editor | undefined {
|
|
39
|
+
const record = this.record;
|
|
40
|
+
if (!record) return undefined;
|
|
41
|
+
if (!this.isCurrentGeneration(record.generation) || record.outerFactory !== currentFactory) {
|
|
42
|
+
this.record = undefined;
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return record.editor;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
isVisibleThrough(currentFactory: Factory | undefined): boolean {
|
|
49
|
+
return this.editorVisibleThrough(currentFactory) !== undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
requestRender(currentFactory: Factory | undefined): void {
|
|
53
|
+
if (this.isVisibleThrough(currentFactory)) this.record?.requestRender();
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED