@xynogen/pix-runtime 0.6.0 → 0.7.0

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 CHANGED
@@ -44,6 +44,12 @@ const timeoutMs = ioTimeoutMs(); // shared network timeout
44
44
  const signal = ioTimeoutSignal(toolSignal); // timeout + cancellation
45
45
  ```
46
46
 
47
+ Set `pretty.maxRenderWidth` and `pretty.maxRenderHeight` in `~/.pi/agent/pix.json`,
48
+ or change **Pretty → max modal width/height** with `/pix`. Values accept terminal
49
+ percentages such as `"65%"`/`"80%"` or fixed columns/rows such as `96`/`20`.
50
+ Percentage choices in `/pix` move in 5% steps. Width is the rendered frame width;
51
+ height is the threshold where modal content starts paging.
52
+
47
53
  Set `io.timeoutSec` in `~/.pi/agent/pix.json`, or change **Network → timeout (sec)**
48
54
  with `/pix`. The default is 30 seconds. It applies to Pix network operations,
49
55
  including remote skills, web fetch/search/transcription, MCP requests and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-runtime",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Pix shared runtime — versioned pix.json config, atomic persistence, typed change events",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -21,7 +21,7 @@ import { collapseSection } from "./sections/collapse.ts";
21
21
  import { compactionSection } from "./sections/compaction.ts";
22
22
  import { gateSection } from "./sections/gate.ts";
23
23
  import { ioSection } from "./sections/io.ts";
24
- import { prettySection } from "./sections/pretty.ts";
24
+ import { prettySection, type RenderSize } from "./sections/pretty.ts";
25
25
 
26
26
  interface SettingRow<T> {
27
27
  section: string;
@@ -42,6 +42,15 @@ function formatTokens(tokens: number): string {
42
42
  return tokens % 1_000_000 === 0 ? `${tokens / 1_000_000}M` : `${tokens / 1000}k`;
43
43
  }
44
44
 
45
+ function parseRenderSize(value: string): RenderSize {
46
+ return value.endsWith("%") ? (value as `${number}%`) : Number.parseFloat(value);
47
+ }
48
+
49
+ function resolveRenderSize(limit: RenderSize, available: number): number {
50
+ if (typeof limit === "number") return Math.floor(limit);
51
+ return Math.floor((available * Number.parseFloat(limit)) / 100);
52
+ }
53
+
45
54
  /** Parse a compact token label ("1M", "150k") back to an absolute count. */
46
55
  function parseTokens(label: string): number {
47
56
  const raw = label.trim();
@@ -71,6 +80,58 @@ const SETTINGS: SettingRow<unknown>[] = [
71
80
  read: (v) => v.lsStyle,
72
81
  patch: (value) => ({ lsStyle: value as "grid" | "tree" }),
73
82
  }),
83
+ row({
84
+ section: "Pretty",
85
+ label: "max modal width",
86
+ handle: prettySection,
87
+ values: [
88
+ "50%",
89
+ "55%",
90
+ "60%",
91
+ "65%",
92
+ "70%",
93
+ "75%",
94
+ "80%",
95
+ "85%",
96
+ "90%",
97
+ "95%",
98
+ "100%",
99
+ "72 cols",
100
+ "80 cols",
101
+ "88 cols",
102
+ "96 cols",
103
+ "104 cols",
104
+ "120 cols",
105
+ ],
106
+ read: (v) =>
107
+ typeof v.maxRenderWidth === "number" ? `${v.maxRenderWidth} cols` : v.maxRenderWidth,
108
+ patch: (value) => ({ maxRenderWidth: parseRenderSize(value) }),
109
+ }),
110
+ row({
111
+ section: "Pretty",
112
+ label: "max modal height",
113
+ handle: prettySection,
114
+ values: [
115
+ "50%",
116
+ "55%",
117
+ "60%",
118
+ "65%",
119
+ "70%",
120
+ "75%",
121
+ "80%",
122
+ "85%",
123
+ "90%",
124
+ "95%",
125
+ "100%",
126
+ "12 rows",
127
+ "16 rows",
128
+ "20 rows",
129
+ "24 rows",
130
+ ],
131
+ read: (v) =>
132
+ typeof v.maxRenderHeight === "number" ? `${v.maxRenderHeight} rows` : v.maxRenderHeight,
133
+ patch: (value) => ({ maxRenderHeight: parseRenderSize(value) }),
134
+ }),
74
135
  row({
75
136
  section: "Collapse",
76
137
  label: "enabled",
@@ -154,7 +215,6 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
154
215
  return;
155
216
  }
156
217
 
157
- const boxW = 52;
158
218
  await ui.custom(
159
219
  (
160
220
  tui: { requestRender(): void; terminal?: { rows?: number } },
@@ -208,7 +268,7 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
208
268
  };
209
269
 
210
270
  return {
211
- render: () => {
271
+ render: (width: number) => {
212
272
  const labelW = Math.max(...SETTINGS.map((r) => r.label.length));
213
273
  const body: string[] = [];
214
274
  const settingBodyLines: number[] = [];
@@ -230,8 +290,11 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
230
290
  body.push(`${cursor} ${label} ${theme.fg(isDefault ? "dim" : "success", value)}`);
231
291
  }
232
292
  const result = frameModal({
233
- width: boxW,
234
- maxHeight: modalHeight(tui.terminal?.rows),
293
+ width,
294
+ maxHeight: modalHeight(
295
+ tui.terminal?.rows,
296
+ runtime.get(prettySection).maxRenderHeight,
297
+ ),
235
298
  header: [theme.fg("accent", theme.bold(" pix settings")), ""],
236
299
  body,
237
300
  footer: [
@@ -282,7 +345,18 @@ export function registerPixCommand(pi: ExtensionAPI, runtime: PixRuntime): void
282
345
  },
283
346
  };
284
347
  },
285
- { overlay: true, overlayOptions: { anchor: "center", width: boxW, maxHeight: "80%" } },
348
+ {
349
+ overlay: true,
350
+ overlayOptions: () => {
351
+ const pretty = runtime.get(prettySection);
352
+ return {
353
+ anchor: "center",
354
+ width: pretty.maxRenderWidth,
355
+ maxHeight: pretty.maxRenderHeight,
356
+ margin: 2,
357
+ };
358
+ },
359
+ },
286
360
  );
287
361
  },
288
362
  });
@@ -309,9 +383,9 @@ interface RuntimeModalResult {
309
383
  maxBodyOffset: number;
310
384
  }
311
385
 
312
- function modalHeight(rows = 24): number {
386
+ function modalHeight(rows = 24, limit: RenderSize = "80%"): number {
313
387
  const safe = Number.isFinite(rows) ? Math.max(1, Math.floor(rows)) : 24;
314
- return Math.max(1, Math.min(safe, Math.floor(safe * 0.8)));
388
+ return Math.max(1, Math.min(safe, resolveRenderSize(limit, safe)));
315
389
  }
316
390
 
317
391
  function pageBodyOffset(offset: number, size: number, max: number, direction: -1 | 1): number {
@@ -20,6 +20,7 @@ export {
20
20
  type LsStyle,
21
21
  type PrettyConfig,
22
22
  prettySection,
23
+ type RenderSize,
23
24
  } from "./pretty.ts";
24
25
 
25
26
  import { collapseSection } from "./collapse.ts";
@@ -2,6 +2,7 @@ import { defineSection, enumOr, isObj, posNumOr } from "../schema.ts";
2
2
 
3
3
  export type IconMode = "nerd" | "unicode" | "ascii";
4
4
  export type LsStyle = "grid" | "tree";
5
+ export type RenderSize = number | `${number}%`;
5
6
 
6
7
  export interface DiffConfig {
7
8
  splitMinWidth: number;
@@ -11,6 +12,8 @@ export interface DiffConfig {
11
12
  export interface PrettyConfig {
12
13
  icons: IconMode;
13
14
  lsStyle: LsStyle;
15
+ maxRenderWidth: RenderSize;
16
+ maxRenderHeight: RenderSize;
14
17
  maxPreviewLines: number;
15
18
  maxRenderLines: number;
16
19
  maxHighlightChars: number;
@@ -21,9 +24,20 @@ export interface PrettyConfig {
21
24
  const ICON_MODES: readonly IconMode[] = ["nerd", "unicode", "ascii"];
22
25
  const LS_STYLES: readonly LsStyle[] = ["grid", "tree"];
23
26
 
27
+ function renderSizeOr(value: unknown, fallback: RenderSize): RenderSize {
28
+ if (typeof value === "number") return Number.isFinite(value) && value > 0 ? value : fallback;
29
+ if (typeof value !== "string") return fallback;
30
+ const match = value.match(/^(\d+(?:\.\d+)?)%$/);
31
+ if (!match) return fallback;
32
+ const percent = Number(match[1]);
33
+ return percent > 0 && percent <= 100 ? (value as `${number}%`) : fallback;
34
+ }
35
+
24
36
  const DEFAULTS: Readonly<PrettyConfig> = {
25
37
  icons: "nerd",
26
38
  lsStyle: "grid",
39
+ maxRenderWidth: "65%",
40
+ maxRenderHeight: "80%",
27
41
  maxPreviewLines: 80,
28
42
  maxRenderLines: 150,
29
43
  maxHighlightChars: 80_000,
@@ -40,6 +54,8 @@ export const prettySection = defineSection<"pretty", PrettyConfig>({
40
54
  return {
41
55
  icons: enumOr(raw.icons, ICON_MODES, DEFAULTS.icons),
42
56
  lsStyle: enumOr(raw.lsStyle, LS_STYLES, DEFAULTS.lsStyle),
57
+ maxRenderWidth: renderSizeOr(raw.maxRenderWidth, DEFAULTS.maxRenderWidth),
58
+ maxRenderHeight: renderSizeOr(raw.maxRenderHeight, DEFAULTS.maxRenderHeight),
43
59
  maxPreviewLines: posNumOr(raw.maxPreviewLines, DEFAULTS.maxPreviewLines),
44
60
  maxRenderLines: posNumOr(raw.maxRenderLines, DEFAULTS.maxRenderLines),
45
61
  maxHighlightChars: posNumOr(raw.maxHighlightChars, DEFAULTS.maxHighlightChars),