@xynogen/pix-pretty 1.18.0 → 1.18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-pretty",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
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",
@@ -49,8 +49,6 @@ const ANSI_CAPTURE_RE = new RegExp(`${ESC_RE}\\[([^m]*)m`, "g");
49
49
  // Terminal bounds + thresholds
50
50
  // ---------------------------------------------------------------------------
51
51
 
52
- const MAX_TERM_WIDTH = 210;
53
-
54
52
  const MAX_PREVIEW_LINES = envInt("PRETTY_MAX_PREVIEW_LINES", 80);
55
53
 
56
54
  const SPLIT_MIN_WIDTH = envInt("DIFF_SPLIT_MIN_WIDTH", dc.splitMinWidth || 150);
@@ -158,8 +156,8 @@ function tabs(s: string): string {
158
156
  function termW(): number {
159
157
  // Single source of truth: utils.termW caches, falls back to tty ioctl, and
160
158
  // invalidates on resize. Diff layout needs a hard floor of 80 cols for the
161
- // split-view column math, so clamp the shared value here.
162
- return Math.max(80, Math.min(utilsTermW(), MAX_TERM_WIDTH));
159
+ // split-view column math (no ceiling — fill the true width on ultrawide).
160
+ return Math.max(80, utilsTermW());
163
161
  }
164
162
 
165
163
  /** Pad/truncate `s` to exactly `w` visible chars. ANSI-aware. */
@@ -557,8 +555,7 @@ export async function renderUnified(
557
555
  if (l.type === "sep") {
558
556
  const gap = l.newNum;
559
557
  const label = gap && gap > 0 ? ` ${gap} unmodified lines ` : "···";
560
- const totalW = Math.min(tw, 72);
561
- const pad = Math.max(0, totalW - label.length - 2);
558
+ const pad = Math.max(0, tw - label.length - 2);
562
559
  const half1 = Math.floor(pad / 2);
563
560
  const half2 = pad - half1;
564
561
  out.push(`${BG_BASE}${FG_DIM}${"─".repeat(half1)}${label}${"─".repeat(half2)}${RST}`);
package/src/utils.test.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  ruleFrame,
16
16
  sectionRule,
17
17
  setResultDetails,
18
+ termW,
18
19
  viewportText,
19
20
  } from "./utils.js";
20
21
 
@@ -160,6 +161,35 @@ function plain(text: string): string {
160
161
  return text.replace(ANSI, "");
161
162
  }
162
163
 
164
+ describe("termW", () => {
165
+ // termW() caches and invalidates on a stdout 'resize' event. Set columns then
166
+ // emit resize so the next call re-reads.
167
+ function setCols(cols: number): void {
168
+ (process.stdout as { columns?: number }).columns = cols;
169
+ process.stdout.emit("resize");
170
+ }
171
+
172
+ it("returns the true terminal width with no upper clamp (ultrawide)", () => {
173
+ const orig = process.stdout.columns;
174
+ try {
175
+ setCols(384); // wider than the old 210 cap
176
+ expect(termW()).toBe(384);
177
+ } finally {
178
+ setCols(orig ?? 80);
179
+ }
180
+ });
181
+
182
+ it("floors width at 1 for a degenerate column count", () => {
183
+ const orig = process.stdout.columns;
184
+ try {
185
+ setCols(0); // falsy — falls through resolution chain, never < 1
186
+ expect(termW()).toBeGreaterThanOrEqual(1);
187
+ } finally {
188
+ setCols(orig ?? 80);
189
+ }
190
+ });
191
+ });
192
+
163
193
  describe("ruleFrame", () => {
164
194
  it("wraps body with a rule top and bottom, then footer below the close", () => {
165
195
  const out = ruleFrame(["a", "b"], ["… +3 more"], 10);
@@ -469,10 +499,9 @@ describe("sectionRule", () => {
469
499
  expect(sectionRule("\x1b[31m=== x ===\x1b[0m", tag, 20)).toBeNull();
470
500
  });
471
501
 
472
- it("caps the divider width on an ultra-wide terminal", () => {
473
- // Ask for 400 columns — the visible width must stay capped (<= 72).
502
+ it("fills the full requested width so it aligns with the tool frame", () => {
474
503
  const out = sectionRule("=== x ===", tag, 400) ?? "";
475
504
  const visible = out.replace(/<\/?[a-z]+>/g, "");
476
- expect([...visible].length).toBeLessThanOrEqual(72);
505
+ expect([...visible].length).toBe(400);
477
506
  });
478
507
  });
package/src/utils.ts CHANGED
@@ -52,6 +52,9 @@ export function fillToolBackground(text: string, bg = BG_BASE, width?: number):
52
52
  }
53
53
 
54
54
  export function viewportTextConstructor(TextComponent: TextComponentCtor): TextComponentCtor {
55
+ // SAFETY: a `new`-able ctor and a plain factory returning the same instance
56
+ // shape are interchangeable to callers here; TS can't see the returned
57
+ // ViewportText satisfies TextComponentCtor's construct signature.
55
58
  return function ViewportTextComponent(text = "") {
56
59
  const component = viewportText(TextComponent);
57
60
  component.setText(text);
@@ -451,7 +454,9 @@ export function termW(): number {
451
454
  Number.parseInt(process.env.COLUMNS ?? "", 10) ||
452
455
  _readTtyColumns() ||
453
456
  120;
454
- _cachedTermW = Math.max(1, Math.min(raw, 210));
457
+ // No upper clamp: frames must fill the true terminal width so our rules align
458
+ // with Pi's native full-width UI on ultrawide displays.
459
+ _cachedTermW = Math.max(1, raw);
455
460
 
456
461
  return _cachedTermW;
457
462
  }
@@ -500,26 +505,21 @@ export function rule(w: number, paint?: RulePaint): string {
500
505
  /** Matches an `=== label ===` separator line (a common shell/echo idiom). */
501
506
  const SECTION_RE = /^\s*={2,}\s*(.+?)\s*={2,}\s*$/;
502
507
 
503
- /** Max width of a section divider — a full-width rule on an ultra-wide
504
- * terminal reads as noise, so cap it regardless of available columns. */
505
- const SECTION_RULE_MAX = 72;
506
-
507
508
  /** Fixed dash count before a left-aligned section label. */
508
509
  const SECTION_RULE_LEAD = 4;
509
510
 
510
511
  /**
511
512
  * If `line` is an `=== label ===` separator, render it as a left-aligned
512
513
  * section divider (`──── label ───────────`); otherwise return null. Callers
513
- * fall back to their normal per-line rendering on null. Sized to `width` but
514
- * capped at SECTION_RULE_MAX so it stays readable on wide terminals. Label and
515
- * rule both use the muted role — theme-driven, ANSI-safe.
514
+ * fall back to their normal per-line rendering on null. Fills the full `width`
515
+ * so the divider aligns with the surrounding tool frame. Label and rule both
516
+ * use the muted role — theme-driven, ANSI-safe.
516
517
  */
517
518
  export function sectionRule(line: string, theme: FgTheme, width: number): string | null {
518
519
  const m = SECTION_RE.exec(line);
519
520
  if (!m || hasAnsi(line)) return null;
520
521
  const label = ` ${m[1]} `;
521
- const w = Math.min(width, SECTION_RULE_MAX);
522
- const trail = w - SECTION_RULE_LEAD - visibleWidth(label);
522
+ const trail = width - SECTION_RULE_LEAD - visibleWidth(label);
523
523
  // Label fits → lead rule + trailing fill. Too long to fill → don't force a
524
524
  // full-width rule; wrap it snugly with 2 dashes each side (`── long text ──`).
525
525
  const [lead, tail] = trail >= 2 ? [SECTION_RULE_LEAD, trail] : [2, 2];