pi-fold 4.1.2 → 4.1.3

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.
@@ -225,6 +225,7 @@ export * from "./lib/persistence.ts";
225
225
  export * from "./lib/policy.ts";
226
226
  export * from "./lib/rollback.ts";
227
227
  export * from "./lib/scheduling.ts";
228
+ export * from "./lib/status-widget.ts";
228
229
  export * from "./lib/selection.ts";
229
230
  export * from "./lib/transcript.ts";
230
231
 
@@ -746,11 +747,18 @@ export function registerActiveContext(pi: any, options: {
746
747
  };
747
748
  }, { placement: "belowEditor" });
748
749
  };
749
- const foldBarModel = (input: {
750
+ const foldBarModel = (ctx: any, input: {
750
751
  share: number | null; budgetTokens: number; staged: number; roots: number; weighed: boolean;
751
752
  }): FoldBarModel => {
752
753
  const state = persistence.state;
753
- const snapshot = lifecycle.latestSnapshot;
754
+ // THE SAME SNAPSHOT /fold-status PRICES AGAINST. `markFreedBytes` answers ZERO, silently,
755
+ // for a span it cannot locate contiguously in the snapshot it is handed, and the last
756
+ // context event's snapshot can trail the branch the marks were cut from; the bar read
757
+ // "5 staged, 0 to free" beside a status command pricing the same marks at 41,877
758
+ // tokens (Shane, 2026-09-02, session 01a06272). The authoritative snapshot is memoized
759
+ // on the branch length, so reading it here costs nothing while nothing has changed.
760
+ let snapshot = lifecycle.latestSnapshot;
761
+ try { snapshot = authoritativeSnapshotFor(ctx); } catch { }
754
762
  let stagedTokens = 0;
755
763
  let placeholderChars = 0;
756
764
  let hiddenChars = 0;
@@ -801,7 +809,7 @@ export function registerActiveContext(pi: any, options: {
801
809
  try {
802
810
  installFoldBar(ctx);
803
811
  if (foldBar.installed) {
804
- foldBar.model = foldBarModel({
812
+ foldBar.model = foldBarModel(ctx, {
805
813
  share, budgetTokens: capacity.budgetTokens, staged, roots,
806
814
  weighed: ladder.bandTopMeasurement !== null &&
807
815
  ladder.bandTopMeasurement === measurements.lastProviderMeasurement,
@@ -66,11 +66,33 @@ export interface FoldBarTheme {
66
66
  fg(color: "dim" | "muted" | "text" | "warning" | "error", text: string): string;
67
67
  bold(text: string): string;
68
68
  getColorMode?(): "truecolor" | "256color";
69
+ getFgAnsi?(color: "text"): string;
70
+ name?: string;
69
71
  }
70
72
 
71
- // Crameri batlow10, classes 3, 6 and 8 (0-indexed). The two darkest classes are unusable
72
- // as fill on a dark terminal, and the lightest washes out on a light one.
73
- const BATLOW = { raw: "#3C6D56", staged: "#D29343", folded: "#FDB7BC" } as const;
73
+ // Crameri batlow10, three classes per background. Classes 3, 6 and 8 (0-indexed) read on
74
+ // a dark terminal; the lightest of them is 1.7:1 against white, so a light terminal steps
75
+ // two classes darker (2, 5 and 6), the same triple the README's light figure uses.
76
+ const BATLOW_ON_DARK = { raw: "#3C6D56", staged: "#D29343", folded: "#FDB7BC" } as const;
77
+ const BATLOW_ON_LIGHT = { raw: "#1C5A62", staged: "#9D892B", folded: "#D29343" } as const;
78
+
79
+ /**
80
+ * WHICH WAY THE BACKGROUND GOES, read off the theme's own text colour rather than its
81
+ * name: a theme whose body text is dark is drawn on a light background. pi hands the
82
+ * text colour out as a truecolor escape, and a theme that does not (256-colour mode,
83
+ * or a host without the accessor) falls back to the name, then to dark.
84
+ */
85
+ export function lightBackground(theme: FoldBarTheme): boolean {
86
+ try {
87
+ const ansi = theme.getFgAnsi?.("text") ?? "";
88
+ const match = /38;2;(\d+);(\d+);(\d+)/.exec(ansi);
89
+ if (match) {
90
+ const [r, g, b] = match.slice(1).map((v) => Number(v) / 255);
91
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.5;
92
+ }
93
+ } catch { }
94
+ return /light/i.test(theme.name ?? "");
95
+ }
74
96
 
75
97
  const truecolor = (hex: string, text: string): string => {
76
98
  const r = parseInt(hex.slice(1, 3), 16);
@@ -140,6 +162,7 @@ export function renderFoldBar(model: FoldBarModel, width: number, theme: FoldBar
140
162
  width, theme.fg("dim", "..."));
141
163
  }
142
164
  const rich = theme.getColorMode?.() === "truecolor";
165
+ const BATLOW = lightBackground(theme) ? BATLOW_ON_LIGHT : BATLOW_ON_DARK;
143
166
  const ink: Record<Cell, (text: string) => string> = {
144
167
  raw: (t) => rich ? truecolor(BATLOW.raw, t) : theme.fg("muted", t),
145
168
  staged: (t) => rich ? truecolor(BATLOW.staged, t) : theme.fg("text", t),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fold",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
4
4
  "description": "Lossless context folding for the Pi coding agent: only the oldest context changes; the fresh window stays untouched.",
5
5
  "keywords": [
6
6
  "pi-package",