@agnishc/edb-global-footer 0.10.6 → 0.10.9

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.9] - 2026-05-18
4
+
5
+ ## [0.10.8] - 2026-05-18
6
+
3
7
  ## [0.10.6] - 2026-05-15
4
8
 
5
9
  ## [0.10.5] - 2026-05-15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agnishc/edb-global-footer",
3
- "version": "0.10.6",
3
+ "version": "0.10.9",
4
4
  "description": "Pi extension: two-line status footer showing path, git branch, token usage, and model",
5
5
  "keywords": [
6
6
  "pi-package",
package/src/git.ts CHANGED
@@ -13,7 +13,7 @@ export function parseGitStatus(output: string): GitStatus {
13
13
  if (!line) continue;
14
14
  if (line.startsWith("# branch.head ")) {
15
15
  const head = line.slice("# branch.head ".length).trim();
16
- branch = head && head !== "(detached)" ? head : null;
16
+ branch = head && !head.startsWith("(detached") ? head : null;
17
17
  continue;
18
18
  }
19
19
  if (line.startsWith("# branch.ab ")) {
@@ -0,0 +1,134 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatThinkingLabel, formatTokens, sanitizeStatusText } from "./format.js";
3
+ import { parseGitStatus } from "./git.js";
4
+ import { withIcon } from "./icons.js";
5
+ import { formatTps } from "./tps.js";
6
+
7
+ describe("formatTokens", () => {
8
+ it("formats small numbers without suffix", () => {
9
+ expect(formatTokens(0)).toBe("0");
10
+ expect(formatTokens(999)).toBe("999");
11
+ });
12
+
13
+ it("formats K suffix for thousands", () => {
14
+ expect(formatTokens(1000)).toBe("1.0k");
15
+ expect(formatTokens(1500)).toBe("1.5k");
16
+ expect(formatTokens(9500)).toBe("9.5k");
17
+ });
18
+
19
+ it("formats M suffix for millions", () => {
20
+ expect(formatTokens(1_000_000)).toBe("1.0M");
21
+ expect(formatTokens(1_500_000)).toBe("1.5M");
22
+ });
23
+
24
+ it("formats M suffix for billions", () => {
25
+ expect(formatTokens(1_000_000_000)).toBe("1000M");
26
+ });
27
+ });
28
+
29
+ describe("formatThinkingLabel", () => {
30
+ it("returns MI for minimal", () => {
31
+ expect(formatThinkingLabel("minimal")).toBe("MI");
32
+ });
33
+
34
+ it("returns L for low", () => {
35
+ expect(formatThinkingLabel("low")).toBe("L");
36
+ });
37
+
38
+ it("returns M for medium", () => {
39
+ expect(formatThinkingLabel("medium")).toBe("M");
40
+ });
41
+
42
+ it("returns H for high", () => {
43
+ expect(formatThinkingLabel("high")).toBe("H");
44
+ });
45
+
46
+ it("returns XH for xhigh", () => {
47
+ expect(formatThinkingLabel("xhigh")).toBe("XH");
48
+ });
49
+
50
+ it("returns empty string for off", () => {
51
+ expect(formatThinkingLabel("off")).toBe("");
52
+ });
53
+ });
54
+
55
+ describe("sanitizeStatusText", () => {
56
+ it("replaces newlines with spaces", () => {
57
+ expect(sanitizeStatusText("hello\nworld")).toBe("hello world");
58
+ });
59
+
60
+ it("replaces carriage returns with spaces", () => {
61
+ expect(sanitizeStatusText("hello\rworld")).toBe("hello world");
62
+ });
63
+
64
+ it("replaces tabs with spaces", () => {
65
+ expect(sanitizeStatusText("hello\tworld")).toBe("hello world");
66
+ });
67
+
68
+ it("collapses multiple spaces", () => {
69
+ expect(sanitizeStatusText("hello world")).toBe("hello world");
70
+ });
71
+
72
+ it("handles combined edge cases", () => {
73
+ expect(sanitizeStatusText("hello\r\n\tworld")).toBe("hello world");
74
+ });
75
+ });
76
+
77
+ describe("parseGitStatus", () => {
78
+ it("parses branch from porcelain=v2 format", () => {
79
+ const output = `# branch.oid abc1234
80
+ # branch.head main
81
+ # branch.upstream origin/main
82
+ # branch.ab +1 -2
83
+ M file.txt`;
84
+ const result = parseGitStatus(output);
85
+ expect(result.branch).toBe("main");
86
+ expect(result.ahead).toBe(1);
87
+ expect(result.behind).toBe(2);
88
+ expect(result.dirty).toBe(true);
89
+ });
90
+
91
+ it("handles detached HEAD", () => {
92
+ const output = `# branch.oid abc1234
93
+ # branch.head (detached at abc1234)
94
+ `;
95
+ const result = parseGitStatus(output);
96
+ expect(result.branch).toBeNull();
97
+ });
98
+
99
+ it("marks dirty when non-comment lines present", () => {
100
+ const output = `# branch.oid abc1234
101
+ # branch.head main
102
+ M modified.txt`;
103
+ const result = parseGitStatus(output);
104
+ expect(result.dirty).toBe(true);
105
+ });
106
+ });
107
+
108
+ describe("withIcon", () => {
109
+ it("combines icon and text", () => {
110
+ expect(withIcon("🚀", "Launch")).toBe("🚀 Launch");
111
+ });
112
+
113
+ it("returns text only when icon is empty", () => {
114
+ expect(withIcon("", "Launch")).toBe("Launch");
115
+ });
116
+ });
117
+
118
+ describe("formatTps", () => {
119
+ it("formats 0 tps as 0", () => {
120
+ expect(formatTps(0)).toBe("0");
121
+ });
122
+
123
+ it("formats single tps", () => {
124
+ expect(formatTps(1)).toBe("1.0");
125
+ });
126
+
127
+ it("formats multiple tps", () => {
128
+ expect(formatTps(10)).toBe("10");
129
+ });
130
+
131
+ it("rounds large tps", () => {
132
+ expect(formatTps(15.7)).toBe("16");
133
+ });
134
+ });