@xynogen/pix-pretty 1.7.12 → 1.7.14

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
@@ -61,7 +61,24 @@ pi install npm:@xynogen/pix-pretty
61
61
 
62
62
  ## Configuration
63
63
 
64
- ### Environment Variables
64
+ Configuration is read from **`~/.pi/agent/pix.json`** (the unified config file hosted by `@xynogen/pix-data/pix-config`). The `pretty` section of that file sets the defaults for theme, icon mode, and preview lines. Environment variables still override `pix.json` values.
65
+
66
+ > **Note:** `pix-config.ts` and `collapse.ts` previously shipped with `pix-pretty` — they have moved to `pix-data` (`@xynogen/pix-data/pix-config` and `@xynogen/pix-data/collapse`). Update any direct imports.
67
+
68
+ ### `pix.json` — `pretty` section
69
+
70
+ ```jsonc
71
+ {
72
+ "pretty": {
73
+ "theme": "monokai", // syntax-highlight theme
74
+ "icons": "nerd", // nerd | unicode | ascii
75
+ "maxPreviewLines": 50,
76
+ "diffColors": true
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### Environment Variables (override `pix.json`)
65
82
 
66
83
  - `PRETTY_THEME` — color theme for syntax highlighting
67
84
  - `PRETTY_MAX_HL_CHARS` — max characters to highlight (default: 80000)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-pretty",
3
- "version": "1.7.12",
3
+ "version": "1.7.14",
4
4
  "description": "Enhanced tool output rendering with syntax highlighting, file icons, tree views, diff rendering, and FFF search",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -58,6 +58,7 @@
58
58
  "access": "public"
59
59
  },
60
60
  "dependencies": {
61
+ "@xynogen/pix-data": "^0.3.0",
61
62
  "cli-highlight": "^2.1.11",
62
63
  "@ff-labs/fff-node": "^0.5.2",
63
64
  "diff": "^7.0.0"
package/src/config.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
+ import { pixConfig } from "@xynogen/pix-data/pix-config";
4
5
  import type { BundledTheme } from "./types.js";
5
6
 
6
7
  const DEFAULT_THEME: BundledTheme = "github-dark";
@@ -29,8 +30,10 @@ function readThemeFromSettings(agentDir?: string): BundledTheme | undefined {
29
30
  }
30
31
 
31
32
  function resolvePrettyTheme(agentDir?: string): BundledTheme {
33
+ // Precedence: env → pix.json → settings.json → default
32
34
  return (
33
35
  (process.env.PRETTY_THEME as BundledTheme | undefined) ??
36
+ (pixConfig().pretty.theme as BundledTheme) ??
34
37
  readThemeFromSettings(agentDir) ??
35
38
  DEFAULT_THEME
36
39
  );
@@ -49,14 +52,46 @@ export function envInt(name: string, fallback: number): number {
49
52
  return Number.isFinite(v) && v > 0 ? v : fallback;
50
53
  }
51
54
 
52
- export const MAX_HL_CHARS = envInt("PRETTY_MAX_HL_CHARS", 80_000);
55
+ // Precedence for numeric config: env var → pix.json → hardcoded default
56
+ function pixOrEnvInt(
57
+ envName: string,
58
+ pixValue: number,
59
+ fallback: number,
60
+ ): number {
61
+ const env = process.env[envName];
62
+ if (env) {
63
+ const v = Number.parseInt(env, 10);
64
+ if (Number.isFinite(v) && v > 0) return v;
65
+ }
66
+ return pixValue !== fallback ? pixValue : fallback;
67
+ }
68
+
69
+ const pc = pixConfig().pretty;
70
+
71
+ export const MAX_HL_CHARS = pixOrEnvInt(
72
+ "PRETTY_MAX_HL_CHARS",
73
+ pc.maxHighlightChars,
74
+ 80_000,
75
+ );
53
76
 
54
- export const MAX_PREVIEW_LINES = envInt("PRETTY_MAX_PREVIEW_LINES", 80);
77
+ export const MAX_PREVIEW_LINES = pixOrEnvInt(
78
+ "PRETTY_MAX_PREVIEW_LINES",
79
+ pc.maxPreviewLines,
80
+ 80,
81
+ );
55
82
 
56
- export const CACHE_LIMIT = envInt("PRETTY_CACHE_LIMIT", 128);
83
+ export const CACHE_LIMIT = pixOrEnvInt(
84
+ "PRETTY_CACHE_LIMIT",
85
+ pc.cacheLimit,
86
+ 128,
87
+ );
57
88
 
58
89
  // --- Diff rendering limits (edit/write tools) ---
59
- export const MAX_RENDER_LINES = envInt("PRETTY_MAX_RENDER_LINES", 150);
90
+ export const MAX_RENDER_LINES = pixOrEnvInt(
91
+ "PRETTY_MAX_RENDER_LINES",
92
+ pc.maxRenderLines,
93
+ 150,
94
+ );
60
95
 
61
96
  // Word-level emphasis only when paired del/add lines are at least this similar.
62
97
  export const WORD_DIFF_MIN_SIM = 0.15;
@@ -8,6 +8,7 @@
8
8
  // technique below works unchanged — diff backgrounds layer underneath and
9
9
  // persist through fg switches.
10
10
 
11
+ import { pixConfig } from "@xynogen/pix-data/pix-config";
11
12
  import * as Diff from "diff";
12
13
  import { BG_BASE, BOLD, FG_DIM, FG_LNUM, FG_RULE, RST } from "./ansi.js";
13
14
  import { MAX_HL_CHARS, MAX_RENDER_LINES, WORD_DIFF_MIN_SIM } from "./config.js";
@@ -44,21 +45,58 @@ function envBg(name: string, fallback: string): string {
44
45
  }
45
46
 
46
47
  // ---------------------------------------------------------------------------
47
- // Diff-specific ANSI (override via env, hex "#RRGGBB")
48
+ // Diff-specific ANSI (override via env pix.json → hardcoded)
48
49
  // ---------------------------------------------------------------------------
49
50
 
50
51
  const DIM = "\x1b[2m";
51
52
 
53
+ function hexToBg(hex: string): string {
54
+ if (!/^#[0-9a-fA-F]{6}$/.test(hex)) return "";
55
+ const r = Number.parseInt(hex.slice(1, 3), 16);
56
+ const g = Number.parseInt(hex.slice(3, 5), 16);
57
+ const b = Number.parseInt(hex.slice(5, 7), 16);
58
+ return `\x1b[48;2;${r};${g};${b}m`;
59
+ }
60
+
61
+ function hexToFg(hex: string): string {
62
+ if (!/^#[0-9a-fA-F]{6}$/.test(hex)) return "";
63
+ const r = Number.parseInt(hex.slice(1, 3), 16);
64
+ const g = Number.parseInt(hex.slice(3, 5), 16);
65
+ const b = Number.parseInt(hex.slice(5, 7), 16);
66
+ return `\x1b[38;2;${r};${g};${b}m`;
67
+ }
68
+
69
+ const dc = pixConfig().pretty.diff;
70
+
52
71
  // Subtle diff backgrounds — muted tones to let syntax fg shine through.
53
- const BG_ADD = envBg("DIFF_BG_ADD", "\x1b[48;2;22;38;32m"); // muted teal-green
54
- const BG_DEL = envBg("DIFF_BG_DEL", "\x1b[48;2;45;25;25m"); // muted brown-red
55
- const BG_ADD_W = envBg("DIFF_BG_ADD_HL", "\x1b[48;2;35;75;50m"); // word emphasis
56
- const BG_DEL_W = envBg("DIFF_BG_DEL_HL", "\x1b[48;2;80;35;35m");
57
- const BG_GUTTER_ADD = envBg("DIFF_BG_GUTTER_ADD", "\x1b[48;2;18;32;26m");
58
- const BG_GUTTER_DEL = envBg("DIFF_BG_GUTTER_DEL", "\x1b[48;2;38;22;22m");
59
-
60
- const FG_ADD = envFg("DIFF_FG_ADD", "\x1b[38;2;100;180;120m"); // desaturated green
61
- const FG_DEL = envFg("DIFF_FG_DEL", "\x1b[38;2;200;100;100m"); // desaturated red
72
+ // Precedence: env pix.json hardcoded default
73
+ const BG_ADD = envBg("DIFF_BG_ADD", hexToBg(dc.bgAdd) || "\x1b[48;2;22;38;32m");
74
+ const BG_DEL = envBg("DIFF_BG_DEL", hexToBg(dc.bgDel) || "\x1b[48;2;45;25;25m");
75
+ const BG_ADD_W = envBg(
76
+ "DIFF_BG_ADD_HL",
77
+ hexToBg(dc.bgAddHighlight) || "\x1b[48;2;35;75;50m",
78
+ );
79
+ const BG_DEL_W = envBg(
80
+ "DIFF_BG_DEL_HL",
81
+ hexToBg(dc.bgDelHighlight) || "\x1b[48;2;80;35;35m",
82
+ );
83
+ const BG_GUTTER_ADD = envBg(
84
+ "DIFF_BG_GUTTER_ADD",
85
+ hexToBg(dc.bgGutterAdd) || "\x1b[48;2;18;32;26m",
86
+ );
87
+ const BG_GUTTER_DEL = envBg(
88
+ "DIFF_BG_GUTTER_DEL",
89
+ hexToBg(dc.bgGutterDel) || "\x1b[48;2;38;22;22m",
90
+ );
91
+
92
+ const FG_ADD = envFg(
93
+ "DIFF_FG_ADD",
94
+ hexToFg(dc.fgAdd) || "\x1b[38;2;100;180;120m",
95
+ );
96
+ const FG_DEL = envFg(
97
+ "DIFF_FG_DEL",
98
+ hexToFg(dc.fgDel) || "\x1b[38;2;200;100;100m",
99
+ );
62
100
  const FG_STRIPE = "\x1b[38;2;40;40;40m"; // diagonal stripes on filler cells
63
101
 
64
102
  const BORDER_BAR = "▌";
@@ -76,8 +114,11 @@ const MAX_TERM_WIDTH = 210;
76
114
 
77
115
  const MAX_PREVIEW_LINES = envInt("PRETTY_MAX_PREVIEW_LINES", 80);
78
116
 
79
- const SPLIT_MIN_WIDTH = envInt("DIFF_SPLIT_MIN_WIDTH", 150);
80
- const SPLIT_MIN_CODE_WIDTH = envInt("DIFF_SPLIT_MIN_CODE_WIDTH", 60);
117
+ const SPLIT_MIN_WIDTH = envInt("DIFF_SPLIT_MIN_WIDTH", dc.splitMinWidth || 150);
118
+ const SPLIT_MIN_CODE_WIDTH = envInt(
119
+ "DIFF_SPLIT_MIN_CODE_WIDTH",
120
+ dc.splitMinCodeWidth || 60,
121
+ );
81
122
  const SPLIT_MAX_WRAP_RATIO = 0.2;
82
123
  const SPLIT_MAX_WRAP_LINES = 8;
83
124
 
@@ -14,6 +14,7 @@
14
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
15
  import { dirname, join } from "node:path";
16
16
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
17
+ import { pixConfig } from "@xynogen/pix-data/pix-config";
17
18
  import { ICON_MODES, type IconMode, setIconMode } from "./icon-catalog.js";
18
19
 
19
20
  function isIconMode(m: string): m is IconMode {
@@ -63,8 +64,16 @@ export function saveIconMode(mode: IconMode): void {
63
64
  /**
64
65
  * Apply the persisted mode (if any) to the catalog. Called once at extension
65
66
  * load so the env-seeded default is overridden by the user's saved choice.
67
+ *
68
+ * Precedence: env PRETTY_ICONS → pretty.json → pix.json pretty.icons → default ("nerd")
66
69
  */
67
70
  export function initIconMode(): void {
68
71
  const saved = loadIconMode();
69
- if (saved) setIconMode(saved);
72
+ if (saved) {
73
+ setIconMode(saved);
74
+ return;
75
+ }
76
+ // No persisted choice — try pix.json
77
+ const pixIcons = pixConfig().pretty.icons;
78
+ if (pixIcons && isIconMode(pixIcons)) setIconMode(pixIcons);
70
79
  }