@xopcai/xopc 0.0.28 → 0.0.29
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/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-CkgFSiCY.js +216 -0
- package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-Co95hLOJ.js → apps-page-Bmq19MS-.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-Co95hLOJ.js.map → apps-page-Bmq19MS-.js.map} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +9 -0
- package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +1 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +2 -0
- package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +1 -0
- package/dist/gateway/static/root/assets/{cron-utils-BmzF4m1y.js → cron-utils-N1PqD2DB.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-BmzF4m1y.js.map → cron-utils-N1PqD2DB.js.map} +1 -1
- package/dist/gateway/static/root/assets/{dist-Dn-ufXyc.js → dist--p2HQ2QF.js} +2 -2
- package/dist/gateway/static/root/assets/{dist-Dn-ufXyc.js.map → dist--p2HQ2QF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BZ8xQ74_.js → extension-debug-page-DwHCB_6T.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-BZ8xQ74_.js.map → extension-debug-page-DwHCB_6T.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BlNgKxwW.js → extension-page-BsYwQIex.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-BlNgKxwW.js.map → extension-page-BsYwQIex.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CWTdW_oY.js → extension-settings-page-nsisEgjB.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-CWTdW_oY.js.map → extension-settings-page-nsisEgjB.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-CR8zUHGR.js +4734 -0
- package/dist/gateway/static/root/assets/{index-lV8FGWlt.js.map → index-CR8zUHGR.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Dnfha4O2.css +1 -0
- package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +2 -0
- package/dist/gateway/static/root/assets/{logs-page-DG31RpvG.js.map → logs-page-CQwdV_Xw.js.map} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +2 -0
- package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js.map +1 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +1 -0
- package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +3 -0
- package/dist/gateway/static/root/assets/{skills-page-lb7vYtlP.js.map → skills-page-Clg8deH0.js.map} +1 -1
- package/dist/gateway/static/root/index.html +2 -2
- package/dist/package.js +1 -1
- package/dist/src/agent/lifecycle/hook-handler.d.ts +2 -0
- package/dist/src/agent/lifecycle/hook-handler.js +24 -0
- package/dist/src/agent/lifecycle/hook-handler.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.js +10 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +77 -20
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +15 -0
- package/dist/src/agent/service.js +21 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/manager.js +2 -2
- package/dist/src/cli/agent-chat-log-level-preset.d.ts +3 -2
- package/dist/src/cli/agent-chat-log-level-preset.js +6 -3
- package/dist/src/cli/agent-chat-log-level-preset.js.map +1 -1
- package/dist/src/cli/index.js +4 -3
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/schema.js +5 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/hooks.js +5 -1
- package/dist/src/extensions/hooks.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +1 -0
- package/dist/src/extensions/loader.js +3 -1
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/core.d.ts +8 -0
- package/dist/src/extensions/types/hooks.d.ts +16 -1
- package/dist/src/extensions/types/hooks.js +1 -0
- package/dist/src/extensions/types/hooks.js.map +1 -1
- package/dist/src/gateway/agents-admin.d.ts +19 -1
- package/dist/src/gateway/agents-admin.js +164 -3
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/app.js +1 -0
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +59 -5
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +2 -2
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +17 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +31 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/session/client-history.d.ts +21 -0
- package/dist/src/session/client-history.js +89 -0
- package/dist/src/session/client-history.js.map +1 -0
- package/dist/src/session/index.d.ts +1 -0
- package/dist/src/session/index.js +2 -1
- package/dist/src/session/manager.d.ts +2 -0
- package/dist/src/session/manager.js +5 -0
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/thinking-resolve.js +1 -1
- package/dist/src/session/thinking-resolve.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +1 -1
- package/dist/src/tui/backends/embedded-backend.js +15 -2
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +34 -4
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/chat-history.d.ts +4 -0
- package/dist/src/tui/chat-history.js +29 -0
- package/dist/src/tui/chat-history.js.map +1 -0
- package/dist/src/tui/components/chat-log.d.ts +3 -1
- package/dist/src/tui/components/chat-log.js +17 -3
- package/dist/src/tui/components/chat-log.js.map +1 -1
- package/dist/src/tui/components/custom-editor.d.ts +1 -0
- package/dist/src/tui/components/custom-editor.js +8 -2
- package/dist/src/tui/components/custom-editor.js.map +1 -1
- package/dist/src/tui/components/fuzzy-filter.d.ts +17 -0
- package/dist/src/tui/components/fuzzy-filter.js +85 -0
- package/dist/src/tui/components/fuzzy-filter.js.map +1 -0
- package/dist/src/tui/components/searchable-select-list.d.ts +39 -0
- package/dist/src/tui/components/searchable-select-list.js +257 -0
- package/dist/src/tui/components/searchable-select-list.js.map +1 -0
- package/dist/src/tui/theme.d.ts +2 -0
- package/dist/src/tui/theme.js +7 -1
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/tui-agent-events.d.ts +7 -0
- package/dist/src/tui/tui-agent-events.js +103 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -0
- package/dist/src/tui/tui-backend.d.ts +8 -12
- package/dist/src/tui/tui-commands.d.ts +23 -0
- package/dist/src/tui/tui-commands.js +165 -0
- package/dist/src/tui/tui-commands.js.map +1 -0
- package/dist/src/tui/tui-lifecycle.d.ts +26 -0
- package/dist/src/tui/tui-lifecycle.js +57 -0
- package/dist/src/tui/tui-lifecycle.js.map +1 -0
- package/dist/src/tui/tui-local-shell.d.ts +28 -0
- package/dist/src/tui/tui-local-shell.js +147 -0
- package/dist/src/tui/tui-local-shell.js.map +1 -0
- package/dist/src/tui/tui-overlays.d.ts +8 -0
- package/dist/src/tui/tui-overlays.js +22 -0
- package/dist/src/tui/tui-overlays.js.map +1 -0
- package/dist/src/tui/tui-picker-overlay.d.ts +26 -0
- package/dist/src/tui/tui-picker-overlay.js +69 -0
- package/dist/src/tui/tui-picker-overlay.js.map +1 -0
- package/dist/src/tui/tui-stdio-filter.d.ts +17 -0
- package/dist/src/tui/tui-stdio-filter.js +96 -0
- package/dist/src/tui/tui-stdio-filter.js.map +1 -0
- package/dist/src/tui/tui-submit.d.ts +25 -0
- package/dist/src/tui/tui-submit.js +102 -0
- package/dist/src/tui/tui-submit.js.map +1 -0
- package/dist/src/tui/tui-suspend.d.ts +10 -0
- package/dist/src/tui/tui-suspend.js +18 -0
- package/dist/src/tui/tui-suspend.js.map +1 -0
- package/dist/src/tui/tui-types.d.ts +1 -0
- package/dist/src/tui/tui-types.js.map +1 -1
- package/dist/src/tui/tui.d.ts +2 -0
- package/dist/src/tui/tui.js +175 -312
- package/dist/src/tui/tui.js.map +1 -1
- package/package.json +2 -6
- package/dist/gateway/static/root/assets/agents-DplaQYS2.js +0 -216
- package/dist/gateway/static/root/assets/agents-DplaQYS2.js.map +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CkfSST0k.js +0 -9
- package/dist/gateway/static/root/assets/channels-settings-CkfSST0k.js.map +0 -1
- package/dist/gateway/static/root/assets/cron-page-D9q6KqL8.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-D9q6KqL8.js.map +0 -1
- package/dist/gateway/static/root/assets/index-OT4cGzon.css +0 -1
- package/dist/gateway/static/root/assets/index-lV8FGWlt.js +0 -4734
- package/dist/gateway/static/root/assets/logs-page-DG31RpvG.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-CdmjxDEM.js +0 -2
- package/dist/gateway/static/root/assets/sessions-page-CdmjxDEM.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-DU2XLf5s.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-DU2XLf5s.js.map +0 -1
- package/dist/gateway/static/root/assets/skills-page-lb7vYtlP.js +0 -3
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Component, type SelectItem, type SelectListTheme } from '@mariozechner/pi-tui';
|
|
2
|
+
export interface SearchableSelectListTheme extends SelectListTheme {
|
|
3
|
+
searchPrompt: (text: string) => string;
|
|
4
|
+
searchInput: (text: string) => string;
|
|
5
|
+
matchHighlight: (text: string) => string;
|
|
6
|
+
}
|
|
7
|
+
export declare class SearchableSelectList implements Component {
|
|
8
|
+
private items;
|
|
9
|
+
private filteredItems;
|
|
10
|
+
private selectedIndex;
|
|
11
|
+
private maxVisible;
|
|
12
|
+
private theme;
|
|
13
|
+
private searchInput;
|
|
14
|
+
private regexCache;
|
|
15
|
+
onSelect?: (item: SelectItem) => void;
|
|
16
|
+
onCancel?: () => void;
|
|
17
|
+
onSelectionChange?: (item: SelectItem) => void;
|
|
18
|
+
private static readonly DESCRIPTION_LAYOUT_MIN_WIDTH;
|
|
19
|
+
private static readonly DESCRIPTION_MIN_WIDTH;
|
|
20
|
+
private static readonly DESCRIPTION_SPACING_WIDTH;
|
|
21
|
+
private static readonly RIGHT_MARGIN_WIDTH;
|
|
22
|
+
constructor(items: SelectItem[], maxVisible: number, theme: SearchableSelectListTheme);
|
|
23
|
+
private getCachedRegex;
|
|
24
|
+
private updateFilter;
|
|
25
|
+
private smartFilter;
|
|
26
|
+
private escapeRegex;
|
|
27
|
+
private compareByScore;
|
|
28
|
+
private getItemLabel;
|
|
29
|
+
private splitAnsiParts;
|
|
30
|
+
private highlightMatch;
|
|
31
|
+
setSelectedIndex(index: number): void;
|
|
32
|
+
invalidate(): void;
|
|
33
|
+
render(width: number): string[];
|
|
34
|
+
private renderItemLine;
|
|
35
|
+
private getDescriptionLayout;
|
|
36
|
+
handleInput(keyData: string): void;
|
|
37
|
+
private notifySelectionChange;
|
|
38
|
+
getSelectedItem(): SelectItem | null;
|
|
39
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { findWordBoundaryIndex, fuzzyFilterLower, normalizeLowercaseStringOrEmpty } from "./fuzzy-filter.js";
|
|
2
|
+
import { Input, isKeyRelease, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
3
|
+
//#region src/tui/components/searchable-select-list.ts
|
|
4
|
+
const ANSI_SGR_REGEX = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
5
|
+
function stripAnsi(raw) {
|
|
6
|
+
return raw.replace(/\x1b\[[0-9;]*m/g, "");
|
|
7
|
+
}
|
|
8
|
+
var SearchableSelectList = class SearchableSelectList {
|
|
9
|
+
items;
|
|
10
|
+
filteredItems;
|
|
11
|
+
selectedIndex = 0;
|
|
12
|
+
maxVisible;
|
|
13
|
+
theme;
|
|
14
|
+
searchInput;
|
|
15
|
+
regexCache = /* @__PURE__ */ new Map();
|
|
16
|
+
onSelect;
|
|
17
|
+
onCancel;
|
|
18
|
+
onSelectionChange;
|
|
19
|
+
static DESCRIPTION_LAYOUT_MIN_WIDTH = 40;
|
|
20
|
+
static DESCRIPTION_MIN_WIDTH = 12;
|
|
21
|
+
static DESCRIPTION_SPACING_WIDTH = 2;
|
|
22
|
+
static RIGHT_MARGIN_WIDTH = 2;
|
|
23
|
+
constructor(items, maxVisible, theme) {
|
|
24
|
+
this.items = items;
|
|
25
|
+
this.filteredItems = items;
|
|
26
|
+
this.maxVisible = maxVisible;
|
|
27
|
+
this.theme = theme;
|
|
28
|
+
this.searchInput = new Input();
|
|
29
|
+
}
|
|
30
|
+
getCachedRegex(pattern) {
|
|
31
|
+
let regex = this.regexCache.get(pattern);
|
|
32
|
+
if (!regex) {
|
|
33
|
+
regex = new RegExp(this.escapeRegex(pattern), "gi");
|
|
34
|
+
this.regexCache.set(pattern, regex);
|
|
35
|
+
}
|
|
36
|
+
return regex;
|
|
37
|
+
}
|
|
38
|
+
updateFilter() {
|
|
39
|
+
const query = this.searchInput.getValue().trim();
|
|
40
|
+
if (!query) this.filteredItems = this.items;
|
|
41
|
+
else this.filteredItems = this.smartFilter(query);
|
|
42
|
+
this.selectedIndex = 0;
|
|
43
|
+
this.notifySelectionChange();
|
|
44
|
+
}
|
|
45
|
+
smartFilter(query) {
|
|
46
|
+
const q = normalizeLowercaseStringOrEmpty(query);
|
|
47
|
+
const scoredItems = [];
|
|
48
|
+
const fuzzyCandidates = [];
|
|
49
|
+
for (const item of this.items) {
|
|
50
|
+
const rawLabel = this.getItemLabel(item);
|
|
51
|
+
const rawDesc = item.description ?? "";
|
|
52
|
+
const label = normalizeLowercaseStringOrEmpty(stripAnsi(rawLabel));
|
|
53
|
+
const desc = normalizeLowercaseStringOrEmpty(stripAnsi(rawDesc));
|
|
54
|
+
const labelIndex = label.indexOf(q);
|
|
55
|
+
if (labelIndex !== -1) {
|
|
56
|
+
scoredItems.push({
|
|
57
|
+
item,
|
|
58
|
+
tier: 0,
|
|
59
|
+
score: labelIndex
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const wordBoundaryIndex = findWordBoundaryIndex(label, q);
|
|
64
|
+
if (wordBoundaryIndex !== null) {
|
|
65
|
+
scoredItems.push({
|
|
66
|
+
item,
|
|
67
|
+
tier: 1,
|
|
68
|
+
score: wordBoundaryIndex
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const descIndex = desc.indexOf(q);
|
|
73
|
+
if (descIndex !== -1) {
|
|
74
|
+
scoredItems.push({
|
|
75
|
+
item,
|
|
76
|
+
tier: 2,
|
|
77
|
+
score: descIndex
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const searchText = item.searchText ?? "";
|
|
82
|
+
fuzzyCandidates.push({
|
|
83
|
+
item,
|
|
84
|
+
searchTextLower: normalizeLowercaseStringOrEmpty([
|
|
85
|
+
rawLabel,
|
|
86
|
+
rawDesc,
|
|
87
|
+
searchText
|
|
88
|
+
].map((value) => stripAnsi(value)).filter(Boolean).join(" "))
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
scoredItems.sort(this.compareByScore);
|
|
92
|
+
const fuzzyMatches = fuzzyFilterLower(fuzzyCandidates, q);
|
|
93
|
+
return [...scoredItems.map((s) => s.item), ...fuzzyMatches.map((entry) => entry.item)];
|
|
94
|
+
}
|
|
95
|
+
escapeRegex(str) {
|
|
96
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
97
|
+
}
|
|
98
|
+
compareByScore = (a, b) => {
|
|
99
|
+
if (a.tier !== b.tier) return a.tier - b.tier;
|
|
100
|
+
if (a.score !== b.score) return a.score - b.score;
|
|
101
|
+
return this.getItemLabel(a.item).localeCompare(this.getItemLabel(b.item));
|
|
102
|
+
};
|
|
103
|
+
getItemLabel(item) {
|
|
104
|
+
return item.label || item.value;
|
|
105
|
+
}
|
|
106
|
+
splitAnsiParts(text) {
|
|
107
|
+
const parts = [];
|
|
108
|
+
ANSI_SGR_REGEX.lastIndex = 0;
|
|
109
|
+
let lastIndex = 0;
|
|
110
|
+
let match;
|
|
111
|
+
while ((match = ANSI_SGR_REGEX.exec(text)) !== null) {
|
|
112
|
+
if (match.index > lastIndex) parts.push({
|
|
113
|
+
text: text.slice(lastIndex, match.index),
|
|
114
|
+
isAnsi: false
|
|
115
|
+
});
|
|
116
|
+
parts.push({
|
|
117
|
+
text: match[0],
|
|
118
|
+
isAnsi: true
|
|
119
|
+
});
|
|
120
|
+
lastIndex = match.index + match[0].length;
|
|
121
|
+
}
|
|
122
|
+
if (lastIndex < text.length) parts.push({
|
|
123
|
+
text: text.slice(lastIndex),
|
|
124
|
+
isAnsi: false
|
|
125
|
+
});
|
|
126
|
+
return parts;
|
|
127
|
+
}
|
|
128
|
+
highlightMatch(text, query) {
|
|
129
|
+
const tokens = query.trim().split(/\s+/).map((token) => normalizeLowercaseStringOrEmpty(token)).filter((token) => token.length > 0);
|
|
130
|
+
if (tokens.length === 0) return text;
|
|
131
|
+
const uniqueTokens = Array.from(new Set(tokens)).toSorted((a, b) => b.length - a.length);
|
|
132
|
+
let parts = this.splitAnsiParts(text);
|
|
133
|
+
for (const token of uniqueTokens) {
|
|
134
|
+
const regex = this.getCachedRegex(token);
|
|
135
|
+
const nextParts = [];
|
|
136
|
+
for (const part of parts) {
|
|
137
|
+
if (part.isAnsi) {
|
|
138
|
+
nextParts.push(part);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
regex.lastIndex = 0;
|
|
142
|
+
const replaced = part.text.replace(regex, (match) => this.theme.matchHighlight(match));
|
|
143
|
+
if (replaced === part.text) {
|
|
144
|
+
nextParts.push(part);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
nextParts.push(...this.splitAnsiParts(replaced));
|
|
148
|
+
}
|
|
149
|
+
parts = nextParts;
|
|
150
|
+
}
|
|
151
|
+
return parts.map((part) => part.text).join("");
|
|
152
|
+
}
|
|
153
|
+
setSelectedIndex(index) {
|
|
154
|
+
this.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));
|
|
155
|
+
}
|
|
156
|
+
invalidate() {
|
|
157
|
+
this.searchInput.invalidate();
|
|
158
|
+
}
|
|
159
|
+
render(width) {
|
|
160
|
+
const lines = [];
|
|
161
|
+
const prompt = this.theme.searchPrompt("search: ");
|
|
162
|
+
const inputWidth = Math.max(1, width - visibleWidth(prompt));
|
|
163
|
+
const inputText = this.searchInput.render(inputWidth)[0] ?? "";
|
|
164
|
+
lines.push(`${prompt}${this.theme.searchInput(inputText)}`);
|
|
165
|
+
lines.push("");
|
|
166
|
+
const query = this.searchInput.getValue().trim();
|
|
167
|
+
if (this.filteredItems.length === 0) {
|
|
168
|
+
lines.push(this.theme.noMatch(" No matches"));
|
|
169
|
+
return lines;
|
|
170
|
+
}
|
|
171
|
+
const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible));
|
|
172
|
+
const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);
|
|
173
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
174
|
+
const item = this.filteredItems[i];
|
|
175
|
+
if (!item) continue;
|
|
176
|
+
const isSelected = i === this.selectedIndex;
|
|
177
|
+
lines.push(this.renderItemLine(item, isSelected, width, query));
|
|
178
|
+
}
|
|
179
|
+
if (this.filteredItems.length > this.maxVisible) {
|
|
180
|
+
const scrollInfo = `${this.selectedIndex + 1}/${this.filteredItems.length}`;
|
|
181
|
+
lines.push(this.theme.scrollInfo(` ${scrollInfo}`));
|
|
182
|
+
}
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
185
|
+
renderItemLine(item, isSelected, width, query) {
|
|
186
|
+
const prefix = isSelected ? "→ " : " ";
|
|
187
|
+
const prefixWidth = prefix.length;
|
|
188
|
+
const displayValue = this.getItemLabel(item);
|
|
189
|
+
const description = item.description;
|
|
190
|
+
if (description) {
|
|
191
|
+
const descriptionLayout = this.getDescriptionLayout(width, prefixWidth);
|
|
192
|
+
if (descriptionLayout) {
|
|
193
|
+
const truncatedValue = truncateToWidth(displayValue, descriptionLayout.maxValueWidth, "");
|
|
194
|
+
const valueText = this.highlightMatch(truncatedValue, query);
|
|
195
|
+
const usedByValue = visibleWidth(valueText);
|
|
196
|
+
const descriptionWidth = descriptionLayout.availableWidth - usedByValue - descriptionLayout.spacingWidth;
|
|
197
|
+
if (descriptionWidth >= SearchableSelectList.DESCRIPTION_MIN_WIDTH) {
|
|
198
|
+
const spacing = " ".repeat(descriptionLayout.spacingWidth);
|
|
199
|
+
const truncatedDesc = truncateToWidth(description, descriptionWidth, "");
|
|
200
|
+
const highlightedDesc = this.highlightMatch(truncatedDesc, query);
|
|
201
|
+
const line = `${prefix}${valueText}${spacing}${isSelected ? highlightedDesc : this.theme.description(highlightedDesc)}`;
|
|
202
|
+
return isSelected ? this.theme.selectedText(line) : line;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const truncatedValue = truncateToWidth(displayValue, width - prefixWidth - 2, "");
|
|
207
|
+
const line = `${prefix}${this.highlightMatch(truncatedValue, query)}`;
|
|
208
|
+
return isSelected ? this.theme.selectedText(line) : line;
|
|
209
|
+
}
|
|
210
|
+
getDescriptionLayout(width, prefixWidth) {
|
|
211
|
+
if (width <= SearchableSelectList.DESCRIPTION_LAYOUT_MIN_WIDTH) return null;
|
|
212
|
+
const availableWidth = Math.max(1, width - prefixWidth - SearchableSelectList.RIGHT_MARGIN_WIDTH);
|
|
213
|
+
const maxValueWidth = availableWidth - SearchableSelectList.DESCRIPTION_MIN_WIDTH - SearchableSelectList.DESCRIPTION_SPACING_WIDTH;
|
|
214
|
+
if (maxValueWidth < 1) return null;
|
|
215
|
+
return {
|
|
216
|
+
availableWidth,
|
|
217
|
+
maxValueWidth,
|
|
218
|
+
spacingWidth: SearchableSelectList.DESCRIPTION_SPACING_WIDTH
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
handleInput(keyData) {
|
|
222
|
+
if (isKeyRelease(keyData)) return;
|
|
223
|
+
if (matchesKey(keyData, "up") || matchesKey(keyData, "ctrl+p")) {
|
|
224
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
|
225
|
+
this.notifySelectionChange();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (matchesKey(keyData, "down") || matchesKey(keyData, "ctrl+n")) {
|
|
229
|
+
this.selectedIndex = Math.min(this.filteredItems.length - 1, this.selectedIndex + 1);
|
|
230
|
+
this.notifySelectionChange();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (matchesKey(keyData, "enter")) {
|
|
234
|
+
const item = this.filteredItems[this.selectedIndex];
|
|
235
|
+
if (item && this.onSelect) this.onSelect(item);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (matchesKey(keyData, "escape") || keyData === "") {
|
|
239
|
+
if (this.onCancel) this.onCancel();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const prevValue = this.searchInput.getValue();
|
|
243
|
+
this.searchInput.handleInput(keyData);
|
|
244
|
+
if (prevValue !== this.searchInput.getValue()) this.updateFilter();
|
|
245
|
+
}
|
|
246
|
+
notifySelectionChange() {
|
|
247
|
+
const item = this.filteredItems[this.selectedIndex];
|
|
248
|
+
if (item && this.onSelectionChange) this.onSelectionChange(item);
|
|
249
|
+
}
|
|
250
|
+
getSelectedItem() {
|
|
251
|
+
return this.filteredItems[this.selectedIndex] ?? null;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
//#endregion
|
|
255
|
+
export { SearchableSelectList };
|
|
256
|
+
|
|
257
|
+
//# sourceMappingURL=searchable-select-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchable-select-list.js","names":[],"sources":["../../../../src/tui/components/searchable-select-list.ts"],"sourcesContent":["import {\n Input,\n isKeyRelease,\n matchesKey,\n type Component,\n type SelectItem,\n type SelectListTheme,\n truncateToWidth,\n visibleWidth,\n} from '@mariozechner/pi-tui';\n\nimport {\n findWordBoundaryIndex,\n fuzzyFilterLower,\n normalizeLowercaseStringOrEmpty,\n} from './fuzzy-filter.js';\n\nconst ANSI_ESCAPE = String.fromCharCode(27);\nconst ANSI_SGR_REGEX = new RegExp(`${ANSI_ESCAPE}\\\\[[0-9;]*m`, 'g');\n\nexport interface SearchableSelectListTheme extends SelectListTheme {\n searchPrompt: (text: string) => string;\n searchInput: (text: string) => string;\n matchHighlight: (text: string) => string;\n}\n\nfunction stripAnsi(raw: string): string {\n return raw.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n\nexport class SearchableSelectList implements Component {\n private items: SelectItem[];\n private filteredItems: SelectItem[];\n private selectedIndex = 0;\n private maxVisible: number;\n private theme: SearchableSelectListTheme;\n private searchInput: Input;\n private regexCache = new Map<string, RegExp>();\n\n onSelect?: (item: SelectItem) => void;\n onCancel?: () => void;\n onSelectionChange?: (item: SelectItem) => void;\n\n private static readonly DESCRIPTION_LAYOUT_MIN_WIDTH = 40;\n private static readonly DESCRIPTION_MIN_WIDTH = 12;\n private static readonly DESCRIPTION_SPACING_WIDTH = 2;\n private static readonly RIGHT_MARGIN_WIDTH = 2;\n\n constructor(items: SelectItem[], maxVisible: number, theme: SearchableSelectListTheme) {\n this.items = items;\n this.filteredItems = items;\n this.maxVisible = maxVisible;\n this.theme = theme;\n this.searchInput = new Input();\n }\n\n private getCachedRegex(pattern: string): RegExp {\n let regex = this.regexCache.get(pattern);\n if (!regex) {\n regex = new RegExp(this.escapeRegex(pattern), 'gi');\n this.regexCache.set(pattern, regex);\n }\n return regex;\n }\n\n private updateFilter() {\n const query = this.searchInput.getValue().trim();\n\n if (!query) {\n this.filteredItems = this.items;\n } else {\n this.filteredItems = this.smartFilter(query);\n }\n\n this.selectedIndex = 0;\n this.notifySelectionChange();\n }\n\n private smartFilter(query: string): SelectItem[] {\n const q = normalizeLowercaseStringOrEmpty(query);\n type ScoredItem = { item: SelectItem; tier: number; score: number };\n type FuzzyCandidate = { item: SelectItem; searchTextLower: string };\n const scoredItems: ScoredItem[] = [];\n const fuzzyCandidates: FuzzyCandidate[] = [];\n\n for (const item of this.items) {\n const rawLabel = this.getItemLabel(item);\n const rawDesc = item.description ?? '';\n const label = normalizeLowercaseStringOrEmpty(stripAnsi(rawLabel));\n const desc = normalizeLowercaseStringOrEmpty(stripAnsi(rawDesc));\n\n const labelIndex = label.indexOf(q);\n if (labelIndex !== -1) {\n scoredItems.push({ item, tier: 0, score: labelIndex });\n continue;\n }\n const wordBoundaryIndex = findWordBoundaryIndex(label, q);\n if (wordBoundaryIndex !== null) {\n scoredItems.push({ item, tier: 1, score: wordBoundaryIndex });\n continue;\n }\n const descIndex = desc.indexOf(q);\n if (descIndex !== -1) {\n scoredItems.push({ item, tier: 2, score: descIndex });\n continue;\n }\n const searchText = (item as { searchText?: string }).searchText ?? '';\n fuzzyCandidates.push({\n item,\n searchTextLower: normalizeLowercaseStringOrEmpty(\n [rawLabel, rawDesc, searchText]\n .map((value) => stripAnsi(value))\n .filter(Boolean)\n .join(' '),\n ),\n });\n }\n\n scoredItems.sort(this.compareByScore);\n const fuzzyMatches = fuzzyFilterLower(fuzzyCandidates, q);\n return [...scoredItems.map((s) => s.item), ...fuzzyMatches.map((entry) => entry.item)];\n }\n\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n private compareByScore = (\n a: { item: SelectItem; tier: number; score: number },\n b: { item: SelectItem; tier: number; score: number },\n ) => {\n if (a.tier !== b.tier) {\n return a.tier - b.tier;\n }\n if (a.score !== b.score) {\n return a.score - b.score;\n }\n return this.getItemLabel(a.item).localeCompare(this.getItemLabel(b.item));\n };\n\n private getItemLabel(item: SelectItem): string {\n return item.label || item.value;\n }\n\n private splitAnsiParts(text: string): Array<{ text: string; isAnsi: boolean }> {\n const parts: Array<{ text: string; isAnsi: boolean }> = [];\n ANSI_SGR_REGEX.lastIndex = 0;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = ANSI_SGR_REGEX.exec(text)) !== null) {\n if (match.index > lastIndex) {\n parts.push({ text: text.slice(lastIndex, match.index), isAnsi: false });\n }\n parts.push({ text: match[0], isAnsi: true });\n lastIndex = match.index + match[0].length;\n }\n if (lastIndex < text.length) {\n parts.push({ text: text.slice(lastIndex), isAnsi: false });\n }\n return parts;\n }\n\n private highlightMatch(text: string, query: string): string {\n const tokens = query\n .trim()\n .split(/\\s+/)\n .map((token) => normalizeLowercaseStringOrEmpty(token))\n .filter((token) => token.length > 0);\n if (tokens.length === 0) {\n return text;\n }\n\n const uniqueTokens = Array.from(new Set(tokens)).toSorted((a, b) => b.length - a.length);\n let parts = this.splitAnsiParts(text);\n for (const token of uniqueTokens) {\n const regex = this.getCachedRegex(token);\n const nextParts: Array<{ text: string; isAnsi: boolean }> = [];\n for (const part of parts) {\n if (part.isAnsi) {\n nextParts.push(part);\n continue;\n }\n regex.lastIndex = 0;\n const replaced = part.text.replace(regex, (match) => this.theme.matchHighlight(match));\n if (replaced === part.text) {\n nextParts.push(part);\n continue;\n }\n nextParts.push(...this.splitAnsiParts(replaced));\n }\n parts = nextParts;\n }\n return parts.map((part) => part.text).join('');\n }\n\n setSelectedIndex(index: number) {\n this.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));\n }\n\n invalidate() {\n this.searchInput.invalidate();\n }\n\n render(width: number): string[] {\n const lines: string[] = [];\n\n const promptText = 'search: ';\n const prompt = this.theme.searchPrompt(promptText);\n const inputWidth = Math.max(1, width - visibleWidth(prompt));\n const inputLines = this.searchInput.render(inputWidth);\n const inputText = inputLines[0] ?? '';\n lines.push(`${prompt}${this.theme.searchInput(inputText)}`);\n lines.push('');\n\n const query = this.searchInput.getValue().trim();\n\n if (this.filteredItems.length === 0) {\n lines.push(this.theme.noMatch(' No matches'));\n return lines;\n }\n\n const startIndex = Math.max(\n 0,\n Math.min(\n this.selectedIndex - Math.floor(this.maxVisible / 2),\n this.filteredItems.length - this.maxVisible,\n ),\n );\n const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n for (let i = startIndex; i < endIndex; i++) {\n const item = this.filteredItems[i];\n if (!item) {\n continue;\n }\n const isSelected = i === this.selectedIndex;\n lines.push(this.renderItemLine(item, isSelected, width, query));\n }\n\n if (this.filteredItems.length > this.maxVisible) {\n const scrollInfo = `${this.selectedIndex + 1}/${this.filteredItems.length}`;\n lines.push(this.theme.scrollInfo(` ${scrollInfo}`));\n }\n\n return lines;\n }\n\n private renderItemLine(\n item: SelectItem,\n isSelected: boolean,\n width: number,\n query: string,\n ): string {\n const prefix = isSelected ? '→ ' : ' ';\n const prefixWidth = prefix.length;\n const displayValue = this.getItemLabel(item);\n\n const description = item.description;\n if (description) {\n const descriptionLayout = this.getDescriptionLayout(width, prefixWidth);\n if (descriptionLayout) {\n const truncatedValue = truncateToWidth(displayValue, descriptionLayout.maxValueWidth, '');\n const valueText = this.highlightMatch(truncatedValue, query);\n\n const usedByValue = visibleWidth(valueText);\n const remainingWidth = descriptionLayout.availableWidth - usedByValue;\n const descriptionWidth = remainingWidth - descriptionLayout.spacingWidth;\n\n if (descriptionWidth >= SearchableSelectList.DESCRIPTION_MIN_WIDTH) {\n const spacing = ' '.repeat(descriptionLayout.spacingWidth);\n const truncatedDesc = truncateToWidth(description, descriptionWidth, '');\n const highlightedDesc = this.highlightMatch(truncatedDesc, query);\n const descText = isSelected ? highlightedDesc : this.theme.description(highlightedDesc);\n const line = `${prefix}${valueText}${spacing}${descText}`;\n return isSelected ? this.theme.selectedText(line) : line;\n }\n }\n }\n\n const maxWidth = width - prefixWidth - 2;\n const truncatedValue = truncateToWidth(displayValue, maxWidth, '');\n const valueText = this.highlightMatch(truncatedValue, query);\n const line = `${prefix}${valueText}`;\n return isSelected ? this.theme.selectedText(line) : line;\n }\n\n private getDescriptionLayout(\n width: number,\n prefixWidth: number,\n ): { availableWidth: number; maxValueWidth: number; spacingWidth: number } | null {\n if (width <= SearchableSelectList.DESCRIPTION_LAYOUT_MIN_WIDTH) {\n return null;\n }\n\n const availableWidth = Math.max(\n 1,\n width - prefixWidth - SearchableSelectList.RIGHT_MARGIN_WIDTH,\n );\n const maxValueWidth =\n availableWidth -\n SearchableSelectList.DESCRIPTION_MIN_WIDTH -\n SearchableSelectList.DESCRIPTION_SPACING_WIDTH;\n\n if (maxValueWidth < 1) {\n return null;\n }\n\n return {\n availableWidth,\n maxValueWidth,\n spacingWidth: SearchableSelectList.DESCRIPTION_SPACING_WIDTH,\n };\n }\n\n handleInput(keyData: string): void {\n if (isKeyRelease(keyData)) {\n return;\n }\n\n if (matchesKey(keyData, 'up') || matchesKey(keyData, 'ctrl+p')) {\n this.selectedIndex = Math.max(0, this.selectedIndex - 1);\n this.notifySelectionChange();\n return;\n }\n\n if (matchesKey(keyData, 'down') || matchesKey(keyData, 'ctrl+n')) {\n this.selectedIndex = Math.min(this.filteredItems.length - 1, this.selectedIndex + 1);\n this.notifySelectionChange();\n return;\n }\n\n if (matchesKey(keyData, 'enter')) {\n const item = this.filteredItems[this.selectedIndex];\n if (item && this.onSelect) {\n this.onSelect(item);\n }\n return;\n }\n\n if (matchesKey(keyData, 'escape') || keyData === '\\u0003') {\n if (this.onCancel) {\n this.onCancel();\n }\n return;\n }\n\n const prevValue = this.searchInput.getValue();\n this.searchInput.handleInput(keyData);\n const newValue = this.searchInput.getValue();\n\n if (prevValue !== newValue) {\n this.updateFilter();\n }\n }\n\n private notifySelectionChange() {\n const item = this.filteredItems[this.selectedIndex];\n if (item && this.onSelectionChange) {\n this.onSelectionChange(item);\n }\n }\n\n getSelectedItem(): SelectItem | null {\n return this.filteredItems[this.selectedIndex] ?? null;\n }\n}\n"],"mappings":";;;AAkBA,MAAM,iBAAiB,IAAI,OAAO,GADd,OAAO,aAAa,GACQ,CAAC,cAAc,IAAI;AAQnE,SAAS,UAAU,KAAqB;AACtC,QAAO,IAAI,QAAQ,mBAAmB,GAAG;;AAG3C,IAAa,uBAAb,MAAa,qBAA0C;CACrD;CACA;CACA,gBAAwB;CACxB;CACA;CACA;CACA,6BAAqB,IAAI,KAAqB;CAE9C;CACA;CACA;CAEA,OAAwB,+BAA+B;CACvD,OAAwB,wBAAwB;CAChD,OAAwB,4BAA4B;CACpD,OAAwB,qBAAqB;CAE7C,YAAY,OAAqB,YAAoB,OAAkC;AACrF,OAAK,QAAQ;AACb,OAAK,gBAAgB;AACrB,OAAK,aAAa;AAClB,OAAK,QAAQ;AACb,OAAK,cAAc,IAAI,OAAO;;CAGhC,eAAuB,SAAyB;EAC9C,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ;AACxC,MAAI,CAAC,OAAO;AACV,WAAQ,IAAI,OAAO,KAAK,YAAY,QAAQ,EAAE,KAAK;AACnD,QAAK,WAAW,IAAI,SAAS,MAAM;;AAErC,SAAO;;CAGT,eAAuB;EACrB,MAAM,QAAQ,KAAK,YAAY,UAAU,CAAC,MAAM;AAEhD,MAAI,CAAC,MACH,MAAK,gBAAgB,KAAK;MAE1B,MAAK,gBAAgB,KAAK,YAAY,MAAM;AAG9C,OAAK,gBAAgB;AACrB,OAAK,uBAAuB;;CAG9B,YAAoB,OAA6B;EAC/C,MAAM,IAAI,gCAAgC,MAAM;EAGhD,MAAM,cAA4B,EAAE;EACpC,MAAM,kBAAoC,EAAE;AAE5C,OAAK,MAAM,QAAQ,KAAK,OAAO;GAC7B,MAAM,WAAW,KAAK,aAAa,KAAK;GACxC,MAAM,UAAU,KAAK,eAAe;GACpC,MAAM,QAAQ,gCAAgC,UAAU,SAAS,CAAC;GAClE,MAAM,OAAO,gCAAgC,UAAU,QAAQ,CAAC;GAEhE,MAAM,aAAa,MAAM,QAAQ,EAAE;AACnC,OAAI,eAAe,IAAI;AACrB,gBAAY,KAAK;KAAE;KAAM,MAAM;KAAG,OAAO;KAAY,CAAC;AACtD;;GAEF,MAAM,oBAAoB,sBAAsB,OAAO,EAAE;AACzD,OAAI,sBAAsB,MAAM;AAC9B,gBAAY,KAAK;KAAE;KAAM,MAAM;KAAG,OAAO;KAAmB,CAAC;AAC7D;;GAEF,MAAM,YAAY,KAAK,QAAQ,EAAE;AACjC,OAAI,cAAc,IAAI;AACpB,gBAAY,KAAK;KAAE;KAAM,MAAM;KAAG,OAAO;KAAW,CAAC;AACrD;;GAEF,MAAM,aAAc,KAAiC,cAAc;AACnE,mBAAgB,KAAK;IACnB;IACA,iBAAiB,gCACf;KAAC;KAAU;KAAS;KAAW,CAC5B,KAAK,UAAU,UAAU,MAAM,CAAC,CAChC,OAAO,QAAQ,CACf,KAAK,IAAI,CACb;IACF,CAAC;;AAGJ,cAAY,KAAK,KAAK,eAAe;EACrC,MAAM,eAAe,iBAAiB,iBAAiB,EAAE;AACzD,SAAO,CAAC,GAAG,YAAY,KAAK,MAAM,EAAE,KAAK,EAAE,GAAG,aAAa,KAAK,UAAU,MAAM,KAAK,CAAC;;CAGxF,YAAoB,KAAqB;AACvC,SAAO,IAAI,QAAQ,uBAAuB,OAAO;;CAGnD,kBACE,GACA,MACG;AACH,MAAI,EAAE,SAAS,EAAE,KACf,QAAO,EAAE,OAAO,EAAE;AAEpB,MAAI,EAAE,UAAU,EAAE,MAChB,QAAO,EAAE,QAAQ,EAAE;AAErB,SAAO,KAAK,aAAa,EAAE,KAAK,CAAC,cAAc,KAAK,aAAa,EAAE,KAAK,CAAC;;CAG3E,aAAqB,MAA0B;AAC7C,SAAO,KAAK,SAAS,KAAK;;CAG5B,eAAuB,MAAwD;EAC7E,MAAM,QAAkD,EAAE;AAC1D,iBAAe,YAAY;EAC3B,IAAI,YAAY;EAChB,IAAI;AAEJ,UAAQ,QAAQ,eAAe,KAAK,KAAK,MAAM,MAAM;AACnD,OAAI,MAAM,QAAQ,UAChB,OAAM,KAAK;IAAE,MAAM,KAAK,MAAM,WAAW,MAAM,MAAM;IAAE,QAAQ;IAAO,CAAC;AAEzE,SAAM,KAAK;IAAE,MAAM,MAAM;IAAI,QAAQ;IAAM,CAAC;AAC5C,eAAY,MAAM,QAAQ,MAAM,GAAG;;AAErC,MAAI,YAAY,KAAK,OACnB,OAAM,KAAK;GAAE,MAAM,KAAK,MAAM,UAAU;GAAE,QAAQ;GAAO,CAAC;AAE5D,SAAO;;CAGT,eAAuB,MAAc,OAAuB;EAC1D,MAAM,SAAS,MACZ,MAAM,CACN,MAAM,MAAM,CACZ,KAAK,UAAU,gCAAgC,MAAM,CAAC,CACtD,QAAQ,UAAU,MAAM,SAAS,EAAE;AACtC,MAAI,OAAO,WAAW,EACpB,QAAO;EAGT,MAAM,eAAe,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,UAAU,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;EACxF,IAAI,QAAQ,KAAK,eAAe,KAAK;AACrC,OAAK,MAAM,SAAS,cAAc;GAChC,MAAM,QAAQ,KAAK,eAAe,MAAM;GACxC,MAAM,YAAsD,EAAE;AAC9D,QAAK,MAAM,QAAQ,OAAO;AACxB,QAAI,KAAK,QAAQ;AACf,eAAU,KAAK,KAAK;AACpB;;AAEF,UAAM,YAAY;IAClB,MAAM,WAAW,KAAK,KAAK,QAAQ,QAAQ,UAAU,KAAK,MAAM,eAAe,MAAM,CAAC;AACtF,QAAI,aAAa,KAAK,MAAM;AAC1B,eAAU,KAAK,KAAK;AACpB;;AAEF,cAAU,KAAK,GAAG,KAAK,eAAe,SAAS,CAAC;;AAElD,WAAQ;;AAEV,SAAO,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC,KAAK,GAAG;;CAGhD,iBAAiB,OAAe;AAC9B,OAAK,gBAAgB,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,cAAc,SAAS,EAAE,CAAC;;CAGlF,aAAa;AACX,OAAK,YAAY,YAAY;;CAG/B,OAAO,OAAyB;EAC9B,MAAM,QAAkB,EAAE;EAG1B,MAAM,SAAS,KAAK,MAAM,aAAa,WAAW;EAClD,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,aAAa,OAAO,CAAC;EAE5D,MAAM,YADa,KAAK,YAAY,OAAO,WACf,CAAC,MAAM;AACnC,QAAM,KAAK,GAAG,SAAS,KAAK,MAAM,YAAY,UAAU,GAAG;AAC3D,QAAM,KAAK,GAAG;EAEd,MAAM,QAAQ,KAAK,YAAY,UAAU,CAAC,MAAM;AAEhD,MAAI,KAAK,cAAc,WAAW,GAAG;AACnC,SAAM,KAAK,KAAK,MAAM,QAAQ,eAAe,CAAC;AAC9C,UAAO;;EAGT,MAAM,aAAa,KAAK,IACtB,GACA,KAAK,IACH,KAAK,gBAAgB,KAAK,MAAM,KAAK,aAAa,EAAE,EACpD,KAAK,cAAc,SAAS,KAAK,WAClC,CACF;EACD,MAAM,WAAW,KAAK,IAAI,aAAa,KAAK,YAAY,KAAK,cAAc,OAAO;AAElF,OAAK,IAAI,IAAI,YAAY,IAAI,UAAU,KAAK;GAC1C,MAAM,OAAO,KAAK,cAAc;AAChC,OAAI,CAAC,KACH;GAEF,MAAM,aAAa,MAAM,KAAK;AAC9B,SAAM,KAAK,KAAK,eAAe,MAAM,YAAY,OAAO,MAAM,CAAC;;AAGjE,MAAI,KAAK,cAAc,SAAS,KAAK,YAAY;GAC/C,MAAM,aAAa,GAAG,KAAK,gBAAgB,EAAE,GAAG,KAAK,cAAc;AACnE,SAAM,KAAK,KAAK,MAAM,WAAW,KAAK,aAAa,CAAC;;AAGtD,SAAO;;CAGT,eACE,MACA,YACA,OACA,OACQ;EACR,MAAM,SAAS,aAAa,OAAO;EACnC,MAAM,cAAc,OAAO;EAC3B,MAAM,eAAe,KAAK,aAAa,KAAK;EAE5C,MAAM,cAAc,KAAK;AACzB,MAAI,aAAa;GACf,MAAM,oBAAoB,KAAK,qBAAqB,OAAO,YAAY;AACvE,OAAI,mBAAmB;IACrB,MAAM,iBAAiB,gBAAgB,cAAc,kBAAkB,eAAe,GAAG;IACzF,MAAM,YAAY,KAAK,eAAe,gBAAgB,MAAM;IAE5D,MAAM,cAAc,aAAa,UAAU;IAE3C,MAAM,mBADiB,kBAAkB,iBAAiB,cAChB,kBAAkB;AAE5D,QAAI,oBAAoB,qBAAqB,uBAAuB;KAClE,MAAM,UAAU,IAAI,OAAO,kBAAkB,aAAa;KAC1D,MAAM,gBAAgB,gBAAgB,aAAa,kBAAkB,GAAG;KACxE,MAAM,kBAAkB,KAAK,eAAe,eAAe,MAAM;KAEjE,MAAM,OAAO,GAAG,SAAS,YAAY,UADpB,aAAa,kBAAkB,KAAK,MAAM,YAAY,gBAAgB;AAEvF,YAAO,aAAa,KAAK,MAAM,aAAa,KAAK,GAAG;;;;EAM1D,MAAM,iBAAiB,gBAAgB,cADtB,QAAQ,cAAc,GACwB,GAAG;EAElE,MAAM,OAAO,GAAG,SADE,KAAK,eAAe,gBAAgB,MACpB;AAClC,SAAO,aAAa,KAAK,MAAM,aAAa,KAAK,GAAG;;CAGtD,qBACE,OACA,aACgF;AAChF,MAAI,SAAS,qBAAqB,6BAChC,QAAO;EAGT,MAAM,iBAAiB,KAAK,IAC1B,GACA,QAAQ,cAAc,qBAAqB,mBAC5C;EACD,MAAM,gBACJ,iBACA,qBAAqB,wBACrB,qBAAqB;AAEvB,MAAI,gBAAgB,EAClB,QAAO;AAGT,SAAO;GACL;GACA;GACA,cAAc,qBAAqB;GACpC;;CAGH,YAAY,SAAuB;AACjC,MAAI,aAAa,QAAQ,CACvB;AAGF,MAAI,WAAW,SAAS,KAAK,IAAI,WAAW,SAAS,SAAS,EAAE;AAC9D,QAAK,gBAAgB,KAAK,IAAI,GAAG,KAAK,gBAAgB,EAAE;AACxD,QAAK,uBAAuB;AAC5B;;AAGF,MAAI,WAAW,SAAS,OAAO,IAAI,WAAW,SAAS,SAAS,EAAE;AAChE,QAAK,gBAAgB,KAAK,IAAI,KAAK,cAAc,SAAS,GAAG,KAAK,gBAAgB,EAAE;AACpF,QAAK,uBAAuB;AAC5B;;AAGF,MAAI,WAAW,SAAS,QAAQ,EAAE;GAChC,MAAM,OAAO,KAAK,cAAc,KAAK;AACrC,OAAI,QAAQ,KAAK,SACf,MAAK,SAAS,KAAK;AAErB;;AAGF,MAAI,WAAW,SAAS,SAAS,IAAI,YAAY,KAAU;AACzD,OAAI,KAAK,SACP,MAAK,UAAU;AAEjB;;EAGF,MAAM,YAAY,KAAK,YAAY,UAAU;AAC7C,OAAK,YAAY,YAAY,QAAQ;AAGrC,MAAI,cAFa,KAAK,YAAY,UAER,CACxB,MAAK,cAAc;;CAIvB,wBAAgC;EAC9B,MAAM,OAAO,KAAK,cAAc,KAAK;AACrC,MAAI,QAAQ,KAAK,kBACf,MAAK,kBAAkB,KAAK;;CAIhC,kBAAqC;AACnC,SAAO,KAAK,cAAc,KAAK,kBAAkB"}
|
package/dist/src/tui/theme.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EditorTheme, MarkdownTheme, SelectListTheme } from '@mariozechner/pi-tui';
|
|
2
|
+
import type { SearchableSelectListTheme } from './components/searchable-select-list.js';
|
|
2
3
|
export declare const lightMode: boolean;
|
|
3
4
|
export declare const palette: {
|
|
4
5
|
readonly text: "#f5f5f7";
|
|
@@ -68,4 +69,5 @@ export declare const theme: {
|
|
|
68
69
|
};
|
|
69
70
|
export declare const markdownTheme: MarkdownTheme;
|
|
70
71
|
export declare const selectListTheme: SelectListTheme;
|
|
72
|
+
export declare const searchableSelectListTheme: SearchableSelectListTheme;
|
|
71
73
|
export declare const editorTheme: EditorTheme;
|
package/dist/src/tui/theme.js
CHANGED
|
@@ -141,11 +141,17 @@ const selectListTheme = {
|
|
|
141
141
|
scrollInfo: (text) => fg(palette.dim)(text),
|
|
142
142
|
noMatch: (text) => fg(palette.dim)(text)
|
|
143
143
|
};
|
|
144
|
+
const searchableSelectListTheme = {
|
|
145
|
+
...selectListTheme,
|
|
146
|
+
searchPrompt: (text) => fg(palette.dim)(text),
|
|
147
|
+
searchInput: (text) => fg(palette.text)(text),
|
|
148
|
+
matchHighlight: (text) => fg(palette.accentSoft)(text)
|
|
149
|
+
};
|
|
144
150
|
const editorTheme = {
|
|
145
151
|
borderColor: (text) => fg(palette.border)(text),
|
|
146
152
|
selectList: selectListTheme
|
|
147
153
|
};
|
|
148
154
|
//#endregion
|
|
149
|
-
export { editorTheme, lightMode, markdownTheme, palette, selectListTheme, theme };
|
|
155
|
+
export { editorTheme, lightMode, markdownTheme, palette, searchableSelectListTheme, selectListTheme, theme };
|
|
150
156
|
|
|
151
157
|
//# sourceMappingURL=theme.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/tui/theme.ts"],"sourcesContent":["import type { EditorTheme, MarkdownTheme, SelectListTheme } from '@mariozechner/pi-tui';\nimport chalk from 'chalk';\n\nconst XTERM_LEVELS = [0, 95, 135, 175, 215, 255] as const;\n\nfunction channelToSrgb(value: number): number {\n const normalized = value / 255;\n return normalized <= 0.03928\n ? normalized / 12.92\n : ((normalized + 0.055) / 1.055) ** 2.4;\n}\n\nfunction relativeLuminanceRgb(red: number, green: number, blue: number): number {\n return (\n 0.2126 * channelToSrgb(red) +\n 0.7152 * channelToSrgb(green) +\n 0.0722 * channelToSrgb(blue)\n );\n}\n\nfunction relativeLuminanceHex(hex: string): number {\n return relativeLuminanceRgb(\n Number.parseInt(hex.slice(1, 3), 16),\n Number.parseInt(hex.slice(3, 5), 16),\n Number.parseInt(hex.slice(5, 7), 16),\n );\n}\n\nfunction contrastRatio(bgLuminance: number, fgHex: string): number {\n const fgLuminance = relativeLuminanceHex(fgHex);\n const lighter = Math.max(bgLuminance, fgLuminance);\n const darker = Math.min(bgLuminance, fgLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction isLightBackground(): boolean {\n const explicit = (process.env.XOPC_THEME ?? '').toLowerCase().trim();\n if (explicit === 'light') return true;\n if (explicit === 'dark') return false;\n\n const colorfgbg = process.env.COLORFGBG;\n if (colorfgbg && colorfgbg.length <= 64) {\n const sep = colorfgbg.lastIndexOf(';');\n const bg = Number.parseInt(sep >= 0 ? colorfgbg.slice(sep + 1) : colorfgbg, 10);\n if (bg >= 0 && bg <= 255) {\n if (bg <= 15) return bg === 7 || bg === 15;\n if (bg >= 232) return bg >= 244;\n const cubeIndex = bg - 16;\n const bVal = XTERM_LEVELS[cubeIndex % 6]!;\n const gVal = XTERM_LEVELS[Math.floor(cubeIndex / 6) % 6]!;\n const rVal = XTERM_LEVELS[Math.floor(cubeIndex / 36)]!;\n const bgLum = relativeLuminanceRgb(rVal, gVal, bVal);\n return (\n contrastRatio(bgLum, '#1d1d1f') >= contrastRatio(bgLum, '#f5f5f7')\n );\n }\n }\n return false;\n}\n\nexport const lightMode = isLightBackground();\n\n// Palette tokens align with DESIGN.md (§2 surface/text/border, §2.5 accent, §12.1).\nconst darkPalette = {\n text: '#f5f5f7',\n dim: '#a1a1a6',\n accent: '#3b82f6',\n accentSoft: '#60a5fa',\n border: '#48484a',\n userBg: '#3a3a3c',\n userText: '#f5f5f7',\n systemText: '#8e8e93',\n toolPendingBg: '#2c2c2e',\n toolSuccessBg: '#1e2a22',\n toolErrorBg: '#2a2222',\n toolTitle: '#3b82f6',\n toolOutput: '#d1d1d6',\n quote: '#60a5fa',\n quoteBorder: '#48484a',\n code: '#d1d1d6',\n codeBlock: '#1c1c1e',\n codeBorder: '#48484a',\n link: '#60a5fa',\n error: '#f87171',\n success: '#34d399',\n} as const;\n\nconst lightPalette = {\n text: '#1d1d1f',\n dim: '#6e6e73',\n accent: '#2563eb',\n accentSoft: '#3b82f6',\n border: '#d2d2d7',\n userBg: '#ffffff',\n userText: '#1d1d1f',\n systemText: '#86868b',\n toolPendingBg: '#f0f5ff',\n toolSuccessBg: '#ecfdf5',\n toolErrorBg: '#fef2f2',\n toolTitle: '#2563eb',\n toolOutput: '#6e6e73',\n quote: '#2563eb',\n quoteBorder: '#d2d2d7',\n code: '#92400e',\n codeBlock: '#ffffff',\n codeBorder: '#d2d2d7',\n link: '#2563eb',\n error: '#dc2626',\n success: '#059669',\n} as const;\n\nexport const palette = lightMode ? lightPalette : darkPalette;\n\nconst fg = (hex: string) => (text: string) => chalk.hex(hex)(text);\nconst bg = (hex: string) => (text: string) => chalk.bgHex(hex)(text);\n\nfunction highlightCode(code: string): string[] {\n return code.split('\\n').map((line) => fg(palette.code)(line));\n}\n\nexport const theme = {\n fg: fg(palette.text),\n assistantText: (text: string) => text,\n dim: fg(palette.dim),\n accent: fg(palette.accent),\n accentSoft: fg(palette.accentSoft),\n success: fg(palette.success),\n error: fg(palette.error),\n header: (text: string) => chalk.bold(fg(palette.accent)(text)),\n system: fg(palette.systemText),\n userBg: bg(palette.userBg),\n userText: fg(palette.userText),\n toolTitle: fg(palette.toolTitle),\n toolOutput: fg(palette.toolOutput),\n toolPendingBg: bg(palette.toolPendingBg),\n toolSuccessBg: bg(palette.toolSuccessBg),\n toolErrorBg: bg(palette.toolErrorBg),\n border: fg(palette.border),\n bold: (text: string) => chalk.bold(text),\n italic: (text: string) => chalk.italic(text),\n};\n\nexport const markdownTheme: MarkdownTheme = {\n heading: (text) => chalk.bold(fg(palette.accent)(text)),\n link: (text) => fg(palette.link)(text),\n linkUrl: (text) => chalk.dim(text),\n code: (text) => fg(palette.code)(text),\n codeBlock: (text) => fg(palette.code)(text),\n codeBlockBorder: (text) => fg(palette.codeBorder)(text),\n quote: (text) => fg(palette.quote)(text),\n quoteBorder: (text) => fg(palette.quoteBorder)(text),\n hr: (text) => fg(palette.border)(text),\n listBullet: (text) => fg(palette.accentSoft)(text),\n bold: (text) => chalk.bold(text),\n italic: (text) => chalk.italic(text),\n strikethrough: (text) => chalk.strikethrough(text),\n underline: (text) => chalk.underline(text),\n highlightCode,\n};\n\nexport const selectListTheme: SelectListTheme = {\n selectedPrefix: (text) => fg(palette.accent)(text),\n selectedText: (text) => chalk.bold(fg(palette.accent)(text)),\n description: (text) => fg(palette.dim)(text),\n scrollInfo: (text) => fg(palette.dim)(text),\n noMatch: (text) => fg(palette.dim)(text),\n};\n\nexport const editorTheme: EditorTheme = {\n borderColor: (text) => fg(palette.border)(text),\n selectList: selectListTheme,\n};\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/tui/theme.ts"],"sourcesContent":["import type { EditorTheme, MarkdownTheme, SelectListTheme } from '@mariozechner/pi-tui';\nimport type { SearchableSelectListTheme } from './components/searchable-select-list.js';\nimport chalk from 'chalk';\n\nconst XTERM_LEVELS = [0, 95, 135, 175, 215, 255] as const;\n\nfunction channelToSrgb(value: number): number {\n const normalized = value / 255;\n return normalized <= 0.03928\n ? normalized / 12.92\n : ((normalized + 0.055) / 1.055) ** 2.4;\n}\n\nfunction relativeLuminanceRgb(red: number, green: number, blue: number): number {\n return (\n 0.2126 * channelToSrgb(red) +\n 0.7152 * channelToSrgb(green) +\n 0.0722 * channelToSrgb(blue)\n );\n}\n\nfunction relativeLuminanceHex(hex: string): number {\n return relativeLuminanceRgb(\n Number.parseInt(hex.slice(1, 3), 16),\n Number.parseInt(hex.slice(3, 5), 16),\n Number.parseInt(hex.slice(5, 7), 16),\n );\n}\n\nfunction contrastRatio(bgLuminance: number, fgHex: string): number {\n const fgLuminance = relativeLuminanceHex(fgHex);\n const lighter = Math.max(bgLuminance, fgLuminance);\n const darker = Math.min(bgLuminance, fgLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction isLightBackground(): boolean {\n const explicit = (process.env.XOPC_THEME ?? '').toLowerCase().trim();\n if (explicit === 'light') return true;\n if (explicit === 'dark') return false;\n\n const colorfgbg = process.env.COLORFGBG;\n if (colorfgbg && colorfgbg.length <= 64) {\n const sep = colorfgbg.lastIndexOf(';');\n const bg = Number.parseInt(sep >= 0 ? colorfgbg.slice(sep + 1) : colorfgbg, 10);\n if (bg >= 0 && bg <= 255) {\n if (bg <= 15) return bg === 7 || bg === 15;\n if (bg >= 232) return bg >= 244;\n const cubeIndex = bg - 16;\n const bVal = XTERM_LEVELS[cubeIndex % 6]!;\n const gVal = XTERM_LEVELS[Math.floor(cubeIndex / 6) % 6]!;\n const rVal = XTERM_LEVELS[Math.floor(cubeIndex / 36)]!;\n const bgLum = relativeLuminanceRgb(rVal, gVal, bVal);\n return (\n contrastRatio(bgLum, '#1d1d1f') >= contrastRatio(bgLum, '#f5f5f7')\n );\n }\n }\n return false;\n}\n\nexport const lightMode = isLightBackground();\n\n// Palette tokens align with DESIGN.md (§2 surface/text/border, §2.5 accent, §12.1).\nconst darkPalette = {\n text: '#f5f5f7',\n dim: '#a1a1a6',\n accent: '#3b82f6',\n accentSoft: '#60a5fa',\n border: '#48484a',\n userBg: '#3a3a3c',\n userText: '#f5f5f7',\n systemText: '#8e8e93',\n toolPendingBg: '#2c2c2e',\n toolSuccessBg: '#1e2a22',\n toolErrorBg: '#2a2222',\n toolTitle: '#3b82f6',\n toolOutput: '#d1d1d6',\n quote: '#60a5fa',\n quoteBorder: '#48484a',\n code: '#d1d1d6',\n codeBlock: '#1c1c1e',\n codeBorder: '#48484a',\n link: '#60a5fa',\n error: '#f87171',\n success: '#34d399',\n} as const;\n\nconst lightPalette = {\n text: '#1d1d1f',\n dim: '#6e6e73',\n accent: '#2563eb',\n accentSoft: '#3b82f6',\n border: '#d2d2d7',\n userBg: '#ffffff',\n userText: '#1d1d1f',\n systemText: '#86868b',\n toolPendingBg: '#f0f5ff',\n toolSuccessBg: '#ecfdf5',\n toolErrorBg: '#fef2f2',\n toolTitle: '#2563eb',\n toolOutput: '#6e6e73',\n quote: '#2563eb',\n quoteBorder: '#d2d2d7',\n code: '#92400e',\n codeBlock: '#ffffff',\n codeBorder: '#d2d2d7',\n link: '#2563eb',\n error: '#dc2626',\n success: '#059669',\n} as const;\n\nexport const palette = lightMode ? lightPalette : darkPalette;\n\nconst fg = (hex: string) => (text: string) => chalk.hex(hex)(text);\nconst bg = (hex: string) => (text: string) => chalk.bgHex(hex)(text);\n\nfunction highlightCode(code: string): string[] {\n return code.split('\\n').map((line) => fg(palette.code)(line));\n}\n\nexport const theme = {\n fg: fg(palette.text),\n assistantText: (text: string) => text,\n dim: fg(palette.dim),\n accent: fg(palette.accent),\n accentSoft: fg(palette.accentSoft),\n success: fg(palette.success),\n error: fg(palette.error),\n header: (text: string) => chalk.bold(fg(palette.accent)(text)),\n system: fg(palette.systemText),\n userBg: bg(palette.userBg),\n userText: fg(palette.userText),\n toolTitle: fg(palette.toolTitle),\n toolOutput: fg(palette.toolOutput),\n toolPendingBg: bg(palette.toolPendingBg),\n toolSuccessBg: bg(palette.toolSuccessBg),\n toolErrorBg: bg(palette.toolErrorBg),\n border: fg(palette.border),\n bold: (text: string) => chalk.bold(text),\n italic: (text: string) => chalk.italic(text),\n};\n\nexport const markdownTheme: MarkdownTheme = {\n heading: (text) => chalk.bold(fg(palette.accent)(text)),\n link: (text) => fg(palette.link)(text),\n linkUrl: (text) => chalk.dim(text),\n code: (text) => fg(palette.code)(text),\n codeBlock: (text) => fg(palette.code)(text),\n codeBlockBorder: (text) => fg(palette.codeBorder)(text),\n quote: (text) => fg(palette.quote)(text),\n quoteBorder: (text) => fg(palette.quoteBorder)(text),\n hr: (text) => fg(palette.border)(text),\n listBullet: (text) => fg(palette.accentSoft)(text),\n bold: (text) => chalk.bold(text),\n italic: (text) => chalk.italic(text),\n strikethrough: (text) => chalk.strikethrough(text),\n underline: (text) => chalk.underline(text),\n highlightCode,\n};\n\nexport const selectListTheme: SelectListTheme = {\n selectedPrefix: (text) => fg(palette.accent)(text),\n selectedText: (text) => chalk.bold(fg(palette.accent)(text)),\n description: (text) => fg(palette.dim)(text),\n scrollInfo: (text) => fg(palette.dim)(text),\n noMatch: (text) => fg(palette.dim)(text),\n};\n\nexport const searchableSelectListTheme: SearchableSelectListTheme = {\n ...selectListTheme,\n searchPrompt: (text) => fg(palette.dim)(text),\n searchInput: (text) => fg(palette.text)(text),\n matchHighlight: (text) => fg(palette.accentSoft)(text),\n};\n\nexport const editorTheme: EditorTheme = {\n borderColor: (text) => fg(palette.border)(text),\n selectList: selectListTheme,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TUI } from '@mariozechner/pi-tui';
|
|
2
|
+
import type { ChatLog } from './components/chat-log.js';
|
|
3
|
+
import type { StreamAssembler } from './stream-assembler.js';
|
|
4
|
+
import type { TuiState } from './tui-types.js';
|
|
5
|
+
export declare function clearPendingToolCallIds(): void;
|
|
6
|
+
export declare function dispatchAgentSSE(event: string, data: Record<string, unknown>, state: TuiState, chatLog: ChatLog, assembler: StreamAssembler, tui: TUI, setActivityStatus: (status: string) => void, touchStreamingActivity?: () => void): void;
|
|
7
|
+
export declare const DEFAULT_STREAMING_WATCHDOG_MS = 30000;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//#region src/tui/tui-agent-events.ts
|
|
2
|
+
const pendingToolCallIds = /* @__PURE__ */ new Map();
|
|
3
|
+
function clearPendingToolCallIds() {
|
|
4
|
+
pendingToolCallIds.clear();
|
|
5
|
+
}
|
|
6
|
+
const STREAM_TOUCH_EVENTS = new Set([
|
|
7
|
+
"status",
|
|
8
|
+
"token",
|
|
9
|
+
"thinking",
|
|
10
|
+
"tool_start",
|
|
11
|
+
"tool_end",
|
|
12
|
+
"progress"
|
|
13
|
+
]);
|
|
14
|
+
function dispatchAgentSSE(event, data, state, chatLog, assembler, tui, setActivityStatus, touchStreamingActivity) {
|
|
15
|
+
if (STREAM_TOUCH_EVENTS.has(event)) touchStreamingActivity?.();
|
|
16
|
+
const runId = state.activeRunId ?? "default";
|
|
17
|
+
switch (event) {
|
|
18
|
+
case "status":
|
|
19
|
+
state.activeRunId = typeof data.runId === "string" ? data.runId : runId;
|
|
20
|
+
setActivityStatus("waiting");
|
|
21
|
+
break;
|
|
22
|
+
case "token": {
|
|
23
|
+
const content = typeof data.content === "string" ? data.content : typeof data.delta === "string" ? data.delta : typeof data.text === "string" ? data.text : "";
|
|
24
|
+
if (!content) break;
|
|
25
|
+
setActivityStatus("streaming");
|
|
26
|
+
const display = assembler.ingestToken(runId, content, state.showThinking);
|
|
27
|
+
if (display !== null) {
|
|
28
|
+
chatLog.updateAssistant(display, runId);
|
|
29
|
+
tui.requestRender();
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "thinking": {
|
|
34
|
+
const thinkContent = String(data.content ?? "");
|
|
35
|
+
const isDelta = Boolean(data.delta);
|
|
36
|
+
if (data.status === "started") break;
|
|
37
|
+
setActivityStatus("streaming");
|
|
38
|
+
const display = assembler.ingestThinking(runId, thinkContent, isDelta, state.showThinking);
|
|
39
|
+
if (display !== null) {
|
|
40
|
+
chatLog.updateAssistant(display, runId);
|
|
41
|
+
tui.requestRender();
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case "thinking_end":
|
|
46
|
+
case "message_end": break;
|
|
47
|
+
case "tool_start": {
|
|
48
|
+
const toolName = String(data.toolName ?? "unknown");
|
|
49
|
+
const toolCallId = String(data.toolCallId || crypto.randomUUID());
|
|
50
|
+
const stack = pendingToolCallIds.get(toolName) ?? [];
|
|
51
|
+
stack.push(toolCallId);
|
|
52
|
+
pendingToolCallIds.set(toolName, stack);
|
|
53
|
+
setActivityStatus("running");
|
|
54
|
+
chatLog.startTool(toolCallId, toolName, data.args, runId);
|
|
55
|
+
tui.requestRender();
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "tool_end": {
|
|
59
|
+
const toolName = String(data.toolName ?? "");
|
|
60
|
+
let toolCallId = typeof data.toolCallId === "string" && data.toolCallId ? data.toolCallId : "";
|
|
61
|
+
if (!toolCallId && toolName) {
|
|
62
|
+
const stack = pendingToolCallIds.get(toolName);
|
|
63
|
+
if (stack && stack.length > 0) {
|
|
64
|
+
toolCallId = stack.shift();
|
|
65
|
+
if (stack.length === 0) pendingToolCallIds.delete(toolName);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const resultText = String(data.result ?? "");
|
|
69
|
+
const isError = Boolean(data.isError);
|
|
70
|
+
if (toolCallId) chatLog.updateToolResult(toolCallId, resultText, isError);
|
|
71
|
+
setActivityStatus("streaming");
|
|
72
|
+
tui.requestRender();
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "error": {
|
|
76
|
+
const errorContent = String(data.content ?? "Unknown error");
|
|
77
|
+
const finalText = assembler.finalize(runId, state.showThinking);
|
|
78
|
+
if (finalText) chatLog.finalizeAssistant(finalText, runId);
|
|
79
|
+
chatLog.addSystem(`❌ ${errorContent}`);
|
|
80
|
+
state.activeRunId = null;
|
|
81
|
+
setActivityStatus("idle");
|
|
82
|
+
tui.requestRender();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "result": {
|
|
86
|
+
const finalText = assembler.finalize(runId, state.showThinking);
|
|
87
|
+
if (finalText) chatLog.finalizeAssistant(finalText, runId);
|
|
88
|
+
state.activeRunId = null;
|
|
89
|
+
setActivityStatus("idle");
|
|
90
|
+
tui.requestRender();
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "progress":
|
|
94
|
+
setActivityStatus("running");
|
|
95
|
+
break;
|
|
96
|
+
default: break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const DEFAULT_STREAMING_WATCHDOG_MS = 3e4;
|
|
100
|
+
//#endregion
|
|
101
|
+
export { DEFAULT_STREAMING_WATCHDOG_MS, clearPendingToolCallIds, dispatchAgentSSE };
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=tui-agent-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-agent-events.js","names":[],"sources":["../../../src/tui/tui-agent-events.ts"],"sourcesContent":["import type { TUI } from '@mariozechner/pi-tui';\n\nimport type { ChatLog } from './components/chat-log.js';\nimport type { StreamAssembler } from './stream-assembler.js';\nimport type { TuiState } from './tui-types.js';\n\nconst pendingToolCallIds = new Map<string, string[]>();\n\nexport function clearPendingToolCallIds(): void {\n pendingToolCallIds.clear();\n}\n\nconst STREAM_TOUCH_EVENTS = new Set([\n 'status',\n 'token',\n 'thinking',\n 'tool_start',\n 'tool_end',\n 'progress',\n]);\n\nexport function dispatchAgentSSE(\n event: string,\n data: Record<string, unknown>,\n state: TuiState,\n chatLog: ChatLog,\n assembler: StreamAssembler,\n tui: TUI,\n setActivityStatus: (status: string) => void,\n touchStreamingActivity?: () => void,\n): void {\n if (STREAM_TOUCH_EVENTS.has(event)) {\n touchStreamingActivity?.();\n }\n\n const runId = state.activeRunId ?? 'default';\n\n switch (event) {\n case 'status': {\n const newRunId = typeof data.runId === 'string' ? data.runId : runId;\n state.activeRunId = newRunId;\n setActivityStatus('waiting');\n break;\n }\n case 'token': {\n const content =\n typeof data.content === 'string'\n ? data.content\n : typeof data.delta === 'string'\n ? data.delta\n : typeof data.text === 'string'\n ? data.text\n : '';\n if (!content) break;\n setActivityStatus('streaming');\n const display = assembler.ingestToken(runId, content, state.showThinking);\n if (display !== null) {\n chatLog.updateAssistant(display, runId);\n tui.requestRender();\n }\n break;\n }\n case 'thinking': {\n const thinkContent = String(data.content ?? '');\n const isDelta = Boolean(data.delta);\n if (data.status === 'started') break;\n setActivityStatus('streaming');\n const display = assembler.ingestThinking(runId, thinkContent, isDelta, state.showThinking);\n if (display !== null) {\n chatLog.updateAssistant(display, runId);\n tui.requestRender();\n }\n break;\n }\n case 'thinking_end':\n case 'message_end':\n break;\n case 'tool_start': {\n const toolName = String(data.toolName ?? 'unknown');\n const toolCallId = String(data.toolCallId || crypto.randomUUID());\n const stack = pendingToolCallIds.get(toolName) ?? [];\n stack.push(toolCallId);\n pendingToolCallIds.set(toolName, stack);\n setActivityStatus('running');\n chatLog.startTool(toolCallId, toolName, data.args, runId);\n tui.requestRender();\n break;\n }\n case 'tool_end': {\n const toolName = String(data.toolName ?? '');\n let toolCallId = typeof data.toolCallId === 'string' && data.toolCallId ? data.toolCallId : '';\n if (!toolCallId && toolName) {\n const stack = pendingToolCallIds.get(toolName);\n if (stack && stack.length > 0) {\n toolCallId = stack.shift()!;\n if (stack.length === 0) pendingToolCallIds.delete(toolName);\n }\n }\n const resultText = String(data.result ?? '');\n const isError = Boolean(data.isError);\n if (toolCallId) {\n chatLog.updateToolResult(toolCallId, resultText, isError);\n }\n setActivityStatus('streaming');\n tui.requestRender();\n break;\n }\n case 'error': {\n const errorContent = String(data.content ?? 'Unknown error');\n const finalText = assembler.finalize(runId, state.showThinking);\n if (finalText) {\n chatLog.finalizeAssistant(finalText, runId);\n }\n chatLog.addSystem(`❌ ${errorContent}`);\n state.activeRunId = null;\n setActivityStatus('idle');\n tui.requestRender();\n break;\n }\n case 'result': {\n const finalText = assembler.finalize(runId, state.showThinking);\n if (finalText) {\n chatLog.finalizeAssistant(finalText, runId);\n }\n state.activeRunId = null;\n setActivityStatus('idle');\n tui.requestRender();\n break;\n }\n case 'progress': {\n setActivityStatus('running');\n break;\n }\n default:\n break;\n }\n}\n\nexport const DEFAULT_STREAMING_WATCHDOG_MS = 30_000;\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ClientHistoryMessage } from '../session/client-history.js';
|
|
1
2
|
import type { SessionInfo } from './tui-types.js';
|
|
2
3
|
/** Options for sending a chat message. */
|
|
3
4
|
export interface ChatSendOptions {
|
|
@@ -37,6 +38,11 @@ export interface TuiBackend {
|
|
|
37
38
|
onEvent?: (evt: TuiEvent) => void;
|
|
38
39
|
onConnected?: () => void;
|
|
39
40
|
onDisconnected?: (reason: string) => void;
|
|
41
|
+
/** Broadcast SSE sequence gap (if the gateway emits `gap` events). */
|
|
42
|
+
onGap?: (info: {
|
|
43
|
+
expected: number;
|
|
44
|
+
received: number;
|
|
45
|
+
}) => void;
|
|
40
46
|
/** Start the backend (open SSE streams / start agent service). */
|
|
41
47
|
start(): void;
|
|
42
48
|
/** Stop the backend. */
|
|
@@ -70,15 +76,5 @@ export interface TuiBackend {
|
|
|
70
76
|
/** Patch session settings (e.g. model). */
|
|
71
77
|
patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void>;
|
|
72
78
|
}
|
|
73
|
-
/** A single message in chat history. */
|
|
74
|
-
export
|
|
75
|
-
role: 'user' | 'assistant' | 'system';
|
|
76
|
-
content: string;
|
|
77
|
-
timestamp?: number;
|
|
78
|
-
toolCalls?: Array<{
|
|
79
|
-
name: string;
|
|
80
|
-
args?: unknown;
|
|
81
|
-
result?: string;
|
|
82
|
-
isError?: boolean;
|
|
83
|
-
}>;
|
|
84
|
-
}
|
|
79
|
+
/** A single message in chat history (aligned with `ClientHistoryMessage`). */
|
|
80
|
+
export type HistoryMessage = ClientHistoryMessage;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TUI } from '@mariozechner/pi-tui';
|
|
2
|
+
import type { ChatLog } from './components/chat-log.js';
|
|
3
|
+
import type { StreamAssembler } from './stream-assembler.js';
|
|
4
|
+
import type { TuiState } from './tui-types.js';
|
|
5
|
+
interface SlashCommandDef {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function getSlashCommands(_isLocal: boolean): SlashCommandDef[];
|
|
10
|
+
export declare function formatTuiHelpText(isLocal: boolean): string;
|
|
11
|
+
export type CommandHandlerDeps = {
|
|
12
|
+
state: TuiState;
|
|
13
|
+
chatLog: ChatLog;
|
|
14
|
+
tui: TUI;
|
|
15
|
+
assembler: StreamAssembler;
|
|
16
|
+
isLocalMode: boolean;
|
|
17
|
+
abortActive: () => Promise<void>;
|
|
18
|
+
sendMessage: (text: string) => void;
|
|
19
|
+
requestExit: () => void;
|
|
20
|
+
updateFooter: () => void;
|
|
21
|
+
};
|
|
22
|
+
export declare function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void;
|
|
23
|
+
export {};
|