kcode-pi 0.1.30 → 0.1.31

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.
@@ -4,6 +4,96 @@ import { readActiveRun } from "../src/harness/state.ts";
4
4
  import type { ActiveRun, GateResult } from "../src/harness/types.ts";
5
5
  import { formatProductProfile } from "../src/product/profile.ts";
6
6
 
7
+ /** ANSI escape sequence pattern: CSI, OSC, APC. */
8
+ const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b_[^\x1b]*\x1b\\/g;
9
+
10
+ /** Visible width of a string (strips ANSI codes; CJK/wide chars = 2 columns). */
11
+ function visibleWidth(str: string): number {
12
+ let width = 0;
13
+ const clean = str.replace(ANSI_RE, "");
14
+ for (const ch of clean) {
15
+ const code = ch.codePointAt(0)!;
16
+ width +=
17
+ code >= 0x1100 &&
18
+ !(code >= 0x00a0 && code <= 0x00ff) &&
19
+ ((code >= 0x1100 && code <= 0x115f) ||
20
+ (code >= 0x2329 && code <= 0x232a) ||
21
+ (code >= 0x2e80 && code <= 0x303e) ||
22
+ (code >= 0x3040 && code <= 0x3247) ||
23
+ (code >= 0x3250 && code <= 0x4dbf) ||
24
+ (code >= 0x4e00 && code <= 0xa4c6) ||
25
+ (code >= 0xa960 && code <= 0xa97c) ||
26
+ (code >= 0xac00 && code <= 0xd7a3) ||
27
+ (code >= 0xf900 && code <= 0xfaff) ||
28
+ (code >= 0xfe10 && code <= 0xfe19) ||
29
+ (code >= 0xfe30 && code <= 0xfe6b) ||
30
+ (code >= 0xff01 && code <= 0xff60) ||
31
+ (code >= 0xffe0 && code <= 0xffe6) ||
32
+ (code >= 0x1f300 && code <= 0x1f9ff) ||
33
+ (code >= 0x20000 && code <= 0x2fffd))
34
+ ? 2
35
+ : 1;
36
+ }
37
+ return width;
38
+ }
39
+
40
+ /**
41
+ * If the line's visible width exceeds `maxWidth`, truncate visible characters
42
+ * and append `>` so the result fits. Preserves ANSI codes in the kept portion
43
+ * and appends SGR reset before the `>`. No padding — pi-tui only requires
44
+ * visibleWidth <= width.
45
+ */
46
+ function clipLine(text: string, maxWidth: number): string {
47
+ if (maxWidth <= 0) return "";
48
+ const vw = visibleWidth(text);
49
+ if (vw <= maxWidth) return text;
50
+
51
+ const targetW = maxWidth - 1; // reserve 1 col for ">"
52
+ let result = "";
53
+ let visibleSoFar = 0;
54
+ let i = 0;
55
+
56
+ while (i < text.length && visibleSoFar < targetW) {
57
+ // Preserve ANSI escape sequences
58
+ if (text[i] === "\x1b") {
59
+ const m = text.slice(i).match(/^(?:\[[0-9;]*[A-Za-z]|\][^\x07]*\x07|_[^\x1b]*\x1b\\)/);
60
+ if (m) {
61
+ result += m[0];
62
+ i += m[0].length;
63
+ continue;
64
+ }
65
+ }
66
+
67
+ const ch = text[i];
68
+ const code = ch.codePointAt(0)!;
69
+ const wide =
70
+ (code >= 0x1100 && code <= 0x115f) ||
71
+ (code >= 0x2329 && code <= 0x232a) ||
72
+ (code >= 0x2e80 && code <= 0x303e) ||
73
+ (code >= 0x3040 && code <= 0x3247) ||
74
+ (code >= 0x3250 && code <= 0x4dbf) ||
75
+ (code >= 0x4e00 && code <= 0xa4c6) ||
76
+ (code >= 0xa960 && code <= 0xa97c) ||
77
+ (code >= 0xac00 && code <= 0xd7a3) ||
78
+ (code >= 0xf900 && code <= 0xfaff) ||
79
+ (code >= 0xfe10 && code <= 0xfe19) ||
80
+ (code >= 0xfe30 && code <= 0xfe6b) ||
81
+ (code >= 0xff01 && code <= 0xff60) ||
82
+ (code >= 0xffe0 && code <= 0xffe6) ||
83
+ (code >= 0x1f300 && code <= 0x1f9ff) ||
84
+ (code >= 0x20000 && code <= 0x2fffd);
85
+ const charW = wide ? 2 : 1;
86
+
87
+ if (visibleSoFar + charW > targetW) break;
88
+
89
+ result += ch;
90
+ visibleSoFar += charW;
91
+ i++;
92
+ }
93
+
94
+ return result + "\x1b[0m>";
95
+ }
96
+
7
97
  function formatProduct(run: ActiveRun | undefined): string {
8
98
  if (!run) return "未选择";
9
99
  if (run.profile?.product === "unknown") return "未确认";
@@ -43,12 +133,6 @@ function riskColor(risk: string): "error" | "warning" | "muted" | "success" {
43
133
  return "success";
44
134
  }
45
135
 
46
- function padOrTrim(text: string, width: number): string {
47
- if (width <= 0) return "";
48
- if (text.length > width) return text.slice(0, Math.max(0, width - 1)) + ">";
49
- return text + " ".repeat(width - text.length);
50
- }
51
-
52
136
  function logoLines(theme: Theme): string[] {
53
137
  const accent = (text: string) => theme.fg("accent", text);
54
138
  const muted = (text: string) => theme.fg("muted", text);
@@ -87,9 +171,9 @@ export default function (pi: ExtensionAPI) {
87
171
 
88
172
  return [
89
173
  "",
90
- ...logoLines(theme).map((line) => padOrTrim(line, width)),
91
- padOrTrim(status, width),
92
- padOrTrim(theme.fg("dim", `run:${runId}`), width),
174
+ ...logoLines(theme).map((line) => clipLine(line, width)),
175
+ clipLine(status, width),
176
+ clipLine(theme.fg("dim", `run:${runId}`), width),
93
177
  "",
94
178
  ];
95
179
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kcode-pi",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "面向金蝶开发的 Pi Coding Agent 启动器、工具包和 Harness 工作流",
5
5
  "type": "module",
6
6
  "private": false,