pi-pretty-tui 0.3.4 → 0.3.5

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.
@@ -1,5 +1,5 @@
1
1
  import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
2
- import { sliceByColumn, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { mixColors, sliceByColumn, visibleWidth } from "@earendil-works/pi-tui";
3
3
 
4
4
  type Range = [number, number];
5
5
  type Row = { marker: string; number?: string; text: string; styled: string; changes: Range[] };
@@ -82,13 +82,16 @@ export function editDiffLayout(theme: any, diff: string, path?: string) {
82
82
  const color = row.marker === "+" ? "toolDiffAdded" : row.marker === "-" ? "toolDiffRemoved" : "toolDiffContext";
83
83
  const background = row.marker === "+" ? "toolSuccessBg" : row.marker === "-" ? "toolErrorBg" : undefined;
84
84
  const paint = (text: string) => background && theme.bg ? theme.bg(background, text) : text;
85
+ const changeBackground = background && theme.colors?.[background] && theme.colors?.[color]
86
+ ? mixColors(theme.colors[background], theme.colors[color], 0.3) : undefined;
85
87
  const width = visibleWidth(row.text);
86
88
  let content = "", cursor = 0;
87
89
  for (const [start, end] of row.changes) {
88
90
  content += sliceByColumn(row.styled, cursor, start - cursor);
89
91
  const changed = sliceByColumn(row.styled, start, end - start);
90
- // Underline + bold preserve syntax foreground colors, unlike inverse.
91
- content += theme.underline ? theme.underline(theme.bold(changed)) : theme.bold(changed);
92
+ // A stronger theme-derived background preserves syntax foregrounds and
93
+ // avoids adding font emphasis to the changed graphemes.
94
+ content += changeBackground && theme.style ? theme.style(changed, { bg: changeBackground }) : paint(changed);
92
95
  cursor = end;
93
96
  }
94
97
  content += sliceByColumn(row.styled, cursor, Math.max(0, width - cursor));
@@ -37,12 +37,15 @@ import {
37
37
  copyToClipboard,
38
38
  getAgentDir,
39
39
  getMarkdownTheme,
40
+ getLanguageFromPath,
41
+ highlightCode,
40
42
  type ExtensionAPI,
41
43
  } from "@earendil-works/pi-coding-agent";
42
44
  import {
43
45
  type Component,
44
46
  Markdown,
45
47
  stripTerminalSequences,
48
+ sliceByColumn,
46
49
  truncateToWidth,
47
50
  TuiAltScreen,
48
51
  type TuiMouseEvent,
@@ -2828,8 +2831,22 @@ export default function prettyTui(pi: ExtensionAPI) {
2828
2831
 
2829
2832
  const paint = (line: string) => row.background
2830
2833
  ? row.background(line + " ".repeat(Math.max(0, contentWidth - visibleWidth(line)))) : line;
2831
- rendered.push(prefix + paint(wrapped[0] ?? ""));
2832
- for (const line of wrapped.slice(1)) rendered.push(continuation + paint(line));
2834
+ const corner = renderMode === "clean" ? stripTerminalSequences(prefix).indexOf("└") : -1;
2835
+ if (corner >= 0 && wrapped.length > 1) {
2836
+ // Close the branch on the final physical row, not the first row of
2837
+ // a logical source line that may wrap or contain explicit newlines.
2838
+ const column = visibleWidth(stripTerminalSequences(prefix).slice(0, corner));
2839
+ const guide = (symbol: string) => sliceByColumn(continuation, 0, column) +
2840
+ defaultActivityTheme().fg("dim", symbol) +
2841
+ sliceByColumn(continuation, column + 1, Math.max(0, visibleWidth(continuation) - column - 1));
2842
+ rendered.push(prefix.replace("└", "│") + paint(wrapped[0] ?? ""));
2843
+ for (let index = 1; index < wrapped.length; index++) {
2844
+ rendered.push(guide(index === wrapped.length - 1 ? "└" : "│") + paint(wrapped[index]));
2845
+ }
2846
+ } else {
2847
+ rendered.push(prefix + paint(wrapped[0] ?? ""));
2848
+ for (const line of wrapped.slice(1)) rendered.push(continuation + paint(line));
2849
+ }
2833
2850
  }
2834
2851
 
2835
2852
  return rendered;
@@ -2850,7 +2867,8 @@ export default function prettyTui(pi: ExtensionAPI) {
2850
2867
  if (renderMode === "compact") return name === "bash" ? theme.fg("toolTitle", "$ ") : "";
2851
2868
  return theme.fg(color, "● ");
2852
2869
  },
2853
- continuation: renderMode === "compact" && name !== "bash" ? "" : " ",
2870
+ continuation: renderMode === "clean" && expanded ? theme.fg("dim", "│ ") :
2871
+ renderMode === "compact" && name !== "bash" ? "" : " ",
2854
2872
  content: renderMode === "compact" ? theme.fg("muted", detail) :
2855
2873
  theme.fg("text", theme.bold(name)) +
2856
2874
  theme.fg("dim", "(") +
@@ -3569,7 +3587,7 @@ export default function prettyTui(pi: ExtensionAPI) {
3569
3587
  const dimPhase = responseRunStartedAt !== undefined &&
3570
3588
  Math.floor(Math.max(0, Date.now() - responseRunStartedAt) / RUNNING_DOT_INTERVAL_MS) % 2 === 1;
3571
3589
  const color = currentActivity === "done"
3572
- ? collapsedDone ? "dim" : "success"
3590
+ ? collapsedDone ? "dim" : "text"
3573
3591
  : dimPhase ? "dim" : "accent";
3574
3592
  return currentActivity !== "done" && !dimPhase
3575
3593
  ? runningColor(theme, "● ")
@@ -3580,7 +3598,7 @@ export default function prettyTui(pi: ExtensionAPI) {
3580
3598
  const currentActivity = typeof activity === "function" ? activity() : activity;
3581
3599
  const label = currentActivity === "done" ? "Done" : "Running";
3582
3600
  const color = label === "Done"
3583
- ? collapsedDone ? "dim" : "success"
3601
+ ? collapsedDone ? "dim" : "text"
3584
3602
  : "accent";
3585
3603
  const detailColor = label === "Done" && collapsedDone ? "dim" : "text";
3586
3604
  const title = label === "Running"
@@ -4652,7 +4670,7 @@ export default function prettyTui(pi: ExtensionAPI) {
4652
4670
  if (hideCleanTool(context.toolCallId, context.expanded, context.executionStarted)) return cleanToolCall(theme, writeLabel, context.toolCallId);
4653
4671
  const content = typeof args.content === "string" ? args.content : "";
4654
4672
  const path = String(args.path ?? "");
4655
- if (useCompactToolView(context.toolCallId, context.expanded)) {
4673
+ if (renderMode === "clean" || useCompactToolView(context.toolCallId, context.expanded)) {
4656
4674
  const lines = content.replace(/\t/g, " ").split("\n");
4657
4675
  while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
4658
4676
  const total = lines.length;
@@ -4665,6 +4683,33 @@ export default function prettyTui(pi: ExtensionAPI) {
4665
4683
  if (hideCleanTool(context.toolCallId, options.expanded, context.executionStarted)) return hiddenToolResult(context, options, output);
4666
4684
  if (options.isPartial) return partialResult(context, theme, "Writing…");
4667
4685
  const failed = completeStatus(context);
4686
+ if (renderMode === "clean") {
4687
+ if (failed) return result(theme, "Write failed", output, options.expanded, true);
4688
+ const status = output ? `Written · ${output}` : "Written";
4689
+ const rows: DisplayRow[] = [{
4690
+ prefix: theme.fg("dim", "└ "), continuation: " ",
4691
+ content: theme.fg("toolOutput", status),
4692
+ }];
4693
+ if (options.expanded) {
4694
+ const content = typeof context.args?.content === "string" ? context.args.content : "";
4695
+ const source = content.replace(/\r\n|\r/g, "\n").replace(/\t/g, " ").split("\n");
4696
+ // A final newline terminates the last line; additional blank lines
4697
+ // are actual file content and retain their own line numbers.
4698
+ if (source.at(-1) === "") source.pop();
4699
+ let highlighted: string[];
4700
+ try { highlighted = highlightCode(source.join("\n"), getLanguageFromPath(String(context.args?.path ?? ""))); }
4701
+ catch { highlighted = source; }
4702
+ const digits = String(source.length).length;
4703
+ for (let index = 0; index < source.length; index++) {
4704
+ rows.push({
4705
+ prefix: theme.fg("dim", index === source.length - 1 ? " └ " : " │ ") + theme.fg("dim", `${String(index + 1).padStart(digits)} `),
4706
+ continuation: (index === source.length - 1 ? " " : theme.fg("dim", " │ ")) + " ".repeat(digits + 1),
4707
+ content: highlighted[index] ?? source[index],
4708
+ });
4709
+ }
4710
+ }
4711
+ return block(rows);
4712
+ }
4668
4713
  return result(
4669
4714
  theme,
4670
4715
  failed ? output.split("\n")[0] || "Write failed" : "Written",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-pretty-tui",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "A beautiful, polished TUI for the Pi coding agent",
5
5
  "license": "MIT",
6
6
  "author": "Kainan Yang <ykn0309@whu.edu.cn>",