pi-my-compact-ui 0.2.2 → 0.4.0

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.
@@ -0,0 +1,19 @@
1
+ import type { ThinkingEntry, ToolStatus } from "./types.js";
2
+ export declare function shortenPath(path: string): string;
3
+ export declare function oneLine(value: unknown, max?: number): string;
4
+ export declare function estimateTextTokens(text: string): number;
5
+ export declare function formatTokenK(tokens: number): string;
6
+ export declare function thinkingTokenLabel(entry: ThinkingEntry): string;
7
+ export declare function toolSummary(name: string, args: any): {
8
+ name: string;
9
+ content: string;
10
+ };
11
+ export declare function toolStatus(tool: any): ToolStatus;
12
+ export declare function streamingToolArgs(block: any): any;
13
+ export declare function locateStreamingToolCall(content: any[], streamEvent: any): {
14
+ index: number;
15
+ block: any;
16
+ } | undefined;
17
+ export declare function toolResultText(tool: any): string;
18
+ export declare function formatWorkedTime(elapsedMs: number): string;
19
+ export declare function isCtrlI(data: string): boolean;
@@ -0,0 +1,7 @@
1
+ import type { MarkdownTheme, Markdown } from "@earendil-works/pi-tui";
2
+ export declare function isCompactCodeBlockOpen(visible: string): boolean;
3
+ export declare function isCompactCodeBlockClose(visible: string): boolean;
4
+ export declare function getCompactMarkdownTheme(): MarkdownTheme;
5
+ export declare function renderCodeBlockBackgroundRow(content: string, width: number): string;
6
+ export declare function normalizeCompactCodeBlockLines(lines: string[], width: number, paddingX?: number): string[];
7
+ export declare function installVisibleAssistantMarkdownRendering(component: Markdown): void;
@@ -0,0 +1,38 @@
1
+ import type { Component } from "@earendil-works/pi-tui";
2
+ import type { GroupEntry } from "./types.js";
3
+ export declare class DetailModalComponent implements Component {
4
+ readonly entry: GroupEntry;
5
+ private scrollTop;
6
+ private followingEnd;
7
+ private lastContentLineCount;
8
+ private lastInnerHeight;
9
+ private pollTimer;
10
+ constructor(entry: GroupEntry);
11
+ private isEntryActive;
12
+ private startPollingIfNeeded;
13
+ dispose(): void;
14
+ invalidate(): void;
15
+ scrollBy(delta: number): void;
16
+ scrollTo(newTop: number): void;
17
+ scrollToEnd(): void;
18
+ handleInput(data: string): boolean;
19
+ render(width: number): string[];
20
+ }
21
+ export declare function openDetailModal(entry: GroupEntry): void;
22
+ export declare function closeDetailModal(): void;
23
+ export declare function getAllInspectableEntries(): GroupEntry[];
24
+ export declare class InspectSelectorModal implements Component {
25
+ private entries;
26
+ private selectedIndex;
27
+ private scrollTop;
28
+ private visibleHeight;
29
+ constructor();
30
+ refreshEntries(isInitial?: boolean): void;
31
+ private adjustScroll;
32
+ handleInput(data: string): boolean;
33
+ render(width: number): string[];
34
+ dispose(): void;
35
+ invalidate(): void;
36
+ }
37
+ export declare function openInspectSelectorModal(): void;
38
+ export declare function closeInspectSelectorModal(): void;
@@ -0,0 +1,31 @@
1
+ /** 配置交互界面与数值编辑器。 */
2
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ import type { Component } from "@earendil-works/pi-tui";
4
+ export declare const CONFIG_KEYS: readonly [{
5
+ readonly id: "expandedToolLines";
6
+ readonly label: "Expanded tool lines";
7
+ readonly description: "Result lines shown per tool when expanded";
8
+ readonly min: 1;
9
+ readonly max: 50;
10
+ readonly step: 1;
11
+ }, {
12
+ readonly id: "expandedThinkingLines";
13
+ readonly label: "Expanded thinking lines";
14
+ readonly description: "Thinking lines shown when expanded";
15
+ readonly min: 1;
16
+ readonly max: 100;
17
+ readonly step: 1;
18
+ }, {
19
+ readonly id: "maxGroupEntries";
20
+ readonly label: "Max collapsed entries";
21
+ readonly description: "Maximum entries shown in collapsed group";
22
+ readonly min: 1;
23
+ readonly max: 30;
24
+ readonly step: 1;
25
+ }];
26
+ export declare function makeStepper(title: string, initial: number, meta: {
27
+ min: number;
28
+ max: number;
29
+ step: number;
30
+ }, theme: any, done: (value?: string) => void): Component;
31
+ export declare function registerConfigCommand(pi: ExtensionAPI): void;
@@ -0,0 +1,45 @@
1
+ /** 集中管理会话状态;集合与锚点通过工厂创建,便于测试隔离。 */
2
+ import type { ThinkingEntry, PreparingTool, AssistantContentState } from "./types.js";
3
+ import type { ToolGroupComponent } from "./tool-group.js";
4
+ import type { OverlayHandle, Container } from "@earendil-works/pi-tui";
5
+ import type { DetailModalComponent, InspectSelectorModal } from "./modals.js";
6
+ /** Mutable session/UI state. A fresh snapshot also makes isolated tests possible. */
7
+ export interface RuntimeState {
8
+ currentTheme: any;
9
+ activeThinking: ThinkingEntry | null;
10
+ activeThinkingIndex: number | null;
11
+ activeThinkingBlocks: Map<number, string>;
12
+ unattachedThinking: ThinkingEntry[];
13
+ thinkingEntrySeq: number;
14
+ handledTextIndexes: Set<number>;
15
+ sealedThinkingIndexes: Set<number>;
16
+ pendingTextSeal: boolean;
17
+ pendingTextOrdinal: number | null;
18
+ lastActiveGroup: ToolGroupComponent | null;
19
+ lastSealedThinkingEntry: ThinkingEntry | null;
20
+ lastReportedReasoningTokens: number;
21
+ lastStreamingComp: any;
22
+ lastChatContainer: any;
23
+ toolStarts: Map<string, number>;
24
+ toolDurations: Map<string, number>;
25
+ turnStartMs: number;
26
+ preparingByIndex: Map<number, PreparingTool>;
27
+ activeModalHandle: OverlayHandle | null;
28
+ activeModalComponent: DetailModalComponent | null;
29
+ activeModalCloseFn: (() => void) | null;
30
+ activeUIContext: any;
31
+ activeSelectorHandle: OverlayHandle | null;
32
+ activeSelectorComponent: InspectSelectorModal | null;
33
+ activeSelectorCloseFn: (() => void) | null;
34
+ animTimer: ReturnType<typeof setTimeout> | null;
35
+ capturedTui: any;
36
+ groups: Set<ToolGroupComponent>;
37
+ assistantContentContainers: WeakSet<Container>;
38
+ assistantContentStates: WeakMap<Container, AssistantContentState>;
39
+ groupAnchors: WeakMap<ToolGroupComponent, {
40
+ container: Container;
41
+ ordinal: number;
42
+ }>;
43
+ }
44
+ export declare function createRuntimeState(): RuntimeState;
45
+ export declare const runtime: RuntimeState;
@@ -0,0 +1,16 @@
1
+ import { ToolGroupComponent } from "./tool-group.js";
2
+ import type { PreparingTool } from "./types.js";
3
+ export declare function toolElapsed(tool: any): string;
4
+ export declare function rememberToolStart(toolCallId: string): void;
5
+ export declare function loadSessionToolTimings(entries: readonly any[]): void;
6
+ export declare function findGroupedTool(toolCallId: string, contentIndex: number): any | undefined;
7
+ export declare function invalidateGroupedTool(tool: any): void;
8
+ export declare function findOpenGroup(): ToolGroupComponent | null;
9
+ export declare function placeGroupAfter(parent: any, after: any, group: ToolGroupComponent): void;
10
+ export declare function openToolGroup(): ToolGroupComponent | null;
11
+ export declare function attachPreparingTool(tool: PreparingTool): void;
12
+ export declare function detachPreparingTool(tool: PreparingTool): void;
13
+ export declare function takePreparingTool(real: any): PreparingTool | undefined;
14
+ export declare function absorbPreparingTool(real: any): void;
15
+ export declare function clearPreparingTools(): void;
16
+ export declare function noteStreamingToolCall(content: any[], streamEvent: any): void;
@@ -0,0 +1,5 @@
1
+ import type { ThinkingEntry } from "./types.js";
2
+ export declare function updateActiveThinkingTokens(message: any): void;
3
+ export declare function createThinkingEntry(): ThinkingEntry;
4
+ export declare function finalizeActiveThinking(): void;
5
+ export declare function refreshSealedThinkingTokens(reportedReasoning?: unknown): void;
@@ -0,0 +1,41 @@
1
+ /** 按照原始条目顺序渲染折叠分组与有界 Markdown 预览。 */
2
+ import { Container } from "@earendil-works/pi-tui";
3
+ import type { GroupEntry, ThinkingEntry } from "./types.js";
4
+ export declare class ToolGroupComponent extends Container {
5
+ readonly toolCallId: string;
6
+ toolName: string;
7
+ /** Nested at a visible-text boundary rather than rendered at chat level. */
8
+ anchored: boolean;
9
+ private _expanded;
10
+ get expanded(): boolean;
11
+ /** Sealed: this block was closed by real text output — no further entries. */
12
+ sealed: boolean;
13
+ /** Thinking runs and tool calls, in the exact order they streamed in. */
14
+ entries: GroupEntry[];
15
+ private markdownPreviewCache;
16
+ constructor();
17
+ setExpanded(expanded: boolean): void;
18
+ addTool(tool: any): void;
19
+ /** Append a thinking run. Idempotent: a run belongs to exactly one group. */
20
+ addThinking(entry: ThinkingEntry): void;
21
+ removeTool(tool: any): void;
22
+ /** Return only entries that should be displayed (tools, or thinking with non-empty text). */
23
+ getVisibleEntries(): GroupEntry[];
24
+ hasVisibleEntries(): boolean;
25
+ hasPending(): boolean;
26
+ /** True while a thinking run in this group is still streaming. */
27
+ hasActiveThinking(): boolean;
28
+ /** True once any thinking run has been added to this group. */
29
+ hasThinking(): boolean;
30
+ /** True while this group should keep its spinner animating. */
31
+ needsAnimation(): boolean;
32
+ invalidate(): void;
33
+ private renderMarkdownPreview;
34
+ private iconFor;
35
+ private colorFor;
36
+ private toolRow;
37
+ private thinkingRow;
38
+ private renderCollapsed;
39
+ private renderExpanded;
40
+ render(width: number): string[];
41
+ }
@@ -0,0 +1,59 @@
1
+ /** 思考、工具、预览与组件补丁的共享类型。 */
2
+ import type { ToolGroupComponent } from "./tool-group.js";
3
+ export type ThinkingEntry = {
4
+ kind: "thinking";
5
+ id: number;
6
+ text: string;
7
+ tokens: number;
8
+ tokensExact: boolean;
9
+ /** Still streaming — renders the animated spinner instead of the done mark. */
10
+ active: boolean;
11
+ /** Group currently owning this entry, used for targeted invalidation. */
12
+ owner: ToolGroupComponent | null;
13
+ };
14
+ export type GroupEntry = {
15
+ kind: "tool";
16
+ tool: any;
17
+ } | ThinkingEntry;
18
+ export type ToolStatus = "pending" | "success" | "error";
19
+ export type PreparingTool = {
20
+ toolName: string;
21
+ toolCallId: string;
22
+ args: any;
23
+ isPartial: true;
24
+ _preparing: true;
25
+ _contentIndex: number;
26
+ owner: ToolGroupComponent | null;
27
+ };
28
+ export type MarkdownPreview = {
29
+ source: string;
30
+ width: number;
31
+ maxLines: number;
32
+ lines: string[];
33
+ truncated: boolean;
34
+ };
35
+ export type PatchState = {
36
+ active: boolean;
37
+ original: {
38
+ addChild: Function;
39
+ removeChild: Function;
40
+ clear: Function;
41
+ };
42
+ installed: {
43
+ addChild: Function;
44
+ removeChild: Function;
45
+ clear: Function;
46
+ };
47
+ prototype: any;
48
+ };
49
+ export type AssistantContentState = {
50
+ /** Sealed groups keyed by the visible Markdown block they precede. */
51
+ anchors: Map<number, ToolGroupComponent>;
52
+ /** Visible Markdown ordinal while AssistantMessageComponent rebuilds. */
53
+ nextTextOrdinal: number;
54
+ /** Turn-duration divider bound to the final visible Markdown ordinal. */
55
+ finalDivider?: {
56
+ ordinal: number;
57
+ component: any;
58
+ };
59
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-my-compact-ui",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Compact tree-shaped reasoning and tool-call UI for the Pi Coding Agent",
5
5
  "author": "cybershape",
6
6
  "license": "MIT",
@@ -17,11 +17,18 @@
17
17
  "terminal-ui",
18
18
  "tui"
19
19
  ],
20
- "exports": "./index.ts",
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "default": "./dist/index.js"
26
+ }
27
+ },
21
28
  "files": [
22
29
  "LICENSE",
23
30
  "README.md",
24
- "index.ts"
31
+ "dist"
25
32
  ],
26
33
  "publishConfig": {
27
34
  "access": "public",
@@ -29,12 +36,14 @@
29
36
  },
30
37
  "pi": {
31
38
  "extensions": [
32
- "./index.ts"
39
+ "./dist/index.js"
33
40
  ]
34
41
  },
35
42
  "scripts": {
43
+ "build": "esbuild index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --packages=external --sourcemap && tsc -p tsconfig.build.json",
36
44
  "typecheck": "tsc --noEmit",
37
- "check": "npm run typecheck",
45
+ "test": "npm run build && tsc -p tsconfig.test.json && node --test .test-build/tests/*.test.js",
46
+ "check": "npm run typecheck && npm test",
38
47
  "prepublishOnly": "npm run check"
39
48
  },
40
49
  "peerDependencies": {
@@ -45,6 +54,7 @@
45
54
  "@earendil-works/pi-coding-agent": "^0.87.1",
46
55
  "@earendil-works/pi-tui": "^0.87.1",
47
56
  "@types/node": "^22.20.4",
57
+ "esbuild": "^0.28.2",
48
58
  "typescript": "^5.9.3"
49
59
  }
50
60
  }