pi-one-ui 0.1.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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/extensions/app/config/renderer.ts +452 -0
  4. package/extensions/app/config/shell.ts +2484 -0
  5. package/extensions/app/ownership.ts +18 -0
  6. package/extensions/app/panel.ts +533 -0
  7. package/extensions/app/presets.ts +36 -0
  8. package/extensions/features/agent-summary/core.ts +194 -0
  9. package/extensions/features/agent-summary/index.ts +37 -0
  10. package/extensions/features/compact-thinking.ts +1262 -0
  11. package/extensions/features/context.ts +857 -0
  12. package/extensions/features/reference/index.ts +643 -0
  13. package/extensions/features/reference/session.ts +278 -0
  14. package/extensions/features/reference/subagent.ts +218 -0
  15. package/extensions/features/shell/aliases.ts +28 -0
  16. package/extensions/features/shell/flush-docked-bash.ts +39 -0
  17. package/extensions/features/shell/startup-header.ts +357 -0
  18. package/extensions/features/shell/working-message.ts +226 -0
  19. package/extensions/index.ts +52 -0
  20. package/extensions/shell/accent-rail-editor.ts +112 -0
  21. package/extensions/shell/accent-rail-layout-patch.ts +530 -0
  22. package/extensions/shell/completion-menu.ts +186 -0
  23. package/extensions/shell/editor-metadata-format.ts +287 -0
  24. package/extensions/shell/editor-transfer.ts +73 -0
  25. package/extensions/shell/extension-status.ts +102 -0
  26. package/extensions/shell/footer-format.ts +388 -0
  27. package/extensions/shell/footer-layout.ts +144 -0
  28. package/extensions/shell/footer.ts +1035 -0
  29. package/extensions/shell/format.ts +577 -0
  30. package/extensions/shell/git.ts +434 -0
  31. package/extensions/shell/icons.ts +261 -0
  32. package/extensions/shell/index.ts +1659 -0
  33. package/extensions/shell/interaction-summary.ts +1194 -0
  34. package/extensions/shell/live-context.ts +82 -0
  35. package/extensions/shell/minimalist-editor.ts +548 -0
  36. package/extensions/shell/package-version.ts +675 -0
  37. package/extensions/shell/project-refresh.ts +130 -0
  38. package/extensions/shell/project-state.ts +60 -0
  39. package/extensions/shell/prototype-patch-registry.ts +183 -0
  40. package/extensions/shell/runtime.ts +915 -0
  41. package/extensions/shell/selector-border.ts +109 -0
  42. package/extensions/shell/session-lifecycle.ts +60 -0
  43. package/extensions/shell/settings-command.ts +2144 -0
  44. package/extensions/shell/settings-previews.ts +137 -0
  45. package/extensions/shell/state.ts +83 -0
  46. package/extensions/shell/style.ts +480 -0
  47. package/extensions/shell/telemetry.ts +90 -0
  48. package/extensions/shell/ui.ts +1326 -0
  49. package/extensions/shell/user-message-osc.ts +279 -0
  50. package/extensions/shell/user-message-styles.ts +266 -0
  51. package/extensions/shell/user-message.ts +226 -0
  52. package/extensions/shell/working-line-messages.ts +24 -0
  53. package/extensions/shell/working-line-spinners.ts +54 -0
  54. package/extensions/shell/working-line.ts +1603 -0
  55. package/extensions/tools/ansi-color.ts +44 -0
  56. package/extensions/tools/ansi-text.ts +52 -0
  57. package/extensions/tools/component-tree.ts +37 -0
  58. package/extensions/tools/format.ts +28 -0
  59. package/extensions/tools/fullscreen-detect.ts +11 -0
  60. package/extensions/tools/patch-keys.ts +158 -0
  61. package/extensions/tools/sgr-mouse.ts +59 -0
  62. package/extensions/tools/tool-loading-icon.ts +20 -0
  63. package/extensions/tools/tool-result-sanitize.ts +27 -0
  64. package/extensions/transcript/index.ts +65 -0
  65. package/extensions/transcript/renderer/compact-mode.ts +1443 -0
  66. package/extensions/transcript/renderer/default-mode.ts +735 -0
  67. package/extensions/transcript/renderer/index.ts +286 -0
  68. package/extensions/transcript/renderer/markdown-enhance.ts +325 -0
  69. package/extensions/transcript/renderer/mouse/hover.ts +175 -0
  70. package/extensions/transcript/renderer/mouse/interaction.ts +1016 -0
  71. package/extensions/transcript/renderer/mouse/layout.ts +207 -0
  72. package/extensions/transcript/renderer/mouse/packets.ts +96 -0
  73. package/extensions/transcript/renderer/mouse/scroll.ts +211 -0
  74. package/extensions/transcript/renderer/tool/diff/ATTRIBUTION.md +28 -0
  75. package/extensions/transcript/renderer/tool/diff/ansi-utils.ts +128 -0
  76. package/extensions/transcript/renderer/tool/diff/diff-component.ts +112 -0
  77. package/extensions/transcript/renderer/tool/diff/diff-edit-render.ts +255 -0
  78. package/extensions/transcript/renderer/tool/diff/diff-header.ts +184 -0
  79. package/extensions/transcript/renderer/tool/diff/diff-highlight.ts +131 -0
  80. package/extensions/transcript/renderer/tool/diff/diff-inline.ts +350 -0
  81. package/extensions/transcript/renderer/tool/diff/diff-layout.ts +870 -0
  82. package/extensions/transcript/renderer/tool/diff/diff-limits.ts +189 -0
  83. package/extensions/transcript/renderer/tool/diff/diff-palette.ts +436 -0
  84. package/extensions/transcript/renderer/tool/diff/diff-parse.ts +536 -0
  85. package/extensions/transcript/renderer/tool/diff/diff-presentation.ts +79 -0
  86. package/extensions/transcript/renderer/tool/diff/diff-renderer.ts +29 -0
  87. package/extensions/transcript/renderer/tool/diff/diff-text.ts +57 -0
  88. package/extensions/transcript/renderer/tool/diff/diff-write-render.ts +629 -0
  89. package/extensions/transcript/renderer/tool/diff/index.ts +147 -0
  90. package/extensions/transcript/renderer/tool/diff/line-width-safety.ts +100 -0
  91. package/extensions/transcript/renderer/tool/diff/shiki-highlight.ts +104 -0
  92. package/extensions/transcript/renderer/tool/diff/write-execution.ts +124 -0
  93. package/extensions/transcript/renderer/tool/grouping.ts +619 -0
  94. package/extensions/transcript/renderer/tool/message-display.ts +183 -0
  95. package/extensions/transcript/renderer/tool/names.ts +195 -0
  96. package/extensions/transcript/renderer/tool/result.ts +842 -0
  97. package/extensions/transcript/renderer/tool/show-more-hint.ts +42 -0
  98. package/extensions/transcript/renderer/transcript-refresh.ts +79 -0
  99. package/package.json +58 -0
  100. package/themes/cc-dark.json +91 -0
  101. package/themes/cc-light.json +91 -0
@@ -0,0 +1,183 @@
1
+ import {
2
+ BranchSummaryMessageComponent,
3
+ CompactionSummaryMessageComponent,
4
+ SkillInvocationMessageComponent,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import { Markdown, Spacer, Text } from "@earendil-works/pi-tui";
7
+ import {
8
+ MESSAGE_DISPLAY_PATCH,
9
+ patchRegistry,
10
+ } from "../../../tools/patch-keys.ts";
11
+ import { config } from "../../../app/config/renderer.ts";
12
+ import { showMoreHintText } from "./show-more-hint.ts";
13
+ import { walkComponentTree } from "../../../tools/component-tree.ts";
14
+
15
+ /**
16
+ * 接管三个消息组件(`<skill>` 块、压缩摘要、分支摘要),ccstyle on 时渲染为
17
+ * 与工具调用一致的单行风格(● Title · hint),off 时回退原生。
18
+ *
19
+ * 三者结构同构:Box + markdownTheme + updateDisplay,构造/setExpanded/invalidate
20
+ * 三条路径都经过 updateDisplay,patch 一处即可全覆盖。它们不走
21
+ * ToolExecutionComponent,无法复用其原型 patch,这里单独统一管理。
22
+ */
23
+
24
+ // 与 renderer/index.ts renderCall 的成功勾一致:亮绿 ✓(truecolor ANSI)。
25
+ const BRIGHT_GREEN = "\x1b[38;2;80;220;100m";
26
+ const ANSI_FG_RESET = "\x1b[39m";
27
+
28
+ /** 事件上下文中同步的最新主题(与 toolGrouping.setTheme 同一来源)。 */
29
+ let displayTheme: any;
30
+
31
+ export function setMessageDisplayTheme(theme: any): void {
32
+ displayTheme = theme;
33
+ }
34
+
35
+ /** 供工具展开面板背景等共享同一主题来源(ctx.ui.theme)。 */
36
+ export function getMessageDisplayTheme(): any {
37
+ return displayTheme;
38
+ }
39
+
40
+ type DisplayKind = {
41
+ title(component: any): string;
42
+ body(component: any): string;
43
+ };
44
+
45
+ const SKILL_KIND: DisplayKind = {
46
+ title: (component) =>
47
+ `Skill ${String(component.skillBlock?.name ?? "skill")}`,
48
+ body: (component) => String(component.skillBlock?.content ?? ""),
49
+ };
50
+
51
+ const COMPACTION_KIND: DisplayKind = {
52
+ title: (component) =>
53
+ `Compacted from ${Number(component.message?.tokensBefore ?? 0).toLocaleString()} tokens`,
54
+ body: (component) => String(component.message?.summary ?? ""),
55
+ };
56
+
57
+ const BRANCH_KIND: DisplayKind = {
58
+ title: () => "Branch summary",
59
+ body: (component) => String(component.message?.summary ?? ""),
60
+ };
61
+
62
+ function ensureHintHover(component: any): void {
63
+ if (typeof component.setHintHovered === "function") return;
64
+ component.setHintHovered = function (this: any, hovered: boolean) {
65
+ if (this.hintHovered === hovered) return;
66
+ this.hintHovered = hovered;
67
+ this.invalidate?.();
68
+ };
69
+ }
70
+
71
+ function renderCcstyle(component: any, kind: DisplayKind): void {
72
+ const theme = displayTheme;
73
+ if (!theme) return; // 主题未就绪时保留原生渲染
74
+ if (component.bgFn) component.setBgFn?.(undefined); // 与工具调用一致,去掉灰底
75
+ if (component.paddingY !== 0) {
76
+ component._ccstyleOriginalPaddingY = component.paddingY;
77
+ component.paddingY = 0; // 工具组件 paddingY=0;原生 Box 默认 1,会上下各留一个空行
78
+ }
79
+ component.clear();
80
+ ensureHintHover(component);
81
+ const icon = `${BRIGHT_GREEN}✓${ANSI_FG_RESET}`; // 已完成消息,等同工具成功态
82
+ const title = theme.fg("toolTitle", kind.title(component));
83
+ if (!component.expanded) {
84
+ const hovered = component.hintHovered === true;
85
+ const hint = `${theme.fg("dim", " • ")}${theme.fg(hovered ? "text" : "dim", showMoreHintText())}`;
86
+ component.addChild(new Text(`${icon} ${title}${hint}`, 0, 0));
87
+ return;
88
+ }
89
+ // 展开卡与 tool 一致:userMessageBg + 上下左右 1 格
90
+ component.paddingX = 1;
91
+ component.paddingY = 1;
92
+ if (
93
+ typeof theme.bg === "function" &&
94
+ typeof component.setBgFn === "function"
95
+ ) {
96
+ component.setBgFn((text: string) => theme.bg("userMessageBg", text));
97
+ }
98
+ component.addChild(new Text(`${icon} ${title}`, 0, 0));
99
+ component.addChild(new Spacer(1));
100
+ component.addChild(
101
+ new Markdown(kind.body(component), 0, 0, component.markdownTheme, {
102
+ color: (text: string) => theme.fg("customMessageText", text),
103
+ }),
104
+ );
105
+ }
106
+
107
+ type PatchEntry = {
108
+ prototype: any;
109
+ installed: (...args: any[]) => void;
110
+ original: (...args: any[]) => void;
111
+ };
112
+
113
+ /** patch 三个消息组件的 updateDisplay,返回统一 dispose(/reload 链安全)。 */
114
+ export function installMessageDisplayRendering(): () => void {
115
+ const previous = patchRegistry.get<{ dispose: () => void }>(
116
+ MESSAGE_DISPLAY_PATCH,
117
+ );
118
+ if (previous) previous.dispose();
119
+ const patch: { active: boolean; entries: PatchEntry[]; dispose: () => void } =
120
+ {
121
+ active: true,
122
+ entries: [],
123
+ dispose: () => {},
124
+ };
125
+ const installOne = (ComponentClass: any, kind: DisplayKind): void => {
126
+ const prototype = ComponentClass.prototype;
127
+ const original = prototype.updateDisplay;
128
+ const installed = function (this: any) {
129
+ if (patch.active && config.mode !== "off") {
130
+ try {
131
+ renderCcstyle(this, kind);
132
+ return;
133
+ } catch {
134
+ // 渲染失败回退原生
135
+ }
136
+ }
137
+ // 回退原生前恢复 paddingY,避免原生渲染丢失上下内边距
138
+ if (this._ccstyleOriginalPaddingY !== undefined) {
139
+ this.paddingY = this._ccstyleOriginalPaddingY;
140
+ delete this._ccstyleOriginalPaddingY;
141
+ }
142
+ original.call(this);
143
+ };
144
+ prototype.updateDisplay = installed;
145
+ patch.entries.push({ prototype, installed, original });
146
+ };
147
+ installOne(SkillInvocationMessageComponent, SKILL_KIND);
148
+ installOne(CompactionSummaryMessageComponent, COMPACTION_KIND);
149
+ installOne(BranchSummaryMessageComponent, BRANCH_KIND);
150
+ patch.dispose = () => {
151
+ patch.active = false;
152
+ for (const entry of patch.entries) {
153
+ if (entry.prototype.updateDisplay === entry.installed) {
154
+ entry.prototype.updateDisplay = entry.original;
155
+ }
156
+ }
157
+ patchRegistry.dispose(MESSAGE_DISPLAY_PATCH, patch);
158
+ };
159
+ patchRegistry.install(MESSAGE_DISPLAY_PATCH, patch);
160
+ return patch.dispose;
161
+ }
162
+
163
+ /** 三个消息组件共享的结构特征;CustomMessageComponent 用 rebuild 无 updateDisplay,不命中。 */
164
+ export function isMessageDisplayComponent(value: any): boolean {
165
+ return Boolean(
166
+ value &&
167
+ typeof value === "object" &&
168
+ typeof value.markdownTheme === "object" &&
169
+ typeof value.setExpanded === "function" &&
170
+ typeof value.updateDisplay === "function" &&
171
+ typeof value.clear === "function",
172
+ );
173
+ }
174
+
175
+ /** 遍历当前 transcript,让已挂载的消息组件按当前 mode 重渲染(/ccstyle on|off 切换)。 */
176
+ export function refreshMessageDisplays(root: any): void {
177
+ walkComponentTree(root, (value: any) => {
178
+ if (isMessageDisplayComponent(value)) {
179
+ value.invalidate?.();
180
+ return false;
181
+ }
182
+ });
183
+ }
@@ -0,0 +1,195 @@
1
+ import { config } from "../../../app/config/renderer.ts";
2
+ import { oneLine } from "../../../tools/format.ts";
3
+
4
+ function clip(value: unknown): string {
5
+ return oneLine(value, config.toolInputNameLength);
6
+ }
7
+
8
+ /**
9
+ * 工具名/标签人性化:与 default-mode 的 humanizeToolLabel、grouping 的 humanizeToolName
10
+ * 逐字相同,收敛为一个共享实现。
11
+ */
12
+ export function humanizeToolLabel(label: string): string {
13
+ return label
14
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
15
+ .replace(/[_-]+/g, " ")
16
+ .replace(/\b\w/g, (char) => char.toUpperCase());
17
+ }
18
+
19
+ const AGENT_FAMILY_TOOL_NAMES = new Set([
20
+ "Agent",
21
+ "Agents",
22
+ "get_subagent_result",
23
+ "steer_subagent",
24
+ ]);
25
+
26
+ /** default-mode(单工具卡)与 grouping(分组卡)在 agent/bash/grep/find/read/fallback 文案上不同。 */
27
+ export type ToolCallSummaryVariant = "default" | "grouping";
28
+
29
+ export type ToolCallSummaryOptions = {
30
+ /** 已解析标题;缺省为 humanizeToolLabel(toolName)。 */
31
+ title?: string;
32
+ /** 文案变体;缺省 "default"。 */
33
+ variant?: ToolCallSummaryVariant;
34
+ };
35
+
36
+ /**
37
+ * 单工具调用摘要({ main, detail })。
38
+ *
39
+ * default-mode 与 grouping 共用;opts.variant 保留两处各自逐字一致的输出,
40
+ * 不改动任何现有渲染字符串。
41
+ */
42
+ export function toolCallSummary(
43
+ toolName: string,
44
+ args: any,
45
+ opts: ToolCallSummaryOptions = {},
46
+ ): { main: string; detail: string } {
47
+ const title = opts.title ?? humanizeToolLabel(toolName);
48
+ const variant = opts.variant ?? "default";
49
+ if (!args || typeof args !== "object") return { main: title, detail: "" };
50
+ const name = toolName.toLowerCase();
51
+ const value = (fallback: string, ...keys: string[]) => {
52
+ const found = keys
53
+ .map((key) => args[key])
54
+ .find((item) => typeof item === "string" && item);
55
+ return `${title} ${clip(found || fallback)}`;
56
+ };
57
+
58
+ if (
59
+ variant === "default" &&
60
+ AGENT_FAMILY_TOOL_NAMES.has(toolName) &&
61
+ args.agent_id
62
+ ) {
63
+ return { main: `${title} ${clip(args.agent_id)}`, detail: "" };
64
+ }
65
+ if (variant === "grouping" && (name === "agent" || name === "agents")) {
66
+ const displayName = args.subagent_type ?? args.agent_type ?? args.agent;
67
+ if (typeof displayName === "string" && displayName) {
68
+ return { main: `${title} ${displayName}`, detail: "" };
69
+ }
70
+ return {
71
+ main: value(
72
+ name === "agent" ? "launch agent" : "launch agents",
73
+ "description",
74
+ "prompt",
75
+ ),
76
+ detail: "",
77
+ };
78
+ }
79
+ if (
80
+ variant === "grouping" &&
81
+ (name === "get_subagent_result" || name === "steer_subagent")
82
+ ) {
83
+ return {
84
+ main: value(
85
+ name === "get_subagent_result" ? "agent result" : "steer agent",
86
+ "agent_id",
87
+ ),
88
+ detail: "",
89
+ };
90
+ }
91
+ if (variant === "default" && name === "agents") {
92
+ return {
93
+ main: value("launch agents", "description", "prompt"),
94
+ detail: "",
95
+ };
96
+ }
97
+ if (name === "skill") return { main: value("run skill", "name"), detail: "" };
98
+ if (name === "enterplanmode" || name === "enter_plan_mode") {
99
+ return { main: `${title} enable read-only planning`, detail: "" };
100
+ }
101
+ if (name === "exitplanmode" || name === "exit_plan_mode") {
102
+ return { main: `${title} present plan`, detail: "" };
103
+ }
104
+ if (name === "taskcreate")
105
+ return { main: value("create task", "subject"), detail: "" };
106
+ if (name === "tasklist") return { main: `${title} task list`, detail: "" };
107
+ if (name === "taskget" || name === "taskupdate") {
108
+ return { main: value("task", "taskId", "task_id"), detail: "" };
109
+ }
110
+ if (name === "taskoutput" || name === "taskstop") {
111
+ return { main: value("background task", "task_id", "taskId"), detail: "" };
112
+ }
113
+ if (name === "taskexecute") {
114
+ const ids = Array.isArray(args.task_ids)
115
+ ? args.task_ids
116
+ : Array.isArray(args.taskIds)
117
+ ? args.taskIds
118
+ : [];
119
+ const summary = ids.length
120
+ ? `${ids[0]}${ids.length > 1 ? ` (+${ids.length - 1} tasks)` : ""}`
121
+ : "start tasks";
122
+ return { main: `${title} ${summary}`, detail: "" };
123
+ }
124
+ if (toolName === "read") {
125
+ const details = [
126
+ args.offset !== undefined ? `offset=${args.offset}` : "",
127
+ args.limit !== undefined ? `limit=${args.limit}` : "",
128
+ ].filter(Boolean);
129
+ if (variant === "grouping") {
130
+ return {
131
+ main: `Read ${clip(args.path || "...")}`,
132
+ detail: details.length ? ` (${details.join(", ")})` : "",
133
+ };
134
+ }
135
+ return {
136
+ main: `${title}${args.path ? ` ${clip(args.path)}` : ""}`,
137
+ detail: details.length ? ` (${details.join(", ")})` : "",
138
+ };
139
+ }
140
+ if (variant === "grouping") {
141
+ if (toolName === "bash")
142
+ return { main: `Bash ${clip(args.command || "...")}`, detail: "" };
143
+ if (toolName === "grep") {
144
+ const pattern = clip(args.pattern || "...");
145
+ return {
146
+ main: `Grep ${JSON.stringify(pattern)}${args.path ? ` in ${clip(args.path)}` : ""}`,
147
+ detail: "",
148
+ };
149
+ }
150
+ if (toolName === "find") {
151
+ const pattern = clip(args.pattern || "...");
152
+ return {
153
+ main: `Find ${JSON.stringify(pattern)}${args.path ? ` in ${clip(args.path)}` : ""}`,
154
+ detail: "",
155
+ };
156
+ }
157
+ }
158
+ if (variant === "default") {
159
+ const preferred =
160
+ args.path ??
161
+ args.file_path ??
162
+ args.command ??
163
+ args.query ??
164
+ args.question ??
165
+ args.pattern ??
166
+ args.url ??
167
+ args.name ??
168
+ args.tool_use_id ??
169
+ args.toolCallId ??
170
+ args.id ??
171
+ args.message;
172
+ return {
173
+ main:
174
+ preferred !== undefined &&
175
+ preferred !== null &&
176
+ typeof preferred !== "object"
177
+ ? `${title} ${clip(preferred)}`
178
+ : title,
179
+ detail: "",
180
+ };
181
+ }
182
+ const preferred =
183
+ args.agent_id ??
184
+ args.path ??
185
+ args.file_path ??
186
+ args.url ??
187
+ args.description ??
188
+ args.query ??
189
+ args.name ??
190
+ args.prompt;
191
+ return {
192
+ main: `${title}${preferred === undefined ? "" : ` ${clip(preferred)}`}`,
193
+ detail: "",
194
+ };
195
+ }