pi-claude-code-ui 1.0.73 → 1.0.74
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/CHANGELOG.md +6 -0
- package/extensions/index.ts +52 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
> [!IMPORTANT]
|
|
4
4
|
> **1.0.69 — package rename (permanent).** Canonical npm name is now [`pi-claude-code-ui`](https://www.npmjs.com/package/pi-claude-code-ui). `pi-claude-style-tools` is legacy and will not receive further releases. Install with `pi install npm:pi-claude-code-ui` or `npm i pi-claude-code-ui`.
|
|
5
5
|
|
|
6
|
+
## 1.0.74 — 2026-07-18
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **Grouped tool boxes keep Agent breathe aligned** — group rows strip all breathe glyphs (including `·` and the blank off-phase) before re-prefixing a fresh light, so titles no longer walk sideways. Group header/child lights also follow the shared blink phase (and Agent breathe) instead of wall-clock `isBlinkOn()`.
|
|
11
|
+
|
|
6
12
|
## 1.0.73 — 2026-07-18
|
|
7
13
|
|
|
8
14
|
### Fixed
|
package/extensions/index.ts
CHANGED
|
@@ -559,20 +559,33 @@ function themeStatusDot(theme: Theme, colorKey: "success" | "error" | "dim" | "m
|
|
|
559
559
|
return theme.fg(colorKey, `${STATUS_DOT_BOLD}${STATUS_DOT_FILLED}`);
|
|
560
560
|
}
|
|
561
561
|
|
|
562
|
-
function
|
|
562
|
+
function agentBreatheGlyphRaw(): string {
|
|
563
563
|
// Always exactly one display cell — matches ordinary tool dots, keeps titles aligned.
|
|
564
|
-
|
|
564
|
+
return AGENT_BREATHE_GLYPHS[_globalBlinkPhaseIndex % AGENT_BREATHE_LEN];
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function paintAgentBreatheDot(colorAnsi: string = TOOL_STATUS_SUCCESS): string {
|
|
568
|
+
const glyph = agentBreatheGlyphRaw();
|
|
565
569
|
if (glyph === " ") return " ";
|
|
566
570
|
// Bold only on the largest frame so weight changes without shifting the cell.
|
|
567
571
|
const bold = glyph === "●" ? STATUS_DOT_BOLD : "";
|
|
572
|
+
return `${colorAnsi}${bold}${glyph}${TRANSPARENT_RESET}`;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function agentBreatheDot(theme: Theme): string {
|
|
576
|
+
const glyph = agentBreatheGlyphRaw();
|
|
577
|
+
if (glyph === " ") return " ";
|
|
578
|
+
const bold = glyph === "●" ? STATUS_DOT_BOLD : "";
|
|
568
579
|
return theme.fg("success", `${bold}${glyph}`);
|
|
569
580
|
}
|
|
570
581
|
|
|
571
|
-
function groupStatusLight(status: ToolStatus): string {
|
|
582
|
+
function groupStatusLight(status: ToolStatus, options?: { agentBreathe?: boolean }): string {
|
|
572
583
|
const color = status === "success" ? TOOL_STATUS_SUCCESS : status === "error" ? TOOL_STATUS_ERROR : TOOL_STATUS_PENDING;
|
|
573
584
|
if (status === "pending") {
|
|
574
|
-
//
|
|
575
|
-
|
|
585
|
+
// Prefer the shared blink phase over wall-clock so group lights stay in sync
|
|
586
|
+
// with the global timer (and Agent breathe). Space keeps column alignment.
|
|
587
|
+
if (options?.agentBreathe) return paintAgentBreatheDot(TOOL_STATUS_SUCCESS);
|
|
588
|
+
return _globalBlinkPhase ? paintStatusDot(TOOL_STATUS_SUCCESS) : " ";
|
|
576
589
|
}
|
|
577
590
|
return paintStatusDot(color);
|
|
578
591
|
}
|
|
@@ -610,7 +623,13 @@ function stripToolChrome(lines: string[]): string[] {
|
|
|
610
623
|
}
|
|
611
624
|
|
|
612
625
|
function stripLeadingToolStatus(line: string): string {
|
|
613
|
-
|
|
626
|
+
// Drop the single-cell status marker so group rows can re-prefix a fresh light.
|
|
627
|
+
// Include Agent breathe glyphs (·) and the blank off-phase (space) so the title
|
|
628
|
+
// never keeps a leftover marker that shifts when size changes.
|
|
629
|
+
return line.replace(
|
|
630
|
+
/^((?:\x1b\[[0-9;]*m|[ \t]|[├└│─])*)(?:\x1b\[[0-9;]*m)*(?:[●○✗■⬤•·]| )(?:\x1b\[[0-9;]*m)*\s+/,
|
|
631
|
+
"$1",
|
|
632
|
+
);
|
|
614
633
|
}
|
|
615
634
|
|
|
616
635
|
function trimAnsiLeft(text: string): string {
|
|
@@ -684,19 +703,30 @@ function branchContinuation(index: number, total: number, theme?: Theme): string
|
|
|
684
703
|
return index === total - 1 ? " " : ` ${rule}│${TRANSPARENT_RESET} `;
|
|
685
704
|
}
|
|
686
705
|
|
|
687
|
-
function formatBranchedToolLines(
|
|
706
|
+
function formatBranchedToolLines(
|
|
707
|
+
lines: string[],
|
|
708
|
+
index: number,
|
|
709
|
+
total: number,
|
|
710
|
+
width: number,
|
|
711
|
+
status: ToolStatus,
|
|
712
|
+
options?: { agentBreathe?: boolean },
|
|
713
|
+
): string[] {
|
|
688
714
|
const output: string[] = [];
|
|
689
715
|
const content = lines.filter((line) => isTerminalImageLine(line) || stripAnsi(line).trim().length > 0);
|
|
690
716
|
const safeContent = content.length > 0 ? content : [""];
|
|
691
|
-
const light = groupStatusLight(status);
|
|
717
|
+
const light = groupStatusLight(status, options);
|
|
692
718
|
for (let lineIndex = 0; lineIndex < safeContent.length; lineIndex++) {
|
|
693
719
|
const line = safeContent[lineIndex];
|
|
694
720
|
if (isTerminalImageLine(line)) {
|
|
695
721
|
output.push(line);
|
|
696
722
|
continue;
|
|
697
723
|
}
|
|
724
|
+
// Always strip any leftover status marker from the child call line before
|
|
725
|
+
// re-prefixing. Agent breathe used · which the old stripper missed, so the
|
|
726
|
+
// title walked sideways as size changed inside groups.
|
|
727
|
+
const body = lineIndex === 0 ? removeGroupedToolPrefix(line) : trimAnsiLeft(line);
|
|
698
728
|
const prefix = lineIndex === 0 ? `${branchPrefix(index, total)}${light} ` : `${branchContinuation(index, total)} `;
|
|
699
|
-
output.push(clampLineWidth(`${prefix}${
|
|
729
|
+
output.push(clampLineWidth(`${prefix}${body}`, width));
|
|
700
730
|
}
|
|
701
731
|
return output;
|
|
702
732
|
}
|
|
@@ -818,7 +848,11 @@ class ToolGroupComponent extends Container {
|
|
|
818
848
|
const label = getToolGroupLabel(this.tools);
|
|
819
849
|
const names = groupedName ? "" : formatToolNameList(this.tools);
|
|
820
850
|
const overall: ToolStatus = status.error > 0 ? "error" : status.pending > 0 ? "pending" : "success";
|
|
821
|
-
|
|
851
|
+
// Group header breathes only when every pending member is Agent-family;
|
|
852
|
+
// mixed groups keep the ordinary on/off light.
|
|
853
|
+
const pendingTools = this.tools.filter((tool) => getToolStatusForGroup(tool) === "pending");
|
|
854
|
+
const headerBreathe = pendingTools.length > 0 && pendingTools.every((tool) => isAgentFamilyToolName(getToolName(tool)));
|
|
855
|
+
const light = groupStatusLight(overall, { agentBreathe: headerBreathe });
|
|
822
856
|
const summaryLabel = `${label}:`;
|
|
823
857
|
const countParts: string[] = [];
|
|
824
858
|
if (status.pending) countParts.push(statusText("pending", status.pending));
|
|
@@ -835,7 +869,14 @@ class ToolGroupComponent extends Container {
|
|
|
835
869
|
const rawLines = this.expanded
|
|
836
870
|
? getExpandedToolGroupLines(tool, childWidth, groupedName ? label : undefined)
|
|
837
871
|
: [getCompactToolLine(tool, childWidth, groupedName ? label : undefined)];
|
|
838
|
-
const branched = formatBranchedToolLines(
|
|
872
|
+
const branched = formatBranchedToolLines(
|
|
873
|
+
rawLines,
|
|
874
|
+
index,
|
|
875
|
+
total,
|
|
876
|
+
safeWidth,
|
|
877
|
+
getToolStatusForGroup(tool),
|
|
878
|
+
{ agentBreathe: isAgentFamilyToolName(getToolName(tool)) },
|
|
879
|
+
);
|
|
839
880
|
for (let i = 0; i < branched.length; i++) {
|
|
840
881
|
lines.push(clampLineWidth(branched[i], safeWidth));
|
|
841
882
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-claude-code-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.74",
|
|
4
4
|
"description": "Claude Code-style grouped tool rows for pi with Ctrl+O/Ctrl+Shift+O previews, math rendering, and consistent built-in, MCP, and custom tool rendering",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|