opencode-visual-cache 1.6.1 → 1.7.0-beta.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.
- package/LICENSE +21 -21
- package/README.md +247 -247
- package/README_EN.md +247 -247
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/color.d.ts +37 -0
- package/dist/core/color.js +108 -0
- package/dist/core/currency.d.ts +17 -0
- package/dist/core/currency.js +43 -0
- package/dist/core/estimate.d.ts +1 -0
- package/dist/core/estimate.js +40 -0
- package/dist/core/format.d.ts +15 -0
- package/dist/core/format.js +90 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +5 -0
- package/dist/core/types.d.ts +17 -0
- package/dist/core/types.js +1 -0
- package/dist/index.js +159 -815
- package/dist/panel/TokenCachePanel.d.ts +10 -0
- package/dist/panel/TokenCachePanel.js +549 -0
- package/dist/panel/panel-api.d.ts +119 -0
- package/dist/panel/panel-api.js +1 -0
- package/dist/tui.js +349 -264
- package/dist/v2/commands.d.ts +10 -0
- package/dist/v2/commands.js +490 -0
- package/dist/v2/data.d.ts +27 -0
- package/dist/v2/data.js +103 -0
- package/dist/v2/index.d.ts +4 -0
- package/dist/v2/index.js +163 -0
- package/dist/v2/sidebar.d.ts +6 -0
- package/dist/v2/sidebar.js +36 -0
- package/dist/v2/status.d.ts +14 -0
- package/dist/v2/status.js +83 -0
- package/dist/v2/theme.d.ts +8 -0
- package/dist/v2/theme.js +16 -0
- package/dist/v2/types.d.ts +219 -0
- package/dist/v2/types.js +6 -0
- package/dist/v2/v2-panel-api.d.ts +8 -0
- package/dist/v2/v2-panel-api.js +176 -0
- package/dist/v2.js +2941 -0
- package/package.json +70 -67
- package/src/_version.ts +1 -1
- package/src/balance-providers.ts +153 -153
- package/src/core/color.ts +108 -0
- package/src/core/currency.ts +46 -0
- package/src/core/estimate.ts +36 -0
- package/src/core/format.ts +81 -0
- package/src/core/index.ts +5 -0
- package/src/core/types.ts +17 -0
- package/src/i18n.ts +380 -380
- package/src/index.tsx +1035 -1980
- package/src/panel/TokenCachePanel.tsx +776 -0
- package/src/panel/panel-api.ts +104 -0
- package/src/server.ts +10 -10
- package/src/v2/commands.ts +471 -0
- package/src/v2/data.ts +112 -0
- package/src/v2/index.tsx +184 -0
- package/src/v2/sidebar.tsx +69 -0
- package/src/v2/status.tsx +96 -0
- package/src/v2/theme.ts +19 -0
- package/src/v2/types.ts +159 -0
- package/src/v2/v2-panel-api.ts +177 -0
package/dist/v2.js
ADDED
|
@@ -0,0 +1,2941 @@
|
|
|
1
|
+
// src/v2/index.tsx
|
|
2
|
+
import { createComponent as _$createComponent3 } from "@opentui/solid";
|
|
3
|
+
import { createSignal as createSignal2, createEffect as createEffect2, onMount as onMount2, onCleanup as onCleanup2, untrack as untrack2 } from "solid-js";
|
|
4
|
+
|
|
5
|
+
// src/v2/v2-panel-api.ts
|
|
6
|
+
function createPanelApi(context) {
|
|
7
|
+
const kvStore = /* @__PURE__ */ new Map();
|
|
8
|
+
const messageIndex = /* @__PURE__ */ new Map();
|
|
9
|
+
const toV1Part = (p) => {
|
|
10
|
+
if (p.type === "tool") {
|
|
11
|
+
const st = { ...p.state ?? {} };
|
|
12
|
+
if (st.output === void 0 && Array.isArray(st.content)) {
|
|
13
|
+
st.output = st.content.map((c) => c.type === "text" ? c.text : c.type === "file" ? String(c.path ?? c.filename ?? "") : JSON.stringify(c)).filter((t) => typeof t === "string" && t.length > 0).join("\n");
|
|
14
|
+
}
|
|
15
|
+
return { type: "tool", tool: p.tool ?? p.name, state: st, subagent_type: p.subagent_type, metadata: p.metadata };
|
|
16
|
+
}
|
|
17
|
+
if (p.type === "text") {
|
|
18
|
+
return { type: "text", text: String(p.text ?? ""), synthetic: p.synthetic, ignored: p.ignored };
|
|
19
|
+
}
|
|
20
|
+
if (p.type === "file") {
|
|
21
|
+
return { type: "file", source: p.source ?? {} };
|
|
22
|
+
}
|
|
23
|
+
if (p.type === "reasoning") {
|
|
24
|
+
return { type: "reasoning", text: String(p.text ?? "") };
|
|
25
|
+
}
|
|
26
|
+
return p;
|
|
27
|
+
};
|
|
28
|
+
const kvGet = (key, fallback) => {
|
|
29
|
+
let entry = kvStore.get(key);
|
|
30
|
+
if (!entry) {
|
|
31
|
+
const created = context.storage.store(`opencode-visual-cache.${key}`, {
|
|
32
|
+
initial: { value: fallback }
|
|
33
|
+
});
|
|
34
|
+
entry = created;
|
|
35
|
+
kvStore.set(key, entry);
|
|
36
|
+
}
|
|
37
|
+
const v = entry[0].value;
|
|
38
|
+
return v === void 0 ? fallback : v;
|
|
39
|
+
};
|
|
40
|
+
const kvSet = (key, value) => {
|
|
41
|
+
let entry = kvStore.get(key);
|
|
42
|
+
if (!entry) {
|
|
43
|
+
const created = context.storage.store(`opencode-visual-cache.${key}`, {
|
|
44
|
+
initial: { value }
|
|
45
|
+
});
|
|
46
|
+
entry = created;
|
|
47
|
+
kvStore.set(key, entry);
|
|
48
|
+
}
|
|
49
|
+
const [, mutate] = entry;
|
|
50
|
+
return mutate((d) => {
|
|
51
|
+
d.value = value;
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
kv: {
|
|
56
|
+
ready: true,
|
|
57
|
+
get: kvGet,
|
|
58
|
+
set: kvSet
|
|
59
|
+
},
|
|
60
|
+
state: {
|
|
61
|
+
session: {
|
|
62
|
+
get(id) {
|
|
63
|
+
const s = context.data.session.get(id);
|
|
64
|
+
if (!s) return void 0;
|
|
65
|
+
const model = s.model;
|
|
66
|
+
return {
|
|
67
|
+
id: s.id,
|
|
68
|
+
title: s.title,
|
|
69
|
+
agent: s.agent,
|
|
70
|
+
model: model ? { providerID: model.providerID, id: model.id } : void 0
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
messages(id) {
|
|
74
|
+
const raw = context.data.session.message.list(id) ?? [];
|
|
75
|
+
let turnId;
|
|
76
|
+
const normalized = raw.map((m) => {
|
|
77
|
+
const record = m;
|
|
78
|
+
messageIndex.set(String(m.id), record);
|
|
79
|
+
let parentID;
|
|
80
|
+
if (record.type === "assistant") {
|
|
81
|
+
if (!turnId) turnId = String(m.id);
|
|
82
|
+
parentID = turnId;
|
|
83
|
+
const finish = record.finish;
|
|
84
|
+
const terminal = typeof finish === "string" && !["tool-calls", "unknown"].includes(finish) || Boolean(record.error);
|
|
85
|
+
if (terminal) turnId = void 0;
|
|
86
|
+
}
|
|
87
|
+
const model = m.model;
|
|
88
|
+
return {
|
|
89
|
+
...m,
|
|
90
|
+
parentID,
|
|
91
|
+
role: m.type === "assistant" ? "assistant" : m.type === "user" ? "user" : m.type,
|
|
92
|
+
providerID: model?.providerID,
|
|
93
|
+
modelID: model?.id
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return normalized;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
// V2 Provider.Info 无 models——从独立模型列表组装 V1 形状({ id, models: { [modelID]: { cost } } })。
|
|
100
|
+
// Model.Info.cost 是数组(tier 分段定价),取第一档近似(无 tier 匹配信息)。
|
|
101
|
+
// 响应式 getter:每次访问实时组装(对齐官方 dialog-model 的 data.location.model.list(location.ref) 用法,
|
|
102
|
+
// location 数据为 Solid store——组件 effect 中读取自动建立依赖,数据同步后重算)。
|
|
103
|
+
get provider() {
|
|
104
|
+
const models = context.data.location.model?.list(context.location) ?? [];
|
|
105
|
+
const byProvider = /* @__PURE__ */ new Map();
|
|
106
|
+
for (const m of models) {
|
|
107
|
+
const pid = String(m.providerID ?? "");
|
|
108
|
+
if (!pid) continue;
|
|
109
|
+
const costArr = Array.isArray(m.cost) ? m.cost : [];
|
|
110
|
+
const cost = costArr[0];
|
|
111
|
+
if (!cost) continue;
|
|
112
|
+
const entry = byProvider.get(pid) ?? { id: pid, models: {} };
|
|
113
|
+
const modelID = String(m.modelID ?? "");
|
|
114
|
+
const fullId = m.id !== void 0 ? String(m.id) : "";
|
|
115
|
+
const shortId = fullId.split("/").pop() ?? "";
|
|
116
|
+
for (const k of new Set([modelID, fullId, shortId].filter((x) => x.length > 0))) {
|
|
117
|
+
entry.models[k] = { cost };
|
|
118
|
+
}
|
|
119
|
+
byProvider.set(pid, entry);
|
|
120
|
+
}
|
|
121
|
+
return [...byProvider.values()];
|
|
122
|
+
},
|
|
123
|
+
config: {},
|
|
124
|
+
// V2 插件 API 无 config 读取;实验兜底
|
|
125
|
+
part(messageID) {
|
|
126
|
+
const msg = messageIndex.get(String(messageID));
|
|
127
|
+
if (!msg) return [];
|
|
128
|
+
if (!Array.isArray(msg.content) || msg.content.length === 0) {
|
|
129
|
+
if (typeof msg.text === "string" && msg.text.length > 0) {
|
|
130
|
+
return [{ type: "text", text: msg.text }];
|
|
131
|
+
}
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const parts = msg.content.map((p) => toV1Part(p));
|
|
135
|
+
if (msg.type === "assistant" && typeof msg.cost === "number" && Number.isFinite(msg.cost)) {
|
|
136
|
+
parts.push({ type: "step-finish", cost: msg.cost });
|
|
137
|
+
}
|
|
138
|
+
return parts;
|
|
139
|
+
},
|
|
140
|
+
path: { directory: String(context.location?.directory ?? "") }
|
|
141
|
+
},
|
|
142
|
+
event: {
|
|
143
|
+
on: (type, handler) => context.data.on(type, handler)
|
|
144
|
+
},
|
|
145
|
+
renderer: { terminalWidth: context.renderer.terminalWidth },
|
|
146
|
+
keys: { formatBindings: () => void 0 },
|
|
147
|
+
tuiConfig: {
|
|
148
|
+
keybinds: {
|
|
149
|
+
get: (command) => context.keymap.shortcuts(command)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/panel/TokenCachePanel.tsx
|
|
156
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
157
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
158
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
159
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
160
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
161
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
162
|
+
import { use as _$use } from "@opentui/solid";
|
|
163
|
+
import { spread as _$spread } from "@opentui/solid";
|
|
164
|
+
import { mergeProps as _$mergeProps } from "@opentui/solid";
|
|
165
|
+
import { memo as _$memo } from "@opentui/solid";
|
|
166
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
167
|
+
import { createMemo, createSignal, createEffect, onMount, onCleanup, Show, untrack } from "solid-js";
|
|
168
|
+
|
|
169
|
+
// src/balance-providers.ts
|
|
170
|
+
var BalanceError = class extends Error {
|
|
171
|
+
};
|
|
172
|
+
var siliconflowProvider = {
|
|
173
|
+
id: "siliconflow",
|
|
174
|
+
name: "SiliconFlow",
|
|
175
|
+
keyPlaceholder: "sk-...",
|
|
176
|
+
async fetchBalance(apiKey, signal) {
|
|
177
|
+
const res = await fetch("https://api.siliconflow.cn/v1/user/info", {
|
|
178
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
179
|
+
signal
|
|
180
|
+
});
|
|
181
|
+
if (!res.ok) {
|
|
182
|
+
if (res.status === 401) throw new BalanceError("401");
|
|
183
|
+
if (res.status === 403) throw new BalanceError("403");
|
|
184
|
+
throw new BalanceError(String(res.status));
|
|
185
|
+
}
|
|
186
|
+
const json = await res.json();
|
|
187
|
+
const total = json.data?.totalBalance ?? json.data?.balance;
|
|
188
|
+
if (typeof total === "undefined" || total === null) throw new BalanceError("EMPTY");
|
|
189
|
+
return [{ currency: "CNY", total: String(total) }];
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var deepseekProvider = {
|
|
193
|
+
id: "deepseek",
|
|
194
|
+
name: "DeepSeek",
|
|
195
|
+
keyPlaceholder: "sk-...",
|
|
196
|
+
async fetchBalance(apiKey, signal) {
|
|
197
|
+
const res = await fetch("https://api.deepseek.com/user/balance", {
|
|
198
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
199
|
+
signal
|
|
200
|
+
});
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
if (res.status === 401) throw new BalanceError("401");
|
|
203
|
+
if (res.status === 402 || res.status === 403) throw new BalanceError("403");
|
|
204
|
+
throw new BalanceError(String(res.status));
|
|
205
|
+
}
|
|
206
|
+
const json = await res.json();
|
|
207
|
+
const infos = json.balance_infos ?? [];
|
|
208
|
+
if (infos.length === 0) throw new BalanceError("EMPTY");
|
|
209
|
+
return infos.map((info) => ({
|
|
210
|
+
currency: info.currency ?? "CNY",
|
|
211
|
+
total: info.total_balance ?? "0"
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var openrouterProvider = {
|
|
216
|
+
id: "openrouter",
|
|
217
|
+
name: "OpenRouter",
|
|
218
|
+
keyPlaceholder: "sk-or-...",
|
|
219
|
+
async fetchBalance(apiKey, signal) {
|
|
220
|
+
const res = await fetch("https://openrouter.ai/api/v1/credits", {
|
|
221
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
222
|
+
signal
|
|
223
|
+
});
|
|
224
|
+
if (!res.ok) {
|
|
225
|
+
if (res.status === 401 || res.status === 403) throw new BalanceError("403");
|
|
226
|
+
throw new BalanceError(String(res.status));
|
|
227
|
+
}
|
|
228
|
+
const json = await res.json();
|
|
229
|
+
const credits = json.data?.total_credits;
|
|
230
|
+
const usage = json.data?.total_usage;
|
|
231
|
+
if (typeof credits !== "number" || typeof usage !== "number") throw new BalanceError("EMPTY");
|
|
232
|
+
return [{ currency: "USD", total: (credits - usage).toFixed(2) }];
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var moonshotProvider = {
|
|
236
|
+
id: "moonshot",
|
|
237
|
+
name: "Moonshot",
|
|
238
|
+
keyPlaceholder: "sk-...",
|
|
239
|
+
async fetchBalance(apiKey, signal) {
|
|
240
|
+
const res = await fetch("https://api.moonshot.cn/v1/users/me/balance", {
|
|
241
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
242
|
+
signal
|
|
243
|
+
});
|
|
244
|
+
if (!res.ok) {
|
|
245
|
+
if (res.status === 401) throw new BalanceError("401");
|
|
246
|
+
if (res.status === 403) throw new BalanceError("403");
|
|
247
|
+
throw new BalanceError(String(res.status));
|
|
248
|
+
}
|
|
249
|
+
const json = await res.json();
|
|
250
|
+
const balance = json.data?.available_balance;
|
|
251
|
+
if (typeof balance === "undefined" || balance === null) throw new BalanceError("EMPTY");
|
|
252
|
+
return [{ currency: "CNY", total: String(balance) }];
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var balanceProviders = [deepseekProvider, siliconflowProvider, openrouterProvider, moonshotProvider];
|
|
256
|
+
function getBalanceProvider(id) {
|
|
257
|
+
return balanceProviders.find((p) => p.id === id) ?? balanceProviders[0] ?? deepseekProvider;
|
|
258
|
+
}
|
|
259
|
+
function matchBalanceProvider(providerId) {
|
|
260
|
+
const id = providerId.toLowerCase();
|
|
261
|
+
const exact = balanceProviders.find((p) => p.id.toLowerCase() === id);
|
|
262
|
+
if (exact) return exact;
|
|
263
|
+
return balanceProviders.find((p) => id.startsWith(p.id.toLowerCase()));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/i18n.ts
|
|
267
|
+
var ZH_T = {
|
|
268
|
+
title: "\u7F13\u5B58\u7EDF\u8BA1",
|
|
269
|
+
hit: "\u547D\u4E2D\u7387",
|
|
270
|
+
totalHit: "\u603B\u547D\u4E2D:",
|
|
271
|
+
read: "\u7F13\u5B58\u8BFB:",
|
|
272
|
+
write: "\u7F13\u5B58\u5199:",
|
|
273
|
+
miss: "\u672A\u547D\u4E2D:",
|
|
274
|
+
out: "\u8F93\u51FA:",
|
|
275
|
+
cost: "\u8D39\u7528:",
|
|
276
|
+
saved: "\u7D2F\u8BA1\u8282\u7701:",
|
|
277
|
+
model: "\u6A21\u578B:",
|
|
278
|
+
provider: "\u63D0\u4F9B\u5546:",
|
|
279
|
+
rate: "\u5355\u4EF7:",
|
|
280
|
+
hitFolded: "\u547D\u4E2D",
|
|
281
|
+
inputRate: "\u8F93\u5165",
|
|
282
|
+
cacheRate: "\u7F13\u5B58",
|
|
283
|
+
writeRate: "\u5199\u5165",
|
|
284
|
+
noData: "\u7B49\u5F85\u7F13\u5B58\u6570\u636E...",
|
|
285
|
+
tok: "tok",
|
|
286
|
+
distTitle: "\u4F30\u7B97 Token \u5206\u5E03",
|
|
287
|
+
distSys: "\u7CFB\u7EDF\u63D0\u793A:",
|
|
288
|
+
distUser: "\u7528\u6237:",
|
|
289
|
+
distAgent: "\u5B50\u4EE3\u7406\u6307\u4EE4:",
|
|
290
|
+
distTool: "Tool \u8C03\u7528:",
|
|
291
|
+
distRes: "Tool \u7ED3\u679C:",
|
|
292
|
+
distReason: "\u63A8\u7406:",
|
|
293
|
+
stepsCount: "\u672C\u56DE\u5408 {n} \u6B21\u8C03\u7528:",
|
|
294
|
+
secDetail: "\u660E\u7EC6",
|
|
295
|
+
secModel: "\u6A21\u578B",
|
|
296
|
+
secSkills: "\u5DF2\u52A0\u8F7D\u6280\u80FD",
|
|
297
|
+
balTotal: "\u603B\u4F59\u989D:",
|
|
298
|
+
balNoKey: "\u672A\u914D\u7F6E {p} API Key",
|
|
299
|
+
balLoading: "\u67E5\u8BE2\u4E2D...",
|
|
300
|
+
balError: "\u67E5\u8BE2\u5931\u8D25",
|
|
301
|
+
balErr401: "API Key \u65E0\u6548",
|
|
302
|
+
balErr403: "\u4F59\u989D\u67E5\u8BE2\u88AB\u62D2\u7EDD",
|
|
303
|
+
balErrEmpty: "\u672A\u83B7\u53D6\u5230\u4F59\u989D\u6570\u636E",
|
|
304
|
+
balErrTimeout: "\u67E5\u8BE2\u8D85\u65F6",
|
|
305
|
+
balUnsupported: "\u5F53\u524D\u63D0\u4F9B\u5546\u4E0D\u652F\u6301\u4F59\u989D\u67E5\u8BE2",
|
|
306
|
+
barHit: "\u547D\u4E2D\u7387",
|
|
307
|
+
barBal: "\u4F59\u989D",
|
|
308
|
+
barTok: "Tokens",
|
|
309
|
+
// ── /cache-section 面板 ──
|
|
310
|
+
secToggle: "\u5207\u6362\u533A\u5757",
|
|
311
|
+
secBalance: "\u4F59\u989D",
|
|
312
|
+
secBottom: "\u5E95\u90E8\u72B6\u6001\u680F",
|
|
313
|
+
secBorder: "\u9762\u677F\u8FB9\u6846",
|
|
314
|
+
// ── toast ──
|
|
315
|
+
keySaved: "API Key \u5DF2\u4FDD\u5B58\uFF0C\u6B63\u5728\u67E5\u8BE2\u4F59\u989D...",
|
|
316
|
+
keyCleared: "API Key \u5DF2\u6E05\u9664",
|
|
317
|
+
currencySet: "\u5E01\u79CD: {v} ({s}), \u6C47\u7387: {r}",
|
|
318
|
+
rateSet: "\u6C47\u7387\u5DF2\u8BBE\u4E3A {r}",
|
|
319
|
+
panelConfigTitle: "\u7F13\u5B58\u9762\u677F\u914D\u7F6E",
|
|
320
|
+
panelConfigMsg: "\u5E01\u79CD: {c} | \u6C47\u7387: {r} | \u660E\u7EC6: {d} | \u6A21\u578B: {m} | \u5206\u5E03: {t} | \u6280\u80FD: {k} | \u4F59\u989D: {b} | \u5E95\u90E8: {f}",
|
|
321
|
+
borderShown: "\u9762\u677F\u8FB9\u6846 \u5DF2\u663E\u793A",
|
|
322
|
+
borderHidden: "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF",
|
|
323
|
+
sectionShown: "{s} \u5DF2\u663E\u793A",
|
|
324
|
+
sectionHidden: "{s} \u5DF2\u9690\u85CF",
|
|
325
|
+
langSwitched: "\u8BED\u8A00\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587",
|
|
326
|
+
autoSwitchOn: "\u81EA\u52A8\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546: \u5F00",
|
|
327
|
+
autoSwitchOff: "\u81EA\u52A8\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546: \u5173",
|
|
328
|
+
providerManual: "\u4F59\u989D\u63D0\u4F9B\u5546: {p}\uFF08\u81EA\u52A8\u5207\u6362\u5DF2\u5173\u95ED\uFF09",
|
|
329
|
+
runInSession: "\u8BF7\u5728\u4F1A\u8BDD\u5185\u8FD0\u884C\u6B64\u547D\u4EE4",
|
|
330
|
+
backToMain: "\u5DF2\u5207\u56DE\u4E3B\u4F1A\u8BDD",
|
|
331
|
+
subAgentSwitched: "\u5DF2\u5207\u6362\u81F3\u5B50\u4EE3\u7406: {s}",
|
|
332
|
+
// ── 对话框 / 菜单 ──
|
|
333
|
+
langTitle: "\u663E\u793A\u8BED\u8A00",
|
|
334
|
+
subPrefix: "\u5B50\u4EE3\u7406: ",
|
|
335
|
+
keyUser: "\uFF08\u7528\u6237 key\uFF09",
|
|
336
|
+
keyOpenCode: "\uFF08OpenCode\uFF09",
|
|
337
|
+
keyNotSet: "\uFF08\u672A\u914D\u7F6E\uFF09",
|
|
338
|
+
balKeyPrompt: "\u8F93\u5165 {p} API Key \u4EE5\u663E\u793A\u8D26\u6237\u4F59\u989D\uFF08\u7559\u7A7A\u6E05\u9664\uFF09",
|
|
339
|
+
balProvTitle: "\u4F59\u989D\u63D0\u4F9B\u5546 / \u81EA\u52A8\u5207\u6362",
|
|
340
|
+
autoSwitchOpt: "\u81EA\u52A8\u5207\u6362\u63D0\u4F9B\u5546",
|
|
341
|
+
balSelectTitle: "\u9009\u62E9\u4F59\u989D\u63D0\u4F9B\u5546",
|
|
342
|
+
backToMainTitle: "\u56DE\u5230\u4E3B\u4F1A\u8BDD",
|
|
343
|
+
subSelectTitle: "\u9009\u62E9\u5B50\u4EE3\u7406",
|
|
344
|
+
subSwitchTitle: "\u5207\u6362\u5B50\u4EE3\u7406",
|
|
345
|
+
subViewTitle: "\u67E5\u770B\u5B50\u4EE3\u7406\u7F13\u5B58",
|
|
346
|
+
subNoFound: "\u672A\u627E\u5230\u5B50\u4EE3\u7406\uFF0C\u8BF7\u624B\u52A8\u7C98\u8D34 Session ID"
|
|
347
|
+
};
|
|
348
|
+
var EN_T = {
|
|
349
|
+
title: "Token Cache",
|
|
350
|
+
hit: "Hit",
|
|
351
|
+
totalHit: "Total Hit:",
|
|
352
|
+
read: "Read:",
|
|
353
|
+
write: "Write:",
|
|
354
|
+
miss: "Miss:",
|
|
355
|
+
out: "Out:",
|
|
356
|
+
cost: "Cost:",
|
|
357
|
+
saved: "Total Saved:",
|
|
358
|
+
model: "Model:",
|
|
359
|
+
provider: "Provider:",
|
|
360
|
+
rate: "Rate:",
|
|
361
|
+
hitFolded: "hit",
|
|
362
|
+
inputRate: "in",
|
|
363
|
+
cacheRate: "cache",
|
|
364
|
+
writeRate: "write",
|
|
365
|
+
noData: "Waiting for cache data...",
|
|
366
|
+
tok: "tok",
|
|
367
|
+
distTitle: "Estimated Token Dist.",
|
|
368
|
+
distSys: "System:",
|
|
369
|
+
distUser: "User:",
|
|
370
|
+
distAgent: "Sub-Agent Instr:",
|
|
371
|
+
distTool: "Tool Call:",
|
|
372
|
+
distRes: "Tool Result:",
|
|
373
|
+
distReason: "Reasoning:",
|
|
374
|
+
stepsCount: "Turn #{n} calls:",
|
|
375
|
+
secDetail: "Detail",
|
|
376
|
+
secModel: "Model",
|
|
377
|
+
secSkills: "Loaded Skills",
|
|
378
|
+
balTotal: "Total:",
|
|
379
|
+
balNoKey: "{p} API Key not set",
|
|
380
|
+
balLoading: "Fetching...",
|
|
381
|
+
balError: "Fetch failed",
|
|
382
|
+
balErr401: "Invalid API Key",
|
|
383
|
+
balErr403: "Balance request rejected",
|
|
384
|
+
balErrEmpty: "No balance data",
|
|
385
|
+
balErrTimeout: "Request timed out",
|
|
386
|
+
balUnsupported: "Balance query unsupported",
|
|
387
|
+
barHit: "Hit",
|
|
388
|
+
barBal: "Balance",
|
|
389
|
+
barTok: "Tokens",
|
|
390
|
+
// ── /cache-section panel ──
|
|
391
|
+
secToggle: "Toggle Section",
|
|
392
|
+
secBalance: "Balance",
|
|
393
|
+
secBottom: "Bottom Bar",
|
|
394
|
+
secBorder: "Panel Border",
|
|
395
|
+
// ── toasts ──
|
|
396
|
+
keySaved: "API Key saved, fetching balance...",
|
|
397
|
+
keyCleared: "API Key cleared",
|
|
398
|
+
currencySet: "Currency: {v} ({s}), rate: {r}",
|
|
399
|
+
rateSet: "Exchange rate set to {r}",
|
|
400
|
+
panelConfigTitle: "Cache Panel Config",
|
|
401
|
+
panelConfigMsg: "Currency: {c} | Rate: {r} | Detail: {d} | Model: {m} | Dist: {t} | Skills: {k} | Balance: {b} | Bottom: {f}",
|
|
402
|
+
borderShown: "Panel border shown",
|
|
403
|
+
borderHidden: "Panel border hidden",
|
|
404
|
+
sectionShown: "{s} section shown",
|
|
405
|
+
sectionHidden: "{s} section hidden",
|
|
406
|
+
langSwitched: "Switched to English",
|
|
407
|
+
autoSwitchOn: "Auto-switch balance provider: ON",
|
|
408
|
+
autoSwitchOff: "Auto-switch balance provider: OFF",
|
|
409
|
+
providerManual: "Balance provider: {p} (auto-switch off)",
|
|
410
|
+
runInSession: "Please run this command inside a session",
|
|
411
|
+
backToMain: "Switched to main session",
|
|
412
|
+
subAgentSwitched: "Showing sub-agent: {s}",
|
|
413
|
+
// ── dialogs / menus ──
|
|
414
|
+
langTitle: "Display Language",
|
|
415
|
+
subPrefix: "Sub: ",
|
|
416
|
+
keyUser: " (user key)",
|
|
417
|
+
keyOpenCode: " (OpenCode)",
|
|
418
|
+
keyNotSet: " (not set)",
|
|
419
|
+
balKeyPrompt: "Enter your {p} API key to show account balance (leave empty to clear)",
|
|
420
|
+
balProvTitle: "Balance Provider / Auto-switch",
|
|
421
|
+
autoSwitchOpt: "Auto-switch provider",
|
|
422
|
+
balSelectTitle: "Select Balance Provider",
|
|
423
|
+
backToMainTitle: "Back to Main",
|
|
424
|
+
subSelectTitle: "Select Sub-Agent",
|
|
425
|
+
subSwitchTitle: "Switch Sub",
|
|
426
|
+
subViewTitle: "View Sub Cache",
|
|
427
|
+
subNoFound: "No sub-agents found. Paste a Session ID manually"
|
|
428
|
+
};
|
|
429
|
+
var JA_T = {
|
|
430
|
+
title: "\u30AD\u30E3\u30C3\u30B7\u30E5\u7D71\u8A08",
|
|
431
|
+
hit: "\u30D2\u30C3\u30C8\u7387",
|
|
432
|
+
totalHit: "\u7DCF\u30D2\u30C3\u30C8:",
|
|
433
|
+
read: "\u30AD\u30E3\u30C3\u30B7\u30E5\u8AAD\u8FBC:",
|
|
434
|
+
write: "\u30AD\u30E3\u30C3\u30B7\u30E5\u66F8\u8FBC:",
|
|
435
|
+
miss: "\u672A\u30D2\u30C3\u30C8:",
|
|
436
|
+
out: "\u51FA\u529B:",
|
|
437
|
+
cost: "\u8CBB\u7528:",
|
|
438
|
+
saved: "\u7D2F\u8A08\u7BC0\u7D04:",
|
|
439
|
+
model: "\u30E2\u30C7\u30EB:",
|
|
440
|
+
provider: "\u30D7\u30ED\u30D0\u30A4\u30C0:",
|
|
441
|
+
rate: "\u5358\u4FA1:",
|
|
442
|
+
hitFolded: "\u30D2\u30C3\u30C8",
|
|
443
|
+
inputRate: "\u5165\u529B",
|
|
444
|
+
cacheRate: "\u30AD\u30E3\u30C3\u30B7\u30E5",
|
|
445
|
+
writeRate: "\u66F8\u8FBC",
|
|
446
|
+
noData: "\u30AD\u30E3\u30C3\u30B7\u30E5\u30C7\u30FC\u30BF\u5F85\u6A5F\u4E2D...",
|
|
447
|
+
tok: "tok",
|
|
448
|
+
distTitle: "Token \u5206\u5E03\uFF08\u63A8\u5B9A\uFF09",
|
|
449
|
+
distSys: "\u30B7\u30B9\u30C6\u30E0:",
|
|
450
|
+
distUser: "\u30E6\u30FC\u30B6\u30FC:",
|
|
451
|
+
distAgent: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u6307\u793A:",
|
|
452
|
+
distTool: "Tool \u547C\u3073\u51FA\u3057:",
|
|
453
|
+
distRes: "Tool \u7D50\u679C:",
|
|
454
|
+
distReason: "\u63A8\u8AD6:",
|
|
455
|
+
stepsCount: "\u4ECA\u56DE {n} \u56DE\u547C\u51FA:",
|
|
456
|
+
secDetail: "\u660E\u7D30",
|
|
457
|
+
secModel: "\u30E2\u30C7\u30EB",
|
|
458
|
+
secSkills: "\u8AAD\u307F\u8FBC\u307F\u6E08\u307F\u30B9\u30AD\u30EB",
|
|
459
|
+
balTotal: "\u7DCF\u6B8B\u9AD8:",
|
|
460
|
+
balNoKey: "{p} \u306E API Key \u672A\u8A2D\u5B9A",
|
|
461
|
+
balLoading: "\u53D6\u5F97\u4E2D...",
|
|
462
|
+
balError: "\u53D6\u5F97\u5931\u6557",
|
|
463
|
+
balErr401: "API Key \u7121\u52B9",
|
|
464
|
+
balErr403: "\u6B8B\u9AD8\u7167\u4F1A\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",
|
|
465
|
+
balErrEmpty: "\u6B8B\u9AD8\u30C7\u30FC\u30BF\u306A\u3057",
|
|
466
|
+
balErrTimeout: "\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8",
|
|
467
|
+
balUnsupported: "\u3053\u306E\u30D7\u30ED\u30D0\u30A4\u30C0\u306F\u6B8B\u9AD8\u7167\u4F1A\u975E\u5BFE\u5FDC",
|
|
468
|
+
barHit: "\u30D2\u30C3\u30C8\u7387",
|
|
469
|
+
barBal: "\u6B8B\u9AD8",
|
|
470
|
+
barTok: "Tokens",
|
|
471
|
+
// ── /cache-section パネル ──
|
|
472
|
+
secToggle: "\u30BB\u30AF\u30B7\u30E7\u30F3\u5207\u66FF",
|
|
473
|
+
secBalance: "\u6B8B\u9AD8",
|
|
474
|
+
secBottom: "\u4E0B\u90E8\u30B9\u30C6\u30FC\u30BF\u30B9\u30D0\u30FC",
|
|
475
|
+
secBorder: "\u30D1\u30CD\u30EB\u67A0\u7DDA",
|
|
476
|
+
// ── toast ──
|
|
477
|
+
keySaved: "API Key \u4FDD\u5B58\u6E08\u307F\u3001\u6B8B\u9AD8\u3092\u53D6\u5F97\u4E2D...",
|
|
478
|
+
keyCleared: "API Key \u3092\u30AF\u30EA\u30A2\u3057\u307E\u3057\u305F",
|
|
479
|
+
currencySet: "\u901A\u8CA8: {v} ({s}), \u30EC\u30FC\u30C8: {r}",
|
|
480
|
+
rateSet: "\u30EC\u30FC\u30C8\u3092 {r} \u306B\u8A2D\u5B9A\u3057\u307E\u3057\u305F",
|
|
481
|
+
panelConfigTitle: "\u30D1\u30CD\u30EB\u8A2D\u5B9A",
|
|
482
|
+
panelConfigMsg: "\u901A\u8CA8: {c} | \u30EC\u30FC\u30C8: {r} | \u660E\u7D30: {d} | \u30E2\u30C7\u30EB: {m} | \u5206\u5E03: {t} | \u30B9\u30AD\u30EB: {k} | \u6B8B\u9AD8: {b} | \u4E0B\u90E8: {f}",
|
|
483
|
+
borderShown: "\u30D1\u30CD\u30EB\u67A0\u7DDA \u8868\u793A",
|
|
484
|
+
borderHidden: "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A",
|
|
485
|
+
sectionShown: "{s} \u8868\u793A",
|
|
486
|
+
sectionHidden: "{s} \u975E\u8868\u793A",
|
|
487
|
+
langSwitched: "\u65E5\u672C\u8A9E\u306B\u5207\u308A\u66FF\u3048\u307E\u3057\u305F",
|
|
488
|
+
autoSwitchOn: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u81EA\u52D5\u5207\u66FF: \u30AA\u30F3",
|
|
489
|
+
autoSwitchOff: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u81EA\u52D5\u5207\u66FF: \u30AA\u30D5",
|
|
490
|
+
providerManual: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0: {p}\uFF08\u81EA\u52D5\u5207\u66FF\u30AA\u30D5\uFF09",
|
|
491
|
+
runInSession: "\u30BB\u30C3\u30B7\u30E7\u30F3\u5185\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
492
|
+
backToMain: "\u30E1\u30A4\u30F3\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u623B\u308A\u307E\u3057\u305F",
|
|
493
|
+
subAgentSwitched: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u5207\u66FF: {s}",
|
|
494
|
+
// ── ダイアログ / メニュー ──
|
|
495
|
+
langTitle: "\u8868\u793A\u8A00\u8A9E",
|
|
496
|
+
subPrefix: "\u30B5\u30D6: ",
|
|
497
|
+
keyUser: "\uFF08\u30E6\u30FC\u30B6\u30FC key\uFF09",
|
|
498
|
+
keyOpenCode: "\uFF08OpenCode\uFF09",
|
|
499
|
+
keyNotSet: "\uFF08\u672A\u8A2D\u5B9A\uFF09",
|
|
500
|
+
balKeyPrompt: "{p} \u306E API Key \u3092\u5165\u529B\u3057\u3066\u6B8B\u9AD8\u3092\u8868\u793A\uFF08\u7A7A\u6B04\u3067\u30AF\u30EA\u30A2\uFF09",
|
|
501
|
+
balProvTitle: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0 / \u81EA\u52D5\u5207\u66FF",
|
|
502
|
+
autoSwitchOpt: "\u81EA\u52D5\u5207\u66FF\u30D7\u30ED\u30D0\u30A4\u30C0",
|
|
503
|
+
balSelectTitle: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u9078\u629E",
|
|
504
|
+
backToMainTitle: "\u30E1\u30A4\u30F3\u306B\u623B\u308B",
|
|
505
|
+
subSelectTitle: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u9078\u629E",
|
|
506
|
+
subSwitchTitle: "\u30B5\u30D6\u5207\u66FF",
|
|
507
|
+
subViewTitle: "\u30B5\u30D6\u30AD\u30E3\u30C3\u30B7\u30E5\u8868\u793A",
|
|
508
|
+
subNoFound: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002Session ID \u3092\u624B\u52D5\u3067\u8CBC\u308A\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044"
|
|
509
|
+
};
|
|
510
|
+
var KO_T = {
|
|
511
|
+
title: "\uCE90\uC2DC \uD1B5\uACC4",
|
|
512
|
+
hit: "\uD788\uD2B8\uC728",
|
|
513
|
+
totalHit: "\uCD1D \uD788\uD2B8:",
|
|
514
|
+
read: "\uCE90\uC2DC \uC77D\uAE30:",
|
|
515
|
+
write: "\uCE90\uC2DC \uC4F0\uAE30:",
|
|
516
|
+
miss: "\uBBF8\uD788\uD2B8:",
|
|
517
|
+
out: "\uCD9C\uB825:",
|
|
518
|
+
cost: "\uBE44\uC6A9:",
|
|
519
|
+
saved: "\uB204\uC801 \uC808\uC57D:",
|
|
520
|
+
model: "\uBAA8\uB378:",
|
|
521
|
+
provider: "\uD504\uB85C\uBC14\uC774\uB354:",
|
|
522
|
+
rate: "\uB2E8\uAC00:",
|
|
523
|
+
hitFolded: "\uD788\uD2B8",
|
|
524
|
+
inputRate: "\uC785\uB825",
|
|
525
|
+
cacheRate: "\uCE90\uC2DC",
|
|
526
|
+
writeRate: "\uC4F0\uAE30",
|
|
527
|
+
noData: "\uCE90\uC2DC \uB370\uC774\uD130 \uB300\uAE30 \uC911...",
|
|
528
|
+
tok: "tok",
|
|
529
|
+
distTitle: "Token \uBD84\uD3EC(\uCD94\uC815)",
|
|
530
|
+
distSys: "\uC2DC\uC2A4\uD15C:",
|
|
531
|
+
distUser: "\uC0AC\uC6A9\uC790:",
|
|
532
|
+
distAgent: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uC9C0\uC2DC:",
|
|
533
|
+
distTool: "Tool \uD638\uCD9C:",
|
|
534
|
+
distRes: "Tool \uACB0\uACFC:",
|
|
535
|
+
distReason: "\uCD94\uB860:",
|
|
536
|
+
stepsCount: "\uC774\uBC88 \uD134 {n}\uD68C \uD638\uCD9C:",
|
|
537
|
+
secDetail: "\uC0C1\uC138",
|
|
538
|
+
secModel: "\uBAA8\uB378",
|
|
539
|
+
secSkills: "\uB85C\uB4DC\uB41C \uC2A4\uD0AC",
|
|
540
|
+
balTotal: "\uCD1D \uC794\uC561:",
|
|
541
|
+
balNoKey: "{p} API Key \uBBF8\uC124\uC815",
|
|
542
|
+
balLoading: "\uC870\uD68C \uC911...",
|
|
543
|
+
balError: "\uC870\uD68C \uC2E4\uD328",
|
|
544
|
+
balErr401: "API Key \uBB34\uD6A8",
|
|
545
|
+
balErr403: "\uC794\uC561 \uC870\uD68C\uAC00 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
546
|
+
balErrEmpty: "\uC794\uC561 \uB370\uC774\uD130 \uC5C6\uC74C",
|
|
547
|
+
balErrTimeout: "\uC2DC\uAC04 \uCD08\uACFC",
|
|
548
|
+
balUnsupported: "\uC774 \uD504\uB85C\uBC14\uC774\uB354\uB294 \uC794\uC561 \uC870\uD68C \uBBF8\uC9C0\uC6D0",
|
|
549
|
+
barHit: "\uD788\uD2B8\uC728",
|
|
550
|
+
barBal: "\uC794\uC561",
|
|
551
|
+
barTok: "Tokens",
|
|
552
|
+
// ── /cache-section 패널 ──
|
|
553
|
+
secToggle: "\uC139\uC158 \uC804\uD658",
|
|
554
|
+
secBalance: "\uC794\uC561",
|
|
555
|
+
secBottom: "\uD558\uB2E8 \uC0C1\uD0DC\uBC14",
|
|
556
|
+
secBorder: "\uD328\uB110 \uD14C\uB450\uB9AC",
|
|
557
|
+
// ── toast ──
|
|
558
|
+
keySaved: "API Key \uC800\uC7A5\uB428, \uC794\uC561 \uC870\uD68C \uC911...",
|
|
559
|
+
keyCleared: "API Key \uC0AD\uC81C\uB428",
|
|
560
|
+
currencySet: "\uD1B5\uD654: {v} ({s}), \uD658\uC728: {r}",
|
|
561
|
+
rateSet: "\uD658\uC728\uC744 {r}(\uC73C)\uB85C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4",
|
|
562
|
+
panelConfigTitle: "\uD328\uB110 \uC124\uC815",
|
|
563
|
+
panelConfigMsg: "\uD1B5\uD654: {c} | \uD658\uC728: {r} | \uC0C1\uC138: {d} | \uBAA8\uB378: {m} | \uBD84\uD3EC: {t} | \uC2A4\uD0AC: {k} | \uC794\uC561: {b} | \uD558\uB2E8: {f}",
|
|
564
|
+
borderShown: "\uD328\uB110 \uD14C\uB450\uB9AC \uD45C\uC2DC",
|
|
565
|
+
borderHidden: "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40",
|
|
566
|
+
sectionShown: "{s} \uD45C\uC2DC",
|
|
567
|
+
sectionHidden: "{s} \uC228\uAE40",
|
|
568
|
+
langSwitched: "\uD55C\uAD6D\uC5B4\uB85C \uC804\uD658\uD588\uC2B5\uB2C8\uB2E4",
|
|
569
|
+
autoSwitchOn: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC790\uB3D9 \uC804\uD658: \uCF1C\uC9D0",
|
|
570
|
+
autoSwitchOff: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC790\uB3D9 \uC804\uD658: \uAEBC\uC9D0",
|
|
571
|
+
providerManual: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354: {p}(\uC790\uB3D9 \uC804\uD658 \uAEBC\uC9D0)",
|
|
572
|
+
runInSession: "\uC138\uC158 \uB0B4\uC5D0\uC11C \uC2E4\uD589\uD574 \uC8FC\uC138\uC694",
|
|
573
|
+
backToMain: "\uBA54\uC778 \uC138\uC158\uC73C\uB85C \uB3CC\uC544\uAC14\uC2B5\uB2C8\uB2E4",
|
|
574
|
+
subAgentSwitched: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uB85C \uC804\uD658: {s}",
|
|
575
|
+
// ── 다이얼로그 / 메뉴 ──
|
|
576
|
+
langTitle: "\uD45C\uC2DC \uC5B8\uC5B4",
|
|
577
|
+
subPrefix: "\uC11C\uBE0C: ",
|
|
578
|
+
keyUser: "(\uC0AC\uC6A9\uC790 key)",
|
|
579
|
+
keyOpenCode: "(OpenCode)",
|
|
580
|
+
keyNotSet: "(\uBBF8\uC124\uC815)",
|
|
581
|
+
balKeyPrompt: "{p} API Key\uB97C \uC785\uB825\uD558\uC5EC \uC794\uC561 \uD45C\uC2DC(\uBE44\uC6B0\uBA74 \uC0AD\uC81C)",
|
|
582
|
+
balProvTitle: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 / \uC790\uB3D9 \uC804\uD658",
|
|
583
|
+
autoSwitchOpt: "\uC790\uB3D9 \uC804\uD658 \uD504\uB85C\uBC14\uC774\uB354",
|
|
584
|
+
balSelectTitle: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC120\uD0DD",
|
|
585
|
+
backToMainTitle: "\uBA54\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30",
|
|
586
|
+
subSelectTitle: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uC120\uD0DD",
|
|
587
|
+
subSwitchTitle: "\uC11C\uBE0C \uC804\uD658",
|
|
588
|
+
subViewTitle: "\uC11C\uBE0C \uCE90\uC2DC \uBCF4\uAE30",
|
|
589
|
+
subNoFound: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. Session ID\uB97C \uC9C1\uC811 \uBD99\uC5EC\uB123\uC73C\uC138\uC694"
|
|
590
|
+
};
|
|
591
|
+
var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
|
|
592
|
+
var LANG_META = [
|
|
593
|
+
{ code: "zh", label: "\u4E2D\u6587" },
|
|
594
|
+
{ code: "en", label: "English" },
|
|
595
|
+
{ code: "ja", label: "\u65E5\u672C\u8A9E" },
|
|
596
|
+
{ code: "ko", label: "\uD55C\uAD6D\uC5B4" }
|
|
597
|
+
];
|
|
598
|
+
function applyParams(tpl, params) {
|
|
599
|
+
if (!params) return tpl;
|
|
600
|
+
return tpl.replace(
|
|
601
|
+
/\{(\w+)\}/g,
|
|
602
|
+
(m, k) => k in params ? String(params[k]) : m
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
function createT(getCode) {
|
|
606
|
+
return (key, params) => applyParams(LANGS[getCode()][key], params);
|
|
607
|
+
}
|
|
608
|
+
function detectLang() {
|
|
609
|
+
try {
|
|
610
|
+
const loc = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase();
|
|
611
|
+
if (loc.startsWith("zh")) return "zh";
|
|
612
|
+
if (loc.startsWith("ja")) return "ja";
|
|
613
|
+
if (loc.startsWith("ko")) return "ko";
|
|
614
|
+
return "en";
|
|
615
|
+
} catch {
|
|
616
|
+
return "en";
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/core/color.ts
|
|
621
|
+
function rgb(raw) {
|
|
622
|
+
if (typeof raw === "string" && raw.startsWith("#")) {
|
|
623
|
+
const h = raw.slice(1);
|
|
624
|
+
return {
|
|
625
|
+
r: parseInt(h.slice(0, 2), 16),
|
|
626
|
+
g: parseInt(h.slice(2, 4), 16),
|
|
627
|
+
b: parseInt(h.slice(4, 6), 16)
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (raw && typeof raw === "object") {
|
|
631
|
+
const o = raw;
|
|
632
|
+
if (typeof o.r === "number" && typeof o.g === "number" && typeof o.b === "number") {
|
|
633
|
+
const scale = o.r > 1 || o.g > 1 || o.b > 1 ? 1 : 255;
|
|
634
|
+
return {
|
|
635
|
+
r: Math.round(o.r * scale),
|
|
636
|
+
g: Math.round(o.g * scale),
|
|
637
|
+
b: Math.round(o.b * scale)
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
function saturation(r, g, b) {
|
|
644
|
+
const max = Math.max(r, g, b) / 255;
|
|
645
|
+
const min = Math.min(r, g, b) / 255;
|
|
646
|
+
const delta = max - min;
|
|
647
|
+
if (delta === 0) return 0;
|
|
648
|
+
const L = (max + min) / 2;
|
|
649
|
+
return L <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
|
|
650
|
+
}
|
|
651
|
+
function desaturateTo(raw, maxSat, fallback) {
|
|
652
|
+
const c = rgb(raw);
|
|
653
|
+
if (!c) return fallback;
|
|
654
|
+
const sat = saturation(c.r, c.g, c.b);
|
|
655
|
+
if (sat <= maxSat) {
|
|
656
|
+
return "#" + [c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, "0")).join("");
|
|
657
|
+
}
|
|
658
|
+
const luma = c.r * 0.299 + c.g * 0.587 + c.b * 0.114;
|
|
659
|
+
let lo = 0, hi = 1;
|
|
660
|
+
for (let i = 0; i < 12; i++) {
|
|
661
|
+
const mid = (lo + hi) / 2;
|
|
662
|
+
const nr2 = Math.round(c.r + (luma - c.r) * mid);
|
|
663
|
+
const ng2 = Math.round(c.g + (luma - c.g) * mid);
|
|
664
|
+
const nb2 = Math.round(c.b + (luma - c.b) * mid);
|
|
665
|
+
if (saturation(nr2, ng2, nb2) > maxSat) lo = mid;
|
|
666
|
+
else hi = mid;
|
|
667
|
+
}
|
|
668
|
+
const nr = Math.round(c.r + (luma - c.r) * hi);
|
|
669
|
+
const ng = Math.round(c.g + (luma - c.g) * hi);
|
|
670
|
+
const nb = Math.round(c.b + (luma - c.b) * hi);
|
|
671
|
+
return "#" + [nr, ng, nb].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
672
|
+
}
|
|
673
|
+
function dimColor(hex, factor = 0.5) {
|
|
674
|
+
const c = rgb(hex);
|
|
675
|
+
if (!c) return hex;
|
|
676
|
+
const r = Math.round(c.r * factor);
|
|
677
|
+
const g = Math.round(c.g * factor);
|
|
678
|
+
const b = Math.round(c.b * factor);
|
|
679
|
+
return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
680
|
+
}
|
|
681
|
+
var FALLBACK = {
|
|
682
|
+
primary: "#8B9DAF",
|
|
683
|
+
text: "#C5C5BB",
|
|
684
|
+
muted: "#7A7A72",
|
|
685
|
+
success: "#9CAF8B",
|
|
686
|
+
warning: "#C5B88D",
|
|
687
|
+
error: "#B08A8A",
|
|
688
|
+
border: "#6B6B63"
|
|
689
|
+
};
|
|
690
|
+
var MAX_SAT = 0.28;
|
|
691
|
+
|
|
692
|
+
// src/core/format.ts
|
|
693
|
+
function charColumns(c) {
|
|
694
|
+
const code = c.codePointAt(0) ?? 0;
|
|
695
|
+
if (code < 32) return 0;
|
|
696
|
+
if (code < 127) return 1;
|
|
697
|
+
if (code < 160) return 0;
|
|
698
|
+
if (code >= 4352 && code <= 4447 || // Hangul Jamo
|
|
699
|
+
code >= 11904 && code <= 42191 || // CJK Radicals … Yi
|
|
700
|
+
code >= 44032 && code <= 55203 || // Hangul
|
|
701
|
+
code >= 63744 && code <= 64255 || // CJK Compat
|
|
702
|
+
code >= 65040 && code <= 65135 || // Vertical / Compat
|
|
703
|
+
code >= 65281 && code <= 65376 || // Fullwidth
|
|
704
|
+
code >= 65504 && code <= 65510 || // Fullwidth signs
|
|
705
|
+
code >= 127744 && code <= 128591 || // Misc Symbols (emoji)
|
|
706
|
+
code >= 131072 && code <= 262141)
|
|
707
|
+
return 2;
|
|
708
|
+
return 1;
|
|
709
|
+
}
|
|
710
|
+
function visualWidth(s) {
|
|
711
|
+
let w = 0;
|
|
712
|
+
for (const c of s) w += charColumns(c);
|
|
713
|
+
return w;
|
|
714
|
+
}
|
|
715
|
+
function visualPadEnd(s, cols) {
|
|
716
|
+
const pad = cols - visualWidth(s);
|
|
717
|
+
return pad > 0 ? s + " ".repeat(pad) : s;
|
|
718
|
+
}
|
|
719
|
+
function truncateVisual(s, maxCols) {
|
|
720
|
+
if (visualWidth(s) <= maxCols) return s;
|
|
721
|
+
let result = "", w = 0;
|
|
722
|
+
for (const c of s) {
|
|
723
|
+
const cw = charColumns(c);
|
|
724
|
+
if (w + cw > maxCols - 1) {
|
|
725
|
+
result += "\u2026";
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
result += c;
|
|
729
|
+
w += cw;
|
|
730
|
+
}
|
|
731
|
+
return result;
|
|
732
|
+
}
|
|
733
|
+
function progressBar(percent, width) {
|
|
734
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
735
|
+
const filled = Math.round(clamped / 100 * width);
|
|
736
|
+
const empty = Math.max(0, width - filled);
|
|
737
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
738
|
+
}
|
|
739
|
+
function fmt(n) {
|
|
740
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
741
|
+
if (n >= 1e4) return (n / 1e3).toFixed(1) + "K";
|
|
742
|
+
return n.toLocaleString("en-US");
|
|
743
|
+
}
|
|
744
|
+
function num(v) {
|
|
745
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
746
|
+
}
|
|
747
|
+
function fmtCost(n, symbol = "$", rate = 1) {
|
|
748
|
+
const v = n * rate;
|
|
749
|
+
if (v >= 1) return symbol + v.toFixed(2);
|
|
750
|
+
if (v >= 0.01) return symbol + v.toFixed(3);
|
|
751
|
+
return symbol + v.toFixed(4);
|
|
752
|
+
}
|
|
753
|
+
function fmtCompact(n) {
|
|
754
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
755
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "K";
|
|
756
|
+
return String(Math.round(n));
|
|
757
|
+
}
|
|
758
|
+
function formatBalanceAmount(total) {
|
|
759
|
+
const n = parseFloat(total);
|
|
760
|
+
if (!Number.isFinite(n)) return total;
|
|
761
|
+
if (n === 0 || n >= 1) return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
762
|
+
return n.toLocaleString("en-US", { maximumFractionDigits: 6 });
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/core/currency.ts
|
|
766
|
+
var CURRENCIES = {
|
|
767
|
+
USD: "$",
|
|
768
|
+
CNY: "\xA5",
|
|
769
|
+
EUR: "\u20AC",
|
|
770
|
+
JPY: "JP\xA5",
|
|
771
|
+
GBP: "\xA3",
|
|
772
|
+
KRW: "\u20A9"
|
|
773
|
+
};
|
|
774
|
+
var DEFAULT_RATES = {
|
|
775
|
+
USD: 1,
|
|
776
|
+
CNY: 7.2,
|
|
777
|
+
EUR: 0.92,
|
|
778
|
+
JPY: 150,
|
|
779
|
+
GBP: 0.79,
|
|
780
|
+
KRW: 1350
|
|
781
|
+
};
|
|
782
|
+
function convertBalance(target, targetRate, amount, from) {
|
|
783
|
+
if (from === target) return amount;
|
|
784
|
+
const fromRate = DEFAULT_RATES[from] ?? 1;
|
|
785
|
+
const usd = from === "USD" ? amount : amount / fromRate;
|
|
786
|
+
return target === "USD" ? usd : usd * targetRate;
|
|
787
|
+
}
|
|
788
|
+
function balanceSymbol(currency) {
|
|
789
|
+
const sym = CURRENCIES[currency];
|
|
790
|
+
return sym ?? currency + " ";
|
|
791
|
+
}
|
|
792
|
+
function formatBalanceText(list, pref, rate) {
|
|
793
|
+
const native = pref ? list.find((x) => x.currency === pref) : void 0;
|
|
794
|
+
if (native) return balanceSymbol(native.currency) + formatBalanceAmount(native.total);
|
|
795
|
+
const base = list[0];
|
|
796
|
+
const baseAmt = parseFloat(base.total);
|
|
797
|
+
const converted = Number.isFinite(baseAmt) ? convertBalance(pref || base.currency, rate, baseAmt, base.currency) : baseAmt;
|
|
798
|
+
const shown = pref && base.currency !== pref ? converted.toLocaleString("en-US", { maximumFractionDigits: 2 }) : formatBalanceAmount(base.total);
|
|
799
|
+
return balanceSymbol(pref || base.currency) + shown;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/core/estimate.ts
|
|
803
|
+
function estimateTokens(text) {
|
|
804
|
+
if (!text || text.length === 0) return 0;
|
|
805
|
+
let ascii = 0;
|
|
806
|
+
let cjk = 0;
|
|
807
|
+
for (const c of text) {
|
|
808
|
+
const code = c.codePointAt(0) ?? 0;
|
|
809
|
+
if (code >= 19968 && code <= 40959) cjk++;
|
|
810
|
+
else if (code >= 12352 && code <= 12543) cjk++;
|
|
811
|
+
else if (code >= 44032 && code <= 55203) cjk++;
|
|
812
|
+
else if (code >= 4352 && code <= 4607) cjk++;
|
|
813
|
+
else if (code >= 11904 && code <= 12031) cjk++;
|
|
814
|
+
else ascii++;
|
|
815
|
+
}
|
|
816
|
+
const trimmed = text.trimStart();
|
|
817
|
+
const strippedFence = trimmed.replace(/^\x60{3}\w*\s*\n?/, "");
|
|
818
|
+
const jsonLike = (strippedFence.startsWith("{") || strippedFence.startsWith("[")) && /"[^"]+"\s*:/.test(text);
|
|
819
|
+
const codeLike = !jsonLike && /```|^import |^export |^function |^const |^let |^var |^class |^interface |^type |^def |^fn |^pub |^use |^mod |^package /m.test(text);
|
|
820
|
+
const asciiPerToken = jsonLike ? 3.5 : codeLike ? 3.5 : 4;
|
|
821
|
+
return Math.max(1, Math.ceil(ascii / asciiPerToken + cjk / 1));
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/_version.ts
|
|
825
|
+
var PLUGIN_VERSION = "1.7.0-beta.0";
|
|
826
|
+
|
|
827
|
+
// src/panel/TokenCachePanel.tsx
|
|
828
|
+
var MIN_PANEL_WIDTH = 20;
|
|
829
|
+
var DEFAULT_PANEL_WIDTH = 26;
|
|
830
|
+
var LABEL_GAP = 1;
|
|
831
|
+
var BAR_BRACKETS = 2;
|
|
832
|
+
var BAR_GAP = 1;
|
|
833
|
+
var PCT_FIXED_WIDTH = 5;
|
|
834
|
+
var HEADER_PREFIX = 2;
|
|
835
|
+
var UNIT_GAP = 1;
|
|
836
|
+
function TokenCachePanel(props) {
|
|
837
|
+
const [panelWidth, setPanelWidth] = createSignal(DEFAULT_PANEL_WIDTH);
|
|
838
|
+
const [open, setOpen] = createSignal(true);
|
|
839
|
+
const [detailOpen, setDetailOpen] = createSignal(true);
|
|
840
|
+
const [modelOpen, setModelOpen] = createSignal(true);
|
|
841
|
+
const [distOpen, setDistOpen] = createSignal(false);
|
|
842
|
+
const [skillsOpen, setSkillsOpen] = createSignal(true);
|
|
843
|
+
let boxEl;
|
|
844
|
+
createEffect(() => {
|
|
845
|
+
props.signals.setSidebarVisible(true);
|
|
846
|
+
onCleanup(() => props.signals.setSidebarVisible(false));
|
|
847
|
+
});
|
|
848
|
+
const {
|
|
849
|
+
currencySymbol,
|
|
850
|
+
setCurrencySymbol,
|
|
851
|
+
exchangeRate,
|
|
852
|
+
setExchangeRate,
|
|
853
|
+
langCode,
|
|
854
|
+
sectionDetail,
|
|
855
|
+
setSectionDetail,
|
|
856
|
+
sectionModel,
|
|
857
|
+
setSectionModel,
|
|
858
|
+
sectionDist,
|
|
859
|
+
setSectionDist,
|
|
860
|
+
sectionSkills,
|
|
861
|
+
setSectionSkills,
|
|
862
|
+
sectionBalance,
|
|
863
|
+
setSectionBalance,
|
|
864
|
+
balanceRefresh,
|
|
865
|
+
balanceProviderId,
|
|
866
|
+
setBalanceProviderId,
|
|
867
|
+
autoBalance,
|
|
868
|
+
setAutoBalance,
|
|
869
|
+
balanceUnsupported,
|
|
870
|
+
setBalanceUnsupported,
|
|
871
|
+
balanceState,
|
|
872
|
+
balanceCurrency,
|
|
873
|
+
setBalanceCurrency,
|
|
874
|
+
borderVisible,
|
|
875
|
+
setBorderVisible
|
|
876
|
+
} = props.signals;
|
|
877
|
+
const t = createT(() => langCode());
|
|
878
|
+
const [lastDist, setLastDist] = createSignal({
|
|
879
|
+
system: 0,
|
|
880
|
+
user: 0,
|
|
881
|
+
agent: 0,
|
|
882
|
+
toolCall: 0,
|
|
883
|
+
toolResult: 0,
|
|
884
|
+
output: 0,
|
|
885
|
+
reasoning: 0,
|
|
886
|
+
apiOutput: 0,
|
|
887
|
+
apiInput: 0,
|
|
888
|
+
stepCost: 0,
|
|
889
|
+
stepCount: 0
|
|
890
|
+
});
|
|
891
|
+
const [lastHasDist, setLastHasDist] = createSignal(false);
|
|
892
|
+
const [dataSignal, setDataSignal] = createSignal({
|
|
893
|
+
hitRate: 0,
|
|
894
|
+
read: 0,
|
|
895
|
+
write: 0,
|
|
896
|
+
freshInput: 0,
|
|
897
|
+
output: 0,
|
|
898
|
+
cost: 0,
|
|
899
|
+
saved: 0,
|
|
900
|
+
model: "",
|
|
901
|
+
inputRate: 0,
|
|
902
|
+
cacheReadRate: 0,
|
|
903
|
+
cacheWriteRate: 0,
|
|
904
|
+
hasPricing: false,
|
|
905
|
+
hasData: false,
|
|
906
|
+
trend: 0,
|
|
907
|
+
hasTrendData: false,
|
|
908
|
+
providerName: "",
|
|
909
|
+
sessionHitRate: 0,
|
|
910
|
+
dist: {
|
|
911
|
+
system: 0,
|
|
912
|
+
user: 0,
|
|
913
|
+
agent: 0,
|
|
914
|
+
toolCall: 0,
|
|
915
|
+
toolResult: 0,
|
|
916
|
+
output: 0,
|
|
917
|
+
reasoning: 0,
|
|
918
|
+
apiOutput: 0,
|
|
919
|
+
apiInput: 0,
|
|
920
|
+
stepCost: 0,
|
|
921
|
+
stepCount: 0
|
|
922
|
+
},
|
|
923
|
+
hasDistData: false,
|
|
924
|
+
skills: [],
|
|
925
|
+
hasSkills: false
|
|
926
|
+
});
|
|
927
|
+
const [refreshTick, setRefreshTick] = createSignal(0);
|
|
928
|
+
const providerName = createMemo(() => getBalanceProvider(balanceProviderId()).name);
|
|
929
|
+
createEffect(() => {
|
|
930
|
+
if (!autoBalance()) return;
|
|
931
|
+
const sid = props.signals.overrideSessionId() ?? props.sessionId;
|
|
932
|
+
const msgs = props.api.state.session.messages(sid);
|
|
933
|
+
let pid = "";
|
|
934
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
935
|
+
const m = msgs[i];
|
|
936
|
+
if (m.role === "assistant" && m.providerID) {
|
|
937
|
+
pid = m.providerID;
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (!pid) {
|
|
942
|
+
try {
|
|
943
|
+
const session = props.api.state.session.get(sid);
|
|
944
|
+
pid = session?.model?.providerID ?? "";
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (!pid) return;
|
|
949
|
+
const hit = matchBalanceProvider(pid);
|
|
950
|
+
if (hit) {
|
|
951
|
+
setBalanceUnsupported(false);
|
|
952
|
+
if (hit.id !== balanceProviderId()) {
|
|
953
|
+
setBalanceProviderId(hit.id);
|
|
954
|
+
props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
|
|
955
|
+
}
|
|
956
|
+
} else {
|
|
957
|
+
setBalanceUnsupported(true);
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
let lastMainSid = props.sessionId;
|
|
961
|
+
createEffect(() => {
|
|
962
|
+
const sid = props.sessionId;
|
|
963
|
+
if (sid !== lastMainSid) {
|
|
964
|
+
lastMainSid = sid;
|
|
965
|
+
if (props.signals.overrideSessionId()) {
|
|
966
|
+
props.signals.setOverrideSessionId(void 0);
|
|
967
|
+
props.api.kv.set(`${KV_PREFIX3}.session`, "");
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
createEffect(() => {
|
|
972
|
+
const sid = props.signals.overrideSessionId() ?? props.sessionId;
|
|
973
|
+
void refreshTick();
|
|
974
|
+
void partVersion();
|
|
975
|
+
const msgs = props.api.state.session.messages(sid);
|
|
976
|
+
const session = typeof props.api.state.session.get === "function" ? props.api.state.session.get(sid) : void 0;
|
|
977
|
+
let input = session?.tokens?.input ?? 0;
|
|
978
|
+
let read = session?.tokens?.cache?.read ?? 0;
|
|
979
|
+
let write = session?.tokens?.cache?.write ?? 0;
|
|
980
|
+
let output = session?.tokens?.output ?? 0;
|
|
981
|
+
let cost = session?.cost ?? 0;
|
|
982
|
+
let pid = session?.model?.providerID ?? "";
|
|
983
|
+
let mid = session?.model?.id ?? "";
|
|
984
|
+
const fallbackTokens = session?.tokens == null;
|
|
985
|
+
const fallbackCost = session?.cost == null;
|
|
986
|
+
const fallbackModel = !pid || !mid;
|
|
987
|
+
let prevMsgHitRate = -1, lastMsgHitRate = -1;
|
|
988
|
+
for (const msg of msgs) {
|
|
989
|
+
if (msg.role !== "assistant") continue;
|
|
990
|
+
const tok = msg.tokens;
|
|
991
|
+
if (!tok) continue;
|
|
992
|
+
const mit = num(tok.input) + num(tok.cache?.read) + num(tok.cache?.write), mrt = num(tok.cache?.read);
|
|
993
|
+
if (mit > 0) {
|
|
994
|
+
prevMsgHitRate = lastMsgHitRate;
|
|
995
|
+
lastMsgHitRate = mrt / mit * 100;
|
|
996
|
+
}
|
|
997
|
+
if (fallbackTokens) {
|
|
998
|
+
input += num(tok.input);
|
|
999
|
+
read += num(tok.cache?.read);
|
|
1000
|
+
write += num(tok.cache?.write);
|
|
1001
|
+
output += num(tok.output);
|
|
1002
|
+
}
|
|
1003
|
+
if (fallbackCost) {
|
|
1004
|
+
cost += num(msg.cost);
|
|
1005
|
+
}
|
|
1006
|
+
if (fallbackModel && msg.providerID && msg.modelID) {
|
|
1007
|
+
pid = msg.providerID;
|
|
1008
|
+
mid = msg.modelID;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
let saved = 0, inputRate = 0, cacheReadRate = 0, cacheWriteRate = 0;
|
|
1012
|
+
if (read > 0 && pid && mid && Array.isArray(props.api.state.provider)) for (const provider of props.api.state.provider) {
|
|
1013
|
+
if (provider.id !== pid) continue;
|
|
1014
|
+
const model2 = provider.models[mid];
|
|
1015
|
+
if (!model2?.cost) continue;
|
|
1016
|
+
inputRate = num(model2.cost.input);
|
|
1017
|
+
cacheReadRate = num(model2.cost.cache?.read);
|
|
1018
|
+
cacheWriteRate = num(model2.cost.cache?.write);
|
|
1019
|
+
if (inputRate > cacheReadRate) saved = read * (inputRate - cacheReadRate) / 1e6;
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
const hitRate = lastMsgHitRate >= 0 ? lastMsgHitRate : 0;
|
|
1023
|
+
const freshTotal = input + read + write, sessionHitRate = freshTotal > 0 ? read / freshTotal * 100 : 0;
|
|
1024
|
+
const model = mid.split("/").pop() ?? mid, hasPricing = inputRate > 0 || cacheReadRate > 0 || cacheWriteRate > 0;
|
|
1025
|
+
const hasTrendData = prevMsgHitRate >= 0 && lastMsgHitRate >= 0;
|
|
1026
|
+
const trend = hasTrendData ? lastMsgHitRate - prevMsgHitRate : 0, providerName2 = pid || "";
|
|
1027
|
+
const distData = untrack(() => {
|
|
1028
|
+
let dist = {
|
|
1029
|
+
system: 0,
|
|
1030
|
+
user: 0,
|
|
1031
|
+
agent: 0,
|
|
1032
|
+
toolCall: 0,
|
|
1033
|
+
toolResult: 0,
|
|
1034
|
+
output: 0,
|
|
1035
|
+
reasoning: 0,
|
|
1036
|
+
apiOutput: 0,
|
|
1037
|
+
apiInput: 0,
|
|
1038
|
+
stepCost: 0,
|
|
1039
|
+
stepCount: 0
|
|
1040
|
+
};
|
|
1041
|
+
let hasDistData = false;
|
|
1042
|
+
const loadedSkills = /* @__PURE__ */ new Map();
|
|
1043
|
+
try {
|
|
1044
|
+
const cfg = props.api.state.config;
|
|
1045
|
+
const agentName = String(session?.agent ?? cfg?.default_agent ?? "build");
|
|
1046
|
+
const agents = cfg?.agent;
|
|
1047
|
+
const agentCfg = agents?.[agentName];
|
|
1048
|
+
const sysPrompt = typeof agentCfg?.prompt === "string" ? agentCfg.prompt : "";
|
|
1049
|
+
if (sysPrompt) dist.system = estimateTokens(sysPrompt);
|
|
1050
|
+
let lastAssMsg;
|
|
1051
|
+
for (const msg of msgs) {
|
|
1052
|
+
if (msg.role === "user") {
|
|
1053
|
+
const um = msg;
|
|
1054
|
+
if (um.system) dist.system += estimateTokens(um.system);
|
|
1055
|
+
let parts = [];
|
|
1056
|
+
try {
|
|
1057
|
+
parts = props.api.state.part(msg.id);
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
for (const p of parts) {
|
|
1061
|
+
if (p.type === "text" && !p.synthetic && !p.ignored) dist.user += estimateTokens(p.text);
|
|
1062
|
+
else if (p.type === "file") {
|
|
1063
|
+
const fp = p;
|
|
1064
|
+
if (fp.source?.text?.value) dist.user += estimateTokens(fp.source.text.value);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} else if (msg.role === "assistant") {
|
|
1068
|
+
const am = msg;
|
|
1069
|
+
dist.output += num(am.tokens?.output);
|
|
1070
|
+
dist.reasoning += num(am.tokens?.reasoning);
|
|
1071
|
+
let parts = [];
|
|
1072
|
+
try {
|
|
1073
|
+
parts = props.api.state.part(msg.id);
|
|
1074
|
+
} catch {
|
|
1075
|
+
}
|
|
1076
|
+
for (const p of parts) {
|
|
1077
|
+
if (p.type === "tool") {
|
|
1078
|
+
const tp = p;
|
|
1079
|
+
let rawInput = "";
|
|
1080
|
+
try {
|
|
1081
|
+
rawInput = tp.state.raw ?? (tp.state.input != null ? JSON.stringify(tp.state.input) : "");
|
|
1082
|
+
} catch {
|
|
1083
|
+
}
|
|
1084
|
+
if (rawInput) dist.toolCall += estimateTokens(rawInput);
|
|
1085
|
+
if (tp.tool === "task" && tp.state?.input) {
|
|
1086
|
+
const ti = tp.state.input;
|
|
1087
|
+
const prompt = typeof ti.prompt === "string" ? ti.prompt : "";
|
|
1088
|
+
const desc = typeof ti.description === "string" ? ti.description : "";
|
|
1089
|
+
dist.agent += estimateTokens(prompt || desc);
|
|
1090
|
+
}
|
|
1091
|
+
if (tp.state.status === "completed") {
|
|
1092
|
+
const c = tp.state;
|
|
1093
|
+
if (c.output) dist.toolResult += estimateTokens(c.output);
|
|
1094
|
+
} else if (tp.state.status === "error") {
|
|
1095
|
+
const e = tp.state;
|
|
1096
|
+
if (e.error) dist.toolResult += estimateTokens(e.error);
|
|
1097
|
+
}
|
|
1098
|
+
if (tp.tool === "skill" && tp.state.status === "completed") {
|
|
1099
|
+
let name = tp.state.metadata?.name;
|
|
1100
|
+
if (typeof name !== "string") {
|
|
1101
|
+
const m = typeof tp.state.output === "string" ? tp.state.output.match(/^#{1,2}\s*Skill:\s*(.+)/m) : null;
|
|
1102
|
+
if (m) name = m[1].trim();
|
|
1103
|
+
}
|
|
1104
|
+
if (typeof name === "string") {
|
|
1105
|
+
const tokens = typeof tp.state.output === "string" ? estimateTokens(tp.state.output) : 0;
|
|
1106
|
+
const existing = loadedSkills.get(name);
|
|
1107
|
+
if (!existing || existing.tokens < tokens) {
|
|
1108
|
+
loadedSkills.set(name, {
|
|
1109
|
+
name,
|
|
1110
|
+
tokens
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
} else if (p.type === "subtask") {
|
|
1116
|
+
const sub = p;
|
|
1117
|
+
dist.agent += estimateTokens(sub.prompt || sub.description || "");
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1123
|
+
if (msgs[i].role !== "assistant") continue;
|
|
1124
|
+
const tok = msgs[i].tokens;
|
|
1125
|
+
if (tok && ((tok.input ?? 0) > 0 || (tok.cache?.read ?? 0) > 0 || (tok.cache?.write ?? 0) > 0)) {
|
|
1126
|
+
lastAssMsg = msgs[i];
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
dist.apiInput = num(lastAssMsg?.tokens?.input) + num(lastAssMsg?.tokens?.cache?.read) + num(lastAssMsg?.tokens?.cache?.write);
|
|
1131
|
+
dist.apiOutput = num(lastAssMsg?.tokens?.output);
|
|
1132
|
+
if (lastAssMsg) {
|
|
1133
|
+
const roundParent = lastAssMsg.parentID;
|
|
1134
|
+
let lastCost;
|
|
1135
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1136
|
+
const m = msgs[i];
|
|
1137
|
+
if (m.role !== "assistant") continue;
|
|
1138
|
+
if (m.parentID !== roundParent) break;
|
|
1139
|
+
let parts = [];
|
|
1140
|
+
try {
|
|
1141
|
+
parts = props.api.state.part(m.id);
|
|
1142
|
+
} catch {
|
|
1143
|
+
}
|
|
1144
|
+
for (const p of parts) {
|
|
1145
|
+
if (p.type !== "step-finish") continue;
|
|
1146
|
+
dist.stepCount++;
|
|
1147
|
+
const sc = p.cost;
|
|
1148
|
+
if (lastCost === void 0 && typeof sc === "number" && Number.isFinite(sc)) lastCost = sc;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
if (lastCost !== void 0) dist.stepCost = lastCost;
|
|
1152
|
+
}
|
|
1153
|
+
hasDistData = dist.system + dist.user + dist.agent + dist.toolCall + dist.toolResult > 0 || dist.apiOutput > 0 || dist.apiInput > 0 || dist.reasoning > 0;
|
|
1154
|
+
} catch {
|
|
1155
|
+
}
|
|
1156
|
+
const finalDist = hasDistData ? dist : lastDist(), finalHasDist = hasDistData || lastHasDist();
|
|
1157
|
+
const skills = [...loadedSkills.values()];
|
|
1158
|
+
return {
|
|
1159
|
+
finalDist,
|
|
1160
|
+
finalHasDist,
|
|
1161
|
+
skills
|
|
1162
|
+
};
|
|
1163
|
+
});
|
|
1164
|
+
setDataSignal({
|
|
1165
|
+
hitRate,
|
|
1166
|
+
read,
|
|
1167
|
+
write,
|
|
1168
|
+
freshInput: input,
|
|
1169
|
+
output,
|
|
1170
|
+
cost,
|
|
1171
|
+
saved,
|
|
1172
|
+
model,
|
|
1173
|
+
inputRate,
|
|
1174
|
+
cacheReadRate,
|
|
1175
|
+
cacheWriteRate,
|
|
1176
|
+
hasPricing,
|
|
1177
|
+
hasData: read > 0 || write > 0 || input > 0 || output > 0 || cost > 0,
|
|
1178
|
+
trend,
|
|
1179
|
+
hasTrendData,
|
|
1180
|
+
providerName: providerName2,
|
|
1181
|
+
sessionHitRate,
|
|
1182
|
+
dist: distData.finalDist,
|
|
1183
|
+
hasDistData: distData.finalHasDist,
|
|
1184
|
+
skills: distData.skills,
|
|
1185
|
+
hasSkills: distData.skills.length > 0
|
|
1186
|
+
});
|
|
1187
|
+
});
|
|
1188
|
+
const data = createMemo(() => {
|
|
1189
|
+
return dataSignal();
|
|
1190
|
+
});
|
|
1191
|
+
createEffect(() => {
|
|
1192
|
+
const d = data();
|
|
1193
|
+
if (d.hasDistData) {
|
|
1194
|
+
setLastDist({
|
|
1195
|
+
...d.dist
|
|
1196
|
+
});
|
|
1197
|
+
setLastHasDist(true);
|
|
1198
|
+
try {
|
|
1199
|
+
props.api.kv.set(`${KV_PREFIX3}.dist_snapshot`, {
|
|
1200
|
+
...d.dist
|
|
1201
|
+
});
|
|
1202
|
+
} catch {
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
const [partVersion, setPartVersion] = createSignal(0);
|
|
1207
|
+
const KV_PREFIX3 = "cache_panel";
|
|
1208
|
+
const persistFold = (key, val) => {
|
|
1209
|
+
try {
|
|
1210
|
+
props.api.kv.set(`${KV_PREFIX3}.${key}`, val);
|
|
1211
|
+
} catch {
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
onMount(() => {
|
|
1215
|
+
setPanelWidth(DEFAULT_PANEL_WIDTH);
|
|
1216
|
+
try {
|
|
1217
|
+
setOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.open`, false)));
|
|
1218
|
+
setDetailOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.detail`, true)));
|
|
1219
|
+
setModelOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.model`, true)));
|
|
1220
|
+
setDistOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.dist`, false)));
|
|
1221
|
+
setSkillsOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.skills`, true)));
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
const doRestore = () => {
|
|
1225
|
+
try {
|
|
1226
|
+
const sym = props.api.kv.get(`${KV_PREFIX3}.currency`);
|
|
1227
|
+
const rate = props.api.kv.get(`${KV_PREFIX3}.rate`);
|
|
1228
|
+
if (typeof sym === "string") setCurrencySymbol(sym);
|
|
1229
|
+
if (typeof rate === "number" && rate > 0) setExchangeRate(rate);
|
|
1230
|
+
const balCur = props.api.kv.get(`${KV_PREFIX3}.balance_currency`);
|
|
1231
|
+
if (typeof balCur === "string") setBalanceCurrency(balCur);
|
|
1232
|
+
const savedProvider = props.api.kv.get(`${KV_PREFIX3}.balance.provider`);
|
|
1233
|
+
if (typeof savedProvider === "string" && balanceProviders.some((p) => p.id === savedProvider)) {
|
|
1234
|
+
setBalanceProviderId(savedProvider);
|
|
1235
|
+
setBalanceUnsupported(false);
|
|
1236
|
+
}
|
|
1237
|
+
const savedAuto = props.api.kv.get(`${KV_PREFIX3}.balance.auto`);
|
|
1238
|
+
if (typeof savedAuto === "boolean") setAutoBalance(savedAuto);
|
|
1239
|
+
const legacyKey = props.api.kv.get(`${KV_PREFIX3}.ds_key`, "");
|
|
1240
|
+
if (legacyKey) {
|
|
1241
|
+
const dsKey = props.api.kv.get(`${KV_PREFIX3}.balance.deepseek.key`, "");
|
|
1242
|
+
if (!dsKey) props.api.kv.set(`${KV_PREFIX3}.balance.deepseek.key`, legacyKey);
|
|
1243
|
+
props.api.kv.set(`${KV_PREFIX3}.ds_key`, "");
|
|
1244
|
+
}
|
|
1245
|
+
props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
|
|
1246
|
+
setSectionDetail(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.detail`, true)));
|
|
1247
|
+
setSectionModel(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.model`, true)));
|
|
1248
|
+
setSectionDist(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.dist`, true)));
|
|
1249
|
+
setSectionSkills(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.skills`, true)));
|
|
1250
|
+
setSectionBalance(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.balance`, true)));
|
|
1251
|
+
const bv = props.api.kv.get(`${KV_PREFIX3}.border`, true);
|
|
1252
|
+
setBorderVisible(bv !== false);
|
|
1253
|
+
const cachedDist = props.api.kv.get(`${KV_PREFIX3}.dist_snapshot`);
|
|
1254
|
+
if (cachedDist) {
|
|
1255
|
+
setLastDist(cachedDist);
|
|
1256
|
+
setLastHasDist(true);
|
|
1257
|
+
}
|
|
1258
|
+
} catch {
|
|
1259
|
+
}
|
|
1260
|
+
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
1261
|
+
setPanelWidth(Math.max(MIN_PANEL_WIDTH, boxEl.width));
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
if (props.api.kv.ready) {
|
|
1265
|
+
doRestore();
|
|
1266
|
+
} else {
|
|
1267
|
+
const MAX_POLL = 100;
|
|
1268
|
+
let tries = 0;
|
|
1269
|
+
const pollRestore = () => {
|
|
1270
|
+
if (!props.api.kv.ready) {
|
|
1271
|
+
if (++tries > MAX_POLL) {
|
|
1272
|
+
doRestore();
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
setTimeout(pollRestore, 10);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
doRestore();
|
|
1279
|
+
};
|
|
1280
|
+
pollRestore();
|
|
1281
|
+
}
|
|
1282
|
+
let partTimer;
|
|
1283
|
+
const bumpPartVersion = () => {
|
|
1284
|
+
clearTimeout(partTimer);
|
|
1285
|
+
partTimer = setTimeout(() => setPartVersion((v) => v + 1), 100);
|
|
1286
|
+
};
|
|
1287
|
+
const unsubPart = props.api.event.on("message.part.updated", () => {
|
|
1288
|
+
bumpPartVersion();
|
|
1289
|
+
setRefreshTick((v) => v + 1);
|
|
1290
|
+
});
|
|
1291
|
+
const unsubMsg = props.api.event.on("message.updated", () => {
|
|
1292
|
+
bumpPartVersion();
|
|
1293
|
+
setRefreshTick((v) => v + 1);
|
|
1294
|
+
});
|
|
1295
|
+
const unsubSession = props.api.event.on("session.updated", () => {
|
|
1296
|
+
setRefreshTick((v) => v + 1);
|
|
1297
|
+
});
|
|
1298
|
+
setRefreshTick((v) => v + 1);
|
|
1299
|
+
onCleanup(() => {
|
|
1300
|
+
clearTimeout(partTimer);
|
|
1301
|
+
unsubPart();
|
|
1302
|
+
unsubMsg();
|
|
1303
|
+
unsubSession();
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1306
|
+
const pal = createMemo(() => {
|
|
1307
|
+
const t2 = props.theme;
|
|
1308
|
+
const sat = (k, fb) => desaturateTo(t2[k], MAX_SAT, fb);
|
|
1309
|
+
return {
|
|
1310
|
+
primary: sat("primary", FALLBACK.primary),
|
|
1311
|
+
text: sat("text", FALLBACK.text),
|
|
1312
|
+
muted: sat("textMuted", FALLBACK.muted),
|
|
1313
|
+
success: sat("success", FALLBACK.success),
|
|
1314
|
+
warning: sat("warning", FALLBACK.warning),
|
|
1315
|
+
error: sat("error", FALLBACK.error),
|
|
1316
|
+
border: sat("border", FALLBACK.border)
|
|
1317
|
+
};
|
|
1318
|
+
});
|
|
1319
|
+
const hitColor = createMemo(() => {
|
|
1320
|
+
const r = data().hitRate;
|
|
1321
|
+
if (r >= 85) return pal().success;
|
|
1322
|
+
if (r >= 70) return pal().warning;
|
|
1323
|
+
return pal().error;
|
|
1324
|
+
});
|
|
1325
|
+
const gutter = createMemo(() => borderVisible() ? 6 : 0);
|
|
1326
|
+
const sep = createMemo(() => "\u2500".repeat(Math.max(1, panelWidth() - gutter())));
|
|
1327
|
+
function trendLabel(t2) {
|
|
1328
|
+
if (Math.abs(t2) < 0.05) return "-";
|
|
1329
|
+
return (t2 > 0 ? "\u2191" : "\u2193") + Math.abs(t2).toFixed(1) + "%";
|
|
1330
|
+
}
|
|
1331
|
+
const barW = createMemo(() => {
|
|
1332
|
+
const trendSpace = data().hasTrendData ? LABEL_GAP + visualWidth(trendLabel(data().trend)) : 0;
|
|
1333
|
+
const overhead = visualWidth(t("hit")) + LABEL_GAP + BAR_BRACKETS + BAR_GAP + PCT_FIXED_WIDTH + trendSpace + gutter();
|
|
1334
|
+
return Math.max(3, panelWidth() - overhead);
|
|
1335
|
+
});
|
|
1336
|
+
const bar = createMemo(() => progressBar(data().hitRate, barW()));
|
|
1337
|
+
const pct = createMemo(() => (Math.floor(data().hitRate * 10) / 10).toFixed(1) + "%");
|
|
1338
|
+
createEffect(() => {
|
|
1339
|
+
borderVisible();
|
|
1340
|
+
if (boxEl && typeof boxEl.width === "number" && boxEl.width > 0) {
|
|
1341
|
+
const w = Math.max(MIN_PANEL_WIDTH, boxEl.width);
|
|
1342
|
+
setPanelWidth((prev) => prev === w ? prev : w);
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
const justify = (label, value, unit = "") => {
|
|
1346
|
+
const gauge = panelWidth() - gutter();
|
|
1347
|
+
const used = visualWidth(label) + visualWidth(value) + (unit ? visualWidth(unit) + UNIT_GAP : 0);
|
|
1348
|
+
const gap = Math.max(1, gauge - used);
|
|
1349
|
+
return label + " ".repeat(gap) + value + (unit ? " " + unit : "");
|
|
1350
|
+
};
|
|
1351
|
+
return (() => {
|
|
1352
|
+
var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createElement("span"), _el$4 = _$createElement("span"), _el$5 = _$createElement("b");
|
|
1353
|
+
_$insertNode(_el$, _el$2);
|
|
1354
|
+
var _ref$ = boxEl;
|
|
1355
|
+
typeof _ref$ === "function" ? _$use(_ref$, _el$) : boxEl = _el$;
|
|
1356
|
+
_$spread(_el$, _$mergeProps({
|
|
1357
|
+
get border() {
|
|
1358
|
+
return borderVisible();
|
|
1359
|
+
}
|
|
1360
|
+
}, () => borderVisible() ? {
|
|
1361
|
+
borderColor: pal().border
|
|
1362
|
+
} : {}, {
|
|
1363
|
+
"paddingTop": 0,
|
|
1364
|
+
"paddingBottom": 0,
|
|
1365
|
+
get paddingLeft() {
|
|
1366
|
+
return borderVisible() ? 2 : 0;
|
|
1367
|
+
},
|
|
1368
|
+
get paddingRight() {
|
|
1369
|
+
return borderVisible() ? 2 : 0;
|
|
1370
|
+
},
|
|
1371
|
+
"flexDirection": "column",
|
|
1372
|
+
"gap": 0,
|
|
1373
|
+
"onSizeChange": () => {
|
|
1374
|
+
const w = boxEl ? Math.max(MIN_PANEL_WIDTH, boxEl.width ?? 0) : DEFAULT_PANEL_WIDTH;
|
|
1375
|
+
setPanelWidth((prev) => prev === w ? prev : w);
|
|
1376
|
+
}
|
|
1377
|
+
}), true);
|
|
1378
|
+
_$insertNode(_el$2, _el$3);
|
|
1379
|
+
_$insertNode(_el$2, _el$4);
|
|
1380
|
+
_$setProp(_el$2, "onMouseUp", () => setOpen((o) => {
|
|
1381
|
+
const n = !o;
|
|
1382
|
+
persistFold("open", n);
|
|
1383
|
+
return n;
|
|
1384
|
+
}));
|
|
1385
|
+
_$insert(_el$3, () => open() ? "\u25BC " : "\u25B6 ");
|
|
1386
|
+
_$insertNode(_el$4, _el$5);
|
|
1387
|
+
_$insert(_el$5, () => t("title"));
|
|
1388
|
+
_$insert(_el$4, _$createComponent(Show, {
|
|
1389
|
+
get when() {
|
|
1390
|
+
return open();
|
|
1391
|
+
},
|
|
1392
|
+
get children() {
|
|
1393
|
+
var _el$6 = _$createElement("span"), _el$7 = _$createTextNode(` v`);
|
|
1394
|
+
_$insertNode(_el$6, _el$7);
|
|
1395
|
+
_$insert(_el$6, PLUGIN_VERSION, null);
|
|
1396
|
+
_$effect((_$p) => _$setProp(_el$6, "style", {
|
|
1397
|
+
fg: dimColor(pal().muted, 0.75)
|
|
1398
|
+
}, _$p));
|
|
1399
|
+
return _el$6;
|
|
1400
|
+
}
|
|
1401
|
+
}), null);
|
|
1402
|
+
_$insert(_el$2, _$createComponent(Show, {
|
|
1403
|
+
get when() {
|
|
1404
|
+
return _$memo(() => !!!open())() && data().hasData;
|
|
1405
|
+
},
|
|
1406
|
+
get children() {
|
|
1407
|
+
return [_$createComponent(Show, {
|
|
1408
|
+
get when() {
|
|
1409
|
+
return data().hasTrendData;
|
|
1410
|
+
},
|
|
1411
|
+
get children() {
|
|
1412
|
+
return [(() => {
|
|
1413
|
+
var _el$8 = _$createElement("span");
|
|
1414
|
+
_$insert(_el$8, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t("title")) - visualWidth(pct() + " " + t("hitFolded") + " " + trendLabel(data().trend)))));
|
|
1415
|
+
return _el$8;
|
|
1416
|
+
})(), (() => {
|
|
1417
|
+
var _el$9 = _$createElement("span"), _el$0 = _$createTextNode(` `);
|
|
1418
|
+
_$insertNode(_el$9, _el$0);
|
|
1419
|
+
_$insert(_el$9, pct, _el$0);
|
|
1420
|
+
_$insert(_el$9, () => t("hitFolded"), null);
|
|
1421
|
+
_$effect((_$p) => _$setProp(_el$9, "style", {
|
|
1422
|
+
fg: hitColor()
|
|
1423
|
+
}, _$p));
|
|
1424
|
+
return _el$9;
|
|
1425
|
+
})(), (() => {
|
|
1426
|
+
var _el$1 = _$createElement("span"), _el$10 = _$createTextNode(` `);
|
|
1427
|
+
_$insertNode(_el$1, _el$10);
|
|
1428
|
+
_$insert(_el$1, () => trendLabel(data().trend), null);
|
|
1429
|
+
_$effect((_$p) => _$setProp(_el$1, "style", {
|
|
1430
|
+
fg: Math.abs(data().trend) >= 0.05 ? data().trend > 0 ? pal().success : pal().error : pal().text
|
|
1431
|
+
}, _$p));
|
|
1432
|
+
return _el$1;
|
|
1433
|
+
})()];
|
|
1434
|
+
}
|
|
1435
|
+
}), _$createComponent(Show, {
|
|
1436
|
+
get when() {
|
|
1437
|
+
return !data().hasTrendData;
|
|
1438
|
+
},
|
|
1439
|
+
get children() {
|
|
1440
|
+
return [(() => {
|
|
1441
|
+
var _el$11 = _$createElement("span");
|
|
1442
|
+
_$insert(_el$11, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t("title")) - visualWidth(pct() + " " + t("hitFolded")))));
|
|
1443
|
+
return _el$11;
|
|
1444
|
+
})(), (() => {
|
|
1445
|
+
var _el$12 = _$createElement("span"), _el$13 = _$createTextNode(` `);
|
|
1446
|
+
_$insertNode(_el$12, _el$13);
|
|
1447
|
+
_$insert(_el$12, pct, _el$13);
|
|
1448
|
+
_$insert(_el$12, () => t("hitFolded"), null);
|
|
1449
|
+
_$effect((_$p) => _$setProp(_el$12, "style", {
|
|
1450
|
+
fg: hitColor()
|
|
1451
|
+
}, _$p));
|
|
1452
|
+
return _el$12;
|
|
1453
|
+
})()];
|
|
1454
|
+
}
|
|
1455
|
+
})];
|
|
1456
|
+
}
|
|
1457
|
+
}), null);
|
|
1458
|
+
_$insert(_el$, _$createComponent(Show, {
|
|
1459
|
+
get when() {
|
|
1460
|
+
return open();
|
|
1461
|
+
},
|
|
1462
|
+
get children() {
|
|
1463
|
+
return [_$createComponent(Show, {
|
|
1464
|
+
get when() {
|
|
1465
|
+
return props.signals.overrideSessionId();
|
|
1466
|
+
},
|
|
1467
|
+
get children() {
|
|
1468
|
+
return (() => {
|
|
1469
|
+
const prefix = " \u21B3 " + t("subPrefix");
|
|
1470
|
+
const maxSidW = Math.max(6, panelWidth() - visualWidth(prefix));
|
|
1471
|
+
return (() => {
|
|
1472
|
+
var _el$70 = _$createElement("text"), _el$71 = _$createElement("span"), _el$72 = _$createElement("span");
|
|
1473
|
+
_$insertNode(_el$70, _el$71);
|
|
1474
|
+
_$insertNode(_el$70, _el$72);
|
|
1475
|
+
_$insert(_el$71, prefix);
|
|
1476
|
+
_$insert(_el$72, () => truncateVisual(props.signals.overrideSessionId(), maxSidW));
|
|
1477
|
+
_$effect((_p$) => {
|
|
1478
|
+
var _v$17 = {
|
|
1479
|
+
fg: pal().muted
|
|
1480
|
+
}, _v$18 = {
|
|
1481
|
+
fg: pal().text
|
|
1482
|
+
};
|
|
1483
|
+
_v$17 !== _p$.e && (_p$.e = _$setProp(_el$71, "style", _v$17, _p$.e));
|
|
1484
|
+
_v$18 !== _p$.t && (_p$.t = _$setProp(_el$72, "style", _v$18, _p$.t));
|
|
1485
|
+
return _p$;
|
|
1486
|
+
}, {
|
|
1487
|
+
e: void 0,
|
|
1488
|
+
t: void 0
|
|
1489
|
+
});
|
|
1490
|
+
return _el$70;
|
|
1491
|
+
})();
|
|
1492
|
+
})();
|
|
1493
|
+
}
|
|
1494
|
+
}), _$createComponent(Show, {
|
|
1495
|
+
get when() {
|
|
1496
|
+
return data().hasData;
|
|
1497
|
+
},
|
|
1498
|
+
get fallback() {
|
|
1499
|
+
return [(() => {
|
|
1500
|
+
var _el$73 = _$createElement("text");
|
|
1501
|
+
_$insert(_el$73, sep);
|
|
1502
|
+
_$effect((_$p) => _$setProp(_el$73, "fg", pal().muted, _$p));
|
|
1503
|
+
return _el$73;
|
|
1504
|
+
})(), (() => {
|
|
1505
|
+
var _el$74 = _$createElement("text"), _el$75 = _$createElement("span"), _el$77 = _$createElement("span");
|
|
1506
|
+
_$insertNode(_el$74, _el$75);
|
|
1507
|
+
_$insertNode(_el$74, _el$77);
|
|
1508
|
+
_$insertNode(_el$75, _$createTextNode(`> `));
|
|
1509
|
+
_$insert(_el$77, () => t("noData"));
|
|
1510
|
+
_$effect((_p$) => {
|
|
1511
|
+
var _v$19 = {
|
|
1512
|
+
fg: pal().muted
|
|
1513
|
+
}, _v$20 = {
|
|
1514
|
+
fg: pal().muted
|
|
1515
|
+
};
|
|
1516
|
+
_v$19 !== _p$.e && (_p$.e = _$setProp(_el$75, "style", _v$19, _p$.e));
|
|
1517
|
+
_v$20 !== _p$.t && (_p$.t = _$setProp(_el$77, "style", _v$20, _p$.t));
|
|
1518
|
+
return _p$;
|
|
1519
|
+
}, {
|
|
1520
|
+
e: void 0,
|
|
1521
|
+
t: void 0
|
|
1522
|
+
});
|
|
1523
|
+
return _el$74;
|
|
1524
|
+
})()];
|
|
1525
|
+
},
|
|
1526
|
+
get children() {
|
|
1527
|
+
return [(() => {
|
|
1528
|
+
var _el$14 = _$createElement("text");
|
|
1529
|
+
_$insert(_el$14, sep);
|
|
1530
|
+
_$effect((_$p) => _$setProp(_el$14, "fg", pal().muted, _$p));
|
|
1531
|
+
return _el$14;
|
|
1532
|
+
})(), (() => {
|
|
1533
|
+
var _el$15 = _$createElement("text"), _el$16 = _$createElement("span"), _el$17 = _$createTextNode(` `), _el$18 = _$createElement("span"), _el$19 = _$createTextNode(`[`), _el$20 = _$createTextNode(`] `), _el$21 = _$createElement("span");
|
|
1534
|
+
_$insertNode(_el$15, _el$16);
|
|
1535
|
+
_$insertNode(_el$15, _el$18);
|
|
1536
|
+
_$insertNode(_el$15, _el$21);
|
|
1537
|
+
_$insertNode(_el$16, _el$17);
|
|
1538
|
+
_$insert(_el$16, () => t("hit"), _el$17);
|
|
1539
|
+
_$insertNode(_el$18, _el$19);
|
|
1540
|
+
_$insertNode(_el$18, _el$20);
|
|
1541
|
+
_$insert(_el$18, bar, _el$20);
|
|
1542
|
+
_$insert(_el$21, pct);
|
|
1543
|
+
_$insert(_el$15, _$createComponent(Show, {
|
|
1544
|
+
get when() {
|
|
1545
|
+
return data().hasTrendData;
|
|
1546
|
+
},
|
|
1547
|
+
get children() {
|
|
1548
|
+
var _el$22 = _$createElement("span"), _el$23 = _$createTextNode(` `);
|
|
1549
|
+
_$insertNode(_el$22, _el$23);
|
|
1550
|
+
_$insert(_el$22, () => trendLabel(data().trend), null);
|
|
1551
|
+
_$effect((_$p) => _$setProp(_el$22, "style", {
|
|
1552
|
+
fg: Math.abs(data().trend) >= 0.05 ? data().trend > 0 ? pal().success : pal().error : pal().text
|
|
1553
|
+
}, _$p));
|
|
1554
|
+
return _el$22;
|
|
1555
|
+
}
|
|
1556
|
+
}), null);
|
|
1557
|
+
_$effect((_p$) => {
|
|
1558
|
+
var _v$ = {
|
|
1559
|
+
fg: pal().text
|
|
1560
|
+
}, _v$2 = {
|
|
1561
|
+
fg: hitColor()
|
|
1562
|
+
}, _v$3 = {
|
|
1563
|
+
fg: pal().text
|
|
1564
|
+
};
|
|
1565
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$16, "style", _v$, _p$.e));
|
|
1566
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$18, "style", _v$2, _p$.t));
|
|
1567
|
+
_v$3 !== _p$.a && (_p$.a = _$setProp(_el$21, "style", _v$3, _p$.a));
|
|
1568
|
+
return _p$;
|
|
1569
|
+
}, {
|
|
1570
|
+
e: void 0,
|
|
1571
|
+
t: void 0,
|
|
1572
|
+
a: void 0
|
|
1573
|
+
});
|
|
1574
|
+
return _el$15;
|
|
1575
|
+
})(), (() => {
|
|
1576
|
+
var _el$24 = _$createElement("text");
|
|
1577
|
+
_$insert(_el$24, () => justify(t("totalHit"), (Math.floor(data().sessionHitRate * 10) / 10).toFixed(1) + "%"));
|
|
1578
|
+
_$effect((_$p) => _$setProp(_el$24, "fg", pal().muted, _$p));
|
|
1579
|
+
return _el$24;
|
|
1580
|
+
})(), _$createComponent(Show, {
|
|
1581
|
+
get when() {
|
|
1582
|
+
return sectionDetail();
|
|
1583
|
+
},
|
|
1584
|
+
get children() {
|
|
1585
|
+
return [(() => {
|
|
1586
|
+
var _el$25 = _$createElement("text"), _el$26 = _$createElement("span"), _el$27 = _$createElement("span"), _el$28 = _$createElement("b"), _el$29 = _$createElement("span");
|
|
1587
|
+
_$insertNode(_el$25, _el$26);
|
|
1588
|
+
_$insertNode(_el$25, _el$27);
|
|
1589
|
+
_$insertNode(_el$25, _el$29);
|
|
1590
|
+
_$setProp(_el$25, "onMouseUp", () => setDetailOpen((o) => {
|
|
1591
|
+
const n = !o;
|
|
1592
|
+
persistFold("detail", n);
|
|
1593
|
+
return n;
|
|
1594
|
+
}));
|
|
1595
|
+
_$insert(_el$26, () => detailOpen() ? "\u25BC " : "\u25B6 ");
|
|
1596
|
+
_$insertNode(_el$27, _el$28);
|
|
1597
|
+
_$insert(_el$28, () => t("secDetail"));
|
|
1598
|
+
_$insert(_el$29, () => sep().slice(visualWidth((detailOpen() ? "\u25BC " : "\u25B6 ") + t("secDetail"))));
|
|
1599
|
+
_$effect((_p$) => {
|
|
1600
|
+
var _v$4 = {
|
|
1601
|
+
fg: pal().muted
|
|
1602
|
+
}, _v$5 = {
|
|
1603
|
+
fg: pal().primary
|
|
1604
|
+
}, _v$6 = {
|
|
1605
|
+
fg: pal().muted
|
|
1606
|
+
};
|
|
1607
|
+
_v$4 !== _p$.e && (_p$.e = _$setProp(_el$26, "style", _v$4, _p$.e));
|
|
1608
|
+
_v$5 !== _p$.t && (_p$.t = _$setProp(_el$27, "style", _v$5, _p$.t));
|
|
1609
|
+
_v$6 !== _p$.a && (_p$.a = _$setProp(_el$29, "style", _v$6, _p$.a));
|
|
1610
|
+
return _p$;
|
|
1611
|
+
}, {
|
|
1612
|
+
e: void 0,
|
|
1613
|
+
t: void 0,
|
|
1614
|
+
a: void 0
|
|
1615
|
+
});
|
|
1616
|
+
return _el$25;
|
|
1617
|
+
})(), _$createComponent(Show, {
|
|
1618
|
+
get when() {
|
|
1619
|
+
return detailOpen();
|
|
1620
|
+
},
|
|
1621
|
+
get children() {
|
|
1622
|
+
return [_$createComponent(Show, {
|
|
1623
|
+
get when() {
|
|
1624
|
+
return data().read > 0;
|
|
1625
|
+
},
|
|
1626
|
+
get children() {
|
|
1627
|
+
var _el$30 = _$createElement("text");
|
|
1628
|
+
_$insert(_el$30, () => justify(t("read"), fmt(data().read), t("tok")));
|
|
1629
|
+
_$effect((_$p) => _$setProp(_el$30, "fg", pal().muted, _$p));
|
|
1630
|
+
return _el$30;
|
|
1631
|
+
}
|
|
1632
|
+
}), _$createComponent(Show, {
|
|
1633
|
+
get when() {
|
|
1634
|
+
return data().write > 0;
|
|
1635
|
+
},
|
|
1636
|
+
get children() {
|
|
1637
|
+
var _el$31 = _$createElement("text");
|
|
1638
|
+
_$insert(_el$31, () => justify(t("write"), fmt(data().write), t("tok")));
|
|
1639
|
+
_$effect((_$p) => _$setProp(_el$31, "fg", pal().muted, _$p));
|
|
1640
|
+
return _el$31;
|
|
1641
|
+
}
|
|
1642
|
+
}), (() => {
|
|
1643
|
+
var _el$32 = _$createElement("text");
|
|
1644
|
+
_$insert(_el$32, () => justify(t("miss"), fmt(data().freshInput + data().write), t("tok")));
|
|
1645
|
+
_$effect((_$p) => _$setProp(_el$32, "fg", pal().muted, _$p));
|
|
1646
|
+
return _el$32;
|
|
1647
|
+
})(), (() => {
|
|
1648
|
+
var _el$33 = _$createElement("text");
|
|
1649
|
+
_$insert(_el$33, () => justify(t("out"), fmt(data().output), t("tok")));
|
|
1650
|
+
_$effect((_$p) => _$setProp(_el$33, "fg", pal().muted, _$p));
|
|
1651
|
+
return _el$33;
|
|
1652
|
+
})(), _$createComponent(Show, {
|
|
1653
|
+
get when() {
|
|
1654
|
+
return data().dist.stepCount >= 2;
|
|
1655
|
+
},
|
|
1656
|
+
get children() {
|
|
1657
|
+
var _el$34 = _$createElement("text");
|
|
1658
|
+
_$insert(_el$34, () => justify(t("stepsCount", {
|
|
1659
|
+
n: data().dist.stepCount
|
|
1660
|
+
}), fmtCost(data().dist.stepCost, currencySymbol(), exchangeRate())));
|
|
1661
|
+
_$effect((_$p) => _$setProp(_el$34, "fg", pal().muted, _$p));
|
|
1662
|
+
return _el$34;
|
|
1663
|
+
}
|
|
1664
|
+
}), _$createComponent(Show, {
|
|
1665
|
+
get when() {
|
|
1666
|
+
return data().saved > 0;
|
|
1667
|
+
},
|
|
1668
|
+
get children() {
|
|
1669
|
+
var _el$35 = _$createElement("text"), _el$36 = _$createElement("span"), _el$37 = _$createElement("span"), _el$38 = _$createElement("span"), _el$39 = _$createTextNode(`~`);
|
|
1670
|
+
_$insertNode(_el$35, _el$36);
|
|
1671
|
+
_$insertNode(_el$35, _el$37);
|
|
1672
|
+
_$insertNode(_el$35, _el$38);
|
|
1673
|
+
_$insert(_el$36, () => t("saved"));
|
|
1674
|
+
_$insert(_el$37, () => " ".repeat(Math.max(1, panelWidth() - gutter() - visualWidth(t("saved")) - visualWidth("~" + fmtCost(data().saved, currencySymbol(), exchangeRate())))));
|
|
1675
|
+
_$insertNode(_el$38, _el$39);
|
|
1676
|
+
_$insert(_el$38, () => fmtCost(data().saved, currencySymbol(), exchangeRate()), null);
|
|
1677
|
+
_$effect((_p$) => {
|
|
1678
|
+
var _v$7 = {
|
|
1679
|
+
fg: pal().muted
|
|
1680
|
+
}, _v$8 = {
|
|
1681
|
+
fg: pal().success
|
|
1682
|
+
};
|
|
1683
|
+
_v$7 !== _p$.e && (_p$.e = _$setProp(_el$36, "style", _v$7, _p$.e));
|
|
1684
|
+
_v$8 !== _p$.t && (_p$.t = _$setProp(_el$38, "style", _v$8, _p$.t));
|
|
1685
|
+
return _p$;
|
|
1686
|
+
}, {
|
|
1687
|
+
e: void 0,
|
|
1688
|
+
t: void 0
|
|
1689
|
+
});
|
|
1690
|
+
return _el$35;
|
|
1691
|
+
}
|
|
1692
|
+
})];
|
|
1693
|
+
}
|
|
1694
|
+
})];
|
|
1695
|
+
}
|
|
1696
|
+
}), _$createComponent(Show, {
|
|
1697
|
+
get when() {
|
|
1698
|
+
return sectionModel();
|
|
1699
|
+
},
|
|
1700
|
+
get children() {
|
|
1701
|
+
return [_$memo(() => (() => {
|
|
1702
|
+
var _el$78 = _$createElement("text"), _el$79 = _$createElement("span"), _el$80 = _$createElement("span"), _el$81 = _$createElement("b"), _el$82 = _$createElement("span");
|
|
1703
|
+
_$insertNode(_el$78, _el$79);
|
|
1704
|
+
_$insertNode(_el$78, _el$80);
|
|
1705
|
+
_$insertNode(_el$78, _el$82);
|
|
1706
|
+
_$setProp(_el$78, "onMouseUp", () => setModelOpen((o) => {
|
|
1707
|
+
const n = !o;
|
|
1708
|
+
persistFold("model", n);
|
|
1709
|
+
return n;
|
|
1710
|
+
}));
|
|
1711
|
+
_$insert(_el$79, () => modelOpen() ? "\u25BC " : "\u25B6 ");
|
|
1712
|
+
_$insertNode(_el$80, _el$81);
|
|
1713
|
+
_$insert(_el$81, () => t("secModel"));
|
|
1714
|
+
_$insert(_el$82, () => sep().slice(visualWidth((modelOpen() ? "\u25BC " : "\u25B6 ") + t("secModel"))));
|
|
1715
|
+
_$effect((_p$) => {
|
|
1716
|
+
var _v$21 = {
|
|
1717
|
+
fg: pal().muted
|
|
1718
|
+
}, _v$22 = {
|
|
1719
|
+
fg: pal().primary
|
|
1720
|
+
}, _v$23 = {
|
|
1721
|
+
fg: pal().muted
|
|
1722
|
+
};
|
|
1723
|
+
_v$21 !== _p$.e && (_p$.e = _$setProp(_el$79, "style", _v$21, _p$.e));
|
|
1724
|
+
_v$22 !== _p$.t && (_p$.t = _$setProp(_el$80, "style", _v$22, _p$.t));
|
|
1725
|
+
_v$23 !== _p$.a && (_p$.a = _$setProp(_el$82, "style", _v$23, _p$.a));
|
|
1726
|
+
return _p$;
|
|
1727
|
+
}, {
|
|
1728
|
+
e: void 0,
|
|
1729
|
+
t: void 0,
|
|
1730
|
+
a: void 0
|
|
1731
|
+
});
|
|
1732
|
+
return _el$78;
|
|
1733
|
+
})()), _$createComponent(Show, {
|
|
1734
|
+
get when() {
|
|
1735
|
+
return modelOpen();
|
|
1736
|
+
},
|
|
1737
|
+
get children() {
|
|
1738
|
+
return [(() => {
|
|
1739
|
+
var _el$40 = _$createElement("text");
|
|
1740
|
+
_$insert(_el$40, () => justify(t("cost"), fmtCost(data().cost, currencySymbol(), exchangeRate())));
|
|
1741
|
+
_$effect((_$p) => _$setProp(_el$40, "fg", pal().text, _$p));
|
|
1742
|
+
return _el$40;
|
|
1743
|
+
})(), _$createComponent(Show, {
|
|
1744
|
+
get when() {
|
|
1745
|
+
return data().providerName;
|
|
1746
|
+
},
|
|
1747
|
+
get children() {
|
|
1748
|
+
var _el$41 = _$createElement("text");
|
|
1749
|
+
_$insert(_el$41, () => justify(t("provider"), data().providerName));
|
|
1750
|
+
_$effect((_$p) => _$setProp(_el$41, "fg", pal().muted, _$p));
|
|
1751
|
+
return _el$41;
|
|
1752
|
+
}
|
|
1753
|
+
}), (() => {
|
|
1754
|
+
var _el$42 = _$createElement("text");
|
|
1755
|
+
_$insert(_el$42, () => justify(t("model"), data().model));
|
|
1756
|
+
_$effect((_$p) => _$setProp(_el$42, "fg", pal().muted, _$p));
|
|
1757
|
+
return _el$42;
|
|
1758
|
+
})(), _$createComponent(Show, {
|
|
1759
|
+
get when() {
|
|
1760
|
+
return data().hasPricing;
|
|
1761
|
+
},
|
|
1762
|
+
get children() {
|
|
1763
|
+
return [(() => {
|
|
1764
|
+
var _el$43 = _$createElement("text");
|
|
1765
|
+
_$insert(_el$43, () => justify(t("rate"), currencySymbol() + (data().inputRate * exchangeRate()).toFixed(2) + "/M " + t("inputRate")));
|
|
1766
|
+
_$effect((_$p) => _$setProp(_el$43, "fg", pal().muted, _$p));
|
|
1767
|
+
return _el$43;
|
|
1768
|
+
})(), _$createComponent(Show, {
|
|
1769
|
+
get when() {
|
|
1770
|
+
return data().cacheReadRate > 0;
|
|
1771
|
+
},
|
|
1772
|
+
get children() {
|
|
1773
|
+
var _el$44 = _$createElement("text");
|
|
1774
|
+
_$insert(_el$44, () => justify("", currencySymbol() + (data().cacheReadRate * exchangeRate()).toFixed(2) + "/M " + t("cacheRate")));
|
|
1775
|
+
_$effect((_$p) => _$setProp(_el$44, "fg", pal().muted, _$p));
|
|
1776
|
+
return _el$44;
|
|
1777
|
+
}
|
|
1778
|
+
}), _$createComponent(Show, {
|
|
1779
|
+
get when() {
|
|
1780
|
+
return data().cacheWriteRate > 0;
|
|
1781
|
+
},
|
|
1782
|
+
get children() {
|
|
1783
|
+
var _el$45 = _$createElement("text");
|
|
1784
|
+
_$insert(_el$45, () => justify("", currencySymbol() + (data().cacheWriteRate * exchangeRate()).toFixed(2) + "/M " + t("writeRate")));
|
|
1785
|
+
_$effect((_$p) => _$setProp(_el$45, "fg", pal().muted, _$p));
|
|
1786
|
+
return _el$45;
|
|
1787
|
+
}
|
|
1788
|
+
})];
|
|
1789
|
+
}
|
|
1790
|
+
})];
|
|
1791
|
+
}
|
|
1792
|
+
})];
|
|
1793
|
+
}
|
|
1794
|
+
}), _$createComponent(Show, {
|
|
1795
|
+
get when() {
|
|
1796
|
+
return sectionDist();
|
|
1797
|
+
},
|
|
1798
|
+
get children() {
|
|
1799
|
+
return _$createComponent(Show, {
|
|
1800
|
+
get when() {
|
|
1801
|
+
return data().hasDistData;
|
|
1802
|
+
},
|
|
1803
|
+
get children() {
|
|
1804
|
+
return [_$memo(() => (() => {
|
|
1805
|
+
var _el$83 = _$createElement("text"), _el$84 = _$createElement("span"), _el$85 = _$createElement("span"), _el$86 = _$createElement("b"), _el$87 = _$createElement("span");
|
|
1806
|
+
_$insertNode(_el$83, _el$84);
|
|
1807
|
+
_$insertNode(_el$83, _el$85);
|
|
1808
|
+
_$insertNode(_el$83, _el$87);
|
|
1809
|
+
_$setProp(_el$83, "onMouseUp", () => setDistOpen((o) => {
|
|
1810
|
+
const n = !o;
|
|
1811
|
+
persistFold("dist", n);
|
|
1812
|
+
return n;
|
|
1813
|
+
}));
|
|
1814
|
+
_$insert(_el$84, () => distOpen() ? "\u25BC " : "\u25B6 ");
|
|
1815
|
+
_$insertNode(_el$85, _el$86);
|
|
1816
|
+
_$insert(_el$86, () => t("distTitle"));
|
|
1817
|
+
_$insert(_el$87, () => sep().slice(visualWidth((distOpen() ? "\u25BC " : "\u25B6 ") + t("distTitle"))));
|
|
1818
|
+
_$effect((_p$) => {
|
|
1819
|
+
var _v$24 = {
|
|
1820
|
+
fg: pal().muted
|
|
1821
|
+
}, _v$25 = {
|
|
1822
|
+
fg: pal().primary
|
|
1823
|
+
}, _v$26 = {
|
|
1824
|
+
fg: pal().muted
|
|
1825
|
+
};
|
|
1826
|
+
_v$24 !== _p$.e && (_p$.e = _$setProp(_el$84, "style", _v$24, _p$.e));
|
|
1827
|
+
_v$25 !== _p$.t && (_p$.t = _$setProp(_el$85, "style", _v$25, _p$.t));
|
|
1828
|
+
_v$26 !== _p$.a && (_p$.a = _$setProp(_el$87, "style", _v$26, _p$.a));
|
|
1829
|
+
return _p$;
|
|
1830
|
+
}, {
|
|
1831
|
+
e: void 0,
|
|
1832
|
+
t: void 0,
|
|
1833
|
+
a: void 0
|
|
1834
|
+
});
|
|
1835
|
+
return _el$83;
|
|
1836
|
+
})()), _$createComponent(Show, {
|
|
1837
|
+
get when() {
|
|
1838
|
+
return distOpen();
|
|
1839
|
+
},
|
|
1840
|
+
get children() {
|
|
1841
|
+
return [_$createComponent(Show, {
|
|
1842
|
+
get when() {
|
|
1843
|
+
return data().dist.system > 0;
|
|
1844
|
+
},
|
|
1845
|
+
get children() {
|
|
1846
|
+
var _el$46 = _$createElement("text");
|
|
1847
|
+
_$insert(_el$46, () => justify(t("distSys"), fmt(data().dist.system), t("tok")));
|
|
1848
|
+
_$effect((_$p) => _$setProp(_el$46, "fg", pal().muted, _$p));
|
|
1849
|
+
return _el$46;
|
|
1850
|
+
}
|
|
1851
|
+
}), _$createComponent(Show, {
|
|
1852
|
+
get when() {
|
|
1853
|
+
return data().dist.user > 0;
|
|
1854
|
+
},
|
|
1855
|
+
get children() {
|
|
1856
|
+
var _el$47 = _$createElement("text");
|
|
1857
|
+
_$insert(_el$47, () => justify(t("distUser"), fmt(data().dist.user), t("tok")));
|
|
1858
|
+
_$effect((_$p) => _$setProp(_el$47, "fg", pal().muted, _$p));
|
|
1859
|
+
return _el$47;
|
|
1860
|
+
}
|
|
1861
|
+
}), _$createComponent(Show, {
|
|
1862
|
+
get when() {
|
|
1863
|
+
return data().dist.agent > 0;
|
|
1864
|
+
},
|
|
1865
|
+
get children() {
|
|
1866
|
+
var _el$48 = _$createElement("text");
|
|
1867
|
+
_$insert(_el$48, () => justify(t("distAgent"), fmt(data().dist.agent), t("tok")));
|
|
1868
|
+
_$effect((_$p) => _$setProp(_el$48, "fg", pal().muted, _$p));
|
|
1869
|
+
return _el$48;
|
|
1870
|
+
}
|
|
1871
|
+
}), _$createComponent(Show, {
|
|
1872
|
+
get when() {
|
|
1873
|
+
return data().dist.toolCall > 0;
|
|
1874
|
+
},
|
|
1875
|
+
get children() {
|
|
1876
|
+
var _el$49 = _$createElement("text");
|
|
1877
|
+
_$insert(_el$49, () => justify(t("distTool"), fmt(data().dist.toolCall), t("tok")));
|
|
1878
|
+
_$effect((_$p) => _$setProp(_el$49, "fg", pal().muted, _$p));
|
|
1879
|
+
return _el$49;
|
|
1880
|
+
}
|
|
1881
|
+
}), _$createComponent(Show, {
|
|
1882
|
+
get when() {
|
|
1883
|
+
return data().dist.toolResult > 0;
|
|
1884
|
+
},
|
|
1885
|
+
get children() {
|
|
1886
|
+
var _el$50 = _$createElement("text");
|
|
1887
|
+
_$insert(_el$50, () => justify(t("distRes"), fmt(data().dist.toolResult), t("tok")));
|
|
1888
|
+
_$effect((_$p) => _$setProp(_el$50, "fg", pal().muted, _$p));
|
|
1889
|
+
return _el$50;
|
|
1890
|
+
}
|
|
1891
|
+
}), _$createComponent(Show, {
|
|
1892
|
+
get when() {
|
|
1893
|
+
return data().dist.reasoning > 0;
|
|
1894
|
+
},
|
|
1895
|
+
get children() {
|
|
1896
|
+
var _el$51 = _$createElement("text");
|
|
1897
|
+
_$insert(_el$51, () => justify(t("distReason"), fmt(data().dist.reasoning), t("tok")));
|
|
1898
|
+
_$effect((_$p) => _$setProp(_el$51, "fg", pal().muted, _$p));
|
|
1899
|
+
return _el$51;
|
|
1900
|
+
}
|
|
1901
|
+
})];
|
|
1902
|
+
}
|
|
1903
|
+
})];
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
}), _$createComponent(Show, {
|
|
1908
|
+
get when() {
|
|
1909
|
+
return sectionSkills();
|
|
1910
|
+
},
|
|
1911
|
+
get children() {
|
|
1912
|
+
return _$createComponent(Show, {
|
|
1913
|
+
get when() {
|
|
1914
|
+
return data().hasSkills;
|
|
1915
|
+
},
|
|
1916
|
+
get children() {
|
|
1917
|
+
return [_$memo(() => (() => {
|
|
1918
|
+
var _el$88 = _$createElement("text"), _el$89 = _$createElement("span"), _el$90 = _$createElement("span"), _el$91 = _$createElement("b"), _el$92 = _$createElement("span"), _el$93 = _$createTextNode(` (`), _el$94 = _$createTextNode(`)`), _el$95 = _$createElement("span");
|
|
1919
|
+
_$insertNode(_el$88, _el$89);
|
|
1920
|
+
_$insertNode(_el$88, _el$90);
|
|
1921
|
+
_$insertNode(_el$88, _el$92);
|
|
1922
|
+
_$insertNode(_el$88, _el$95);
|
|
1923
|
+
_$setProp(_el$88, "onMouseUp", () => setSkillsOpen((o) => {
|
|
1924
|
+
const n = !o;
|
|
1925
|
+
persistFold("skills", n);
|
|
1926
|
+
return n;
|
|
1927
|
+
}));
|
|
1928
|
+
_$insert(_el$89, () => skillsOpen() ? "\u25BC " : "\u25B6 ");
|
|
1929
|
+
_$insertNode(_el$90, _el$91);
|
|
1930
|
+
_$insert(_el$91, () => t("secSkills"));
|
|
1931
|
+
_$insertNode(_el$92, _el$93);
|
|
1932
|
+
_$insertNode(_el$92, _el$94);
|
|
1933
|
+
_$insert(_el$92, () => data().skills.length, _el$94);
|
|
1934
|
+
_$insert(_el$95, () => sep().slice(visualWidth((skillsOpen() ? "\u25BC " : "\u25B6 ") + t("secSkills") + ` (${data().skills.length})`)));
|
|
1935
|
+
_$effect((_p$) => {
|
|
1936
|
+
var _v$27 = {
|
|
1937
|
+
fg: pal().muted
|
|
1938
|
+
}, _v$28 = {
|
|
1939
|
+
fg: pal().primary
|
|
1940
|
+
}, _v$29 = {
|
|
1941
|
+
fg: pal().muted
|
|
1942
|
+
}, _v$30 = {
|
|
1943
|
+
fg: pal().muted
|
|
1944
|
+
};
|
|
1945
|
+
_v$27 !== _p$.e && (_p$.e = _$setProp(_el$89, "style", _v$27, _p$.e));
|
|
1946
|
+
_v$28 !== _p$.t && (_p$.t = _$setProp(_el$90, "style", _v$28, _p$.t));
|
|
1947
|
+
_v$29 !== _p$.a && (_p$.a = _$setProp(_el$92, "style", _v$29, _p$.a));
|
|
1948
|
+
_v$30 !== _p$.o && (_p$.o = _$setProp(_el$95, "style", _v$30, _p$.o));
|
|
1949
|
+
return _p$;
|
|
1950
|
+
}, {
|
|
1951
|
+
e: void 0,
|
|
1952
|
+
t: void 0,
|
|
1953
|
+
a: void 0,
|
|
1954
|
+
o: void 0
|
|
1955
|
+
});
|
|
1956
|
+
return _el$88;
|
|
1957
|
+
})()), _$createComponent(Show, {
|
|
1958
|
+
get when() {
|
|
1959
|
+
return skillsOpen();
|
|
1960
|
+
},
|
|
1961
|
+
get children() {
|
|
1962
|
+
return data().skills.map((sk) => {
|
|
1963
|
+
const rightW = visualWidth(fmt(sk.tokens)) + UNIT_GAP + visualWidth(t("tok"));
|
|
1964
|
+
const maxLabel = Math.max(4, panelWidth() - gutter() - rightW - 1);
|
|
1965
|
+
const label = truncateVisual(sk.name, maxLabel);
|
|
1966
|
+
return (() => {
|
|
1967
|
+
var _el$96 = _$createElement("text");
|
|
1968
|
+
_$insert(_el$96, () => justify(label, fmt(sk.tokens), t("tok")));
|
|
1969
|
+
_$effect((_$p) => _$setProp(_el$96, "fg", pal().muted, _$p));
|
|
1970
|
+
return _el$96;
|
|
1971
|
+
})();
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
})];
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
}), _$createComponent(Show, {
|
|
1979
|
+
get when() {
|
|
1980
|
+
return sectionBalance();
|
|
1981
|
+
},
|
|
1982
|
+
get children() {
|
|
1983
|
+
return [(() => {
|
|
1984
|
+
var _el$52 = _$createElement("text");
|
|
1985
|
+
_$insert(_el$52, sep);
|
|
1986
|
+
_$effect((_$p) => _$setProp(_el$52, "fg", pal().muted, _$p));
|
|
1987
|
+
return _el$52;
|
|
1988
|
+
})(), _$createComponent(Show, {
|
|
1989
|
+
get when() {
|
|
1990
|
+
return balanceUnsupported();
|
|
1991
|
+
},
|
|
1992
|
+
get children() {
|
|
1993
|
+
var _el$53 = _$createElement("text"), _el$54 = _$createElement("span"), _el$56 = _$createElement("span");
|
|
1994
|
+
_$insertNode(_el$53, _el$54);
|
|
1995
|
+
_$insertNode(_el$53, _el$56);
|
|
1996
|
+
_$insertNode(_el$54, _$createTextNode(`> `));
|
|
1997
|
+
_$insert(_el$56, () => t("balUnsupported"));
|
|
1998
|
+
_$effect((_p$) => {
|
|
1999
|
+
var _v$9 = pal().muted, _v$0 = {
|
|
2000
|
+
fg: pal().muted
|
|
2001
|
+
};
|
|
2002
|
+
_v$9 !== _p$.e && (_p$.e = _$setProp(_el$53, "fg", _v$9, _p$.e));
|
|
2003
|
+
_v$0 !== _p$.t && (_p$.t = _$setProp(_el$54, "style", _v$0, _p$.t));
|
|
2004
|
+
return _p$;
|
|
2005
|
+
}, {
|
|
2006
|
+
e: void 0,
|
|
2007
|
+
t: void 0
|
|
2008
|
+
});
|
|
2009
|
+
return _el$53;
|
|
2010
|
+
}
|
|
2011
|
+
}), _$createComponent(Show, {
|
|
2012
|
+
get when() {
|
|
2013
|
+
return !balanceUnsupported();
|
|
2014
|
+
},
|
|
2015
|
+
get children() {
|
|
2016
|
+
return [_$createComponent(Show, {
|
|
2017
|
+
get when() {
|
|
2018
|
+
return balanceState().status === "idle";
|
|
2019
|
+
},
|
|
2020
|
+
get children() {
|
|
2021
|
+
var _el$57 = _$createElement("text"), _el$58 = _$createElement("span"), _el$60 = _$createElement("span");
|
|
2022
|
+
_$insertNode(_el$57, _el$58);
|
|
2023
|
+
_$insertNode(_el$57, _el$60);
|
|
2024
|
+
_$insertNode(_el$58, _$createTextNode(`> `));
|
|
2025
|
+
_$insert(_el$60, () => t("balNoKey", {
|
|
2026
|
+
p: providerName()
|
|
2027
|
+
}));
|
|
2028
|
+
_$effect((_p$) => {
|
|
2029
|
+
var _v$1 = pal().muted, _v$10 = {
|
|
2030
|
+
fg: pal().muted
|
|
2031
|
+
};
|
|
2032
|
+
_v$1 !== _p$.e && (_p$.e = _$setProp(_el$57, "fg", _v$1, _p$.e));
|
|
2033
|
+
_v$10 !== _p$.t && (_p$.t = _$setProp(_el$58, "style", _v$10, _p$.t));
|
|
2034
|
+
return _p$;
|
|
2035
|
+
}, {
|
|
2036
|
+
e: void 0,
|
|
2037
|
+
t: void 0
|
|
2038
|
+
});
|
|
2039
|
+
return _el$57;
|
|
2040
|
+
}
|
|
2041
|
+
}), _$createComponent(Show, {
|
|
2042
|
+
get when() {
|
|
2043
|
+
return balanceState().status === "loading";
|
|
2044
|
+
},
|
|
2045
|
+
get children() {
|
|
2046
|
+
var _el$61 = _$createElement("text"), _el$62 = _$createElement("span"), _el$64 = _$createElement("span");
|
|
2047
|
+
_$insertNode(_el$61, _el$62);
|
|
2048
|
+
_$insertNode(_el$61, _el$64);
|
|
2049
|
+
_$insertNode(_el$62, _$createTextNode(`> `));
|
|
2050
|
+
_$insert(_el$64, () => t("balLoading"));
|
|
2051
|
+
_$effect((_p$) => {
|
|
2052
|
+
var _v$11 = pal().muted, _v$12 = {
|
|
2053
|
+
fg: pal().muted
|
|
2054
|
+
};
|
|
2055
|
+
_v$11 !== _p$.e && (_p$.e = _$setProp(_el$61, "fg", _v$11, _p$.e));
|
|
2056
|
+
_v$12 !== _p$.t && (_p$.t = _$setProp(_el$62, "style", _v$12, _p$.t));
|
|
2057
|
+
return _p$;
|
|
2058
|
+
}, {
|
|
2059
|
+
e: void 0,
|
|
2060
|
+
t: void 0
|
|
2061
|
+
});
|
|
2062
|
+
return _el$61;
|
|
2063
|
+
}
|
|
2064
|
+
}), _$createComponent(Show, {
|
|
2065
|
+
get when() {
|
|
2066
|
+
return balanceState().status === "error";
|
|
2067
|
+
},
|
|
2068
|
+
get children() {
|
|
2069
|
+
var _el$65 = _$createElement("text"), _el$66 = _$createElement("span"), _el$68 = _$createElement("span");
|
|
2070
|
+
_$insertNode(_el$65, _el$66);
|
|
2071
|
+
_$insertNode(_el$65, _el$68);
|
|
2072
|
+
_$insertNode(_el$66, _$createTextNode(`> `));
|
|
2073
|
+
_$insert(_el$68, () => {
|
|
2074
|
+
const code = balanceState().error;
|
|
2075
|
+
if (code === "401") return t("balErr401");
|
|
2076
|
+
if (code === "403") return t("balErr403");
|
|
2077
|
+
if (code === "EMPTY") return t("balErrEmpty");
|
|
2078
|
+
if (code === "TIMEOUT") return t("balErrTimeout");
|
|
2079
|
+
return t("balError") + (code ? ` (${code})` : "");
|
|
2080
|
+
});
|
|
2081
|
+
_$effect((_p$) => {
|
|
2082
|
+
var _v$13 = pal().error, _v$14 = {
|
|
2083
|
+
fg: pal().muted
|
|
2084
|
+
};
|
|
2085
|
+
_v$13 !== _p$.e && (_p$.e = _$setProp(_el$65, "fg", _v$13, _p$.e));
|
|
2086
|
+
_v$14 !== _p$.t && (_p$.t = _$setProp(_el$66, "style", _v$14, _p$.t));
|
|
2087
|
+
return _p$;
|
|
2088
|
+
}, {
|
|
2089
|
+
e: void 0,
|
|
2090
|
+
t: void 0
|
|
2091
|
+
});
|
|
2092
|
+
return _el$65;
|
|
2093
|
+
}
|
|
2094
|
+
}), _$createComponent(Show, {
|
|
2095
|
+
get when() {
|
|
2096
|
+
return _$memo(() => balanceState().status === "ok")() && balanceState().data;
|
|
2097
|
+
},
|
|
2098
|
+
get children() {
|
|
2099
|
+
var _el$69 = _$createElement("text");
|
|
2100
|
+
_$insert(_el$69, () => justify(t("balTotal"), formatBalanceText(balanceState().data, balanceCurrency(), exchangeRate())));
|
|
2101
|
+
_$effect((_$p) => _$setProp(_el$69, "fg", pal().text, _$p));
|
|
2102
|
+
return _el$69;
|
|
2103
|
+
}
|
|
2104
|
+
})];
|
|
2105
|
+
}
|
|
2106
|
+
})];
|
|
2107
|
+
}
|
|
2108
|
+
})];
|
|
2109
|
+
}
|
|
2110
|
+
})];
|
|
2111
|
+
}
|
|
2112
|
+
}), null);
|
|
2113
|
+
_$effect((_p$) => {
|
|
2114
|
+
var _v$15 = {
|
|
2115
|
+
fg: pal().muted
|
|
2116
|
+
}, _v$16 = {
|
|
2117
|
+
fg: pal().primary
|
|
2118
|
+
};
|
|
2119
|
+
_v$15 !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$15, _p$.e));
|
|
2120
|
+
_v$16 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$16, _p$.t));
|
|
2121
|
+
return _p$;
|
|
2122
|
+
}, {
|
|
2123
|
+
e: void 0,
|
|
2124
|
+
t: void 0
|
|
2125
|
+
});
|
|
2126
|
+
return _el$;
|
|
2127
|
+
})();
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// src/v2/status.tsx
|
|
2131
|
+
import { setProp as _$setProp2 } from "@opentui/solid";
|
|
2132
|
+
import { effect as _$effect2 } from "@opentui/solid";
|
|
2133
|
+
import { insert as _$insert2 } from "@opentui/solid";
|
|
2134
|
+
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
2135
|
+
import { createElement as _$createElement2 } from "@opentui/solid";
|
|
2136
|
+
import { createMemo as createMemo2, For, Show as Show2 } from "solid-js";
|
|
2137
|
+
|
|
2138
|
+
// src/v2/data.ts
|
|
2139
|
+
function collectLastHitRate(context, sessionID) {
|
|
2140
|
+
const msgs = context.data.session.message.list(sessionID) ?? [];
|
|
2141
|
+
const session = context.data.session.get(sessionID);
|
|
2142
|
+
let input = num(session?.tokens?.input);
|
|
2143
|
+
let read = num(session?.tokens?.cache?.read);
|
|
2144
|
+
let write = num(session?.tokens?.cache?.write);
|
|
2145
|
+
if (session?.tokens == null) {
|
|
2146
|
+
for (const m of msgs) {
|
|
2147
|
+
if (m.type !== "assistant") continue;
|
|
2148
|
+
const t = m.tokens;
|
|
2149
|
+
if (!t) continue;
|
|
2150
|
+
input += num(t.input);
|
|
2151
|
+
read += num(t.cache?.read);
|
|
2152
|
+
write += num(t.cache?.write);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
let hitRate = -1, prevHitRate = -1;
|
|
2156
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
2157
|
+
const m = msgs[i];
|
|
2158
|
+
if (m.type !== "assistant") continue;
|
|
2159
|
+
const tk = m.tokens;
|
|
2160
|
+
if (!tk) continue;
|
|
2161
|
+
const mit = num(tk.input) + num(tk.cache?.read) + num(tk.cache?.write);
|
|
2162
|
+
const mrt = num(tk.cache?.read);
|
|
2163
|
+
if (mit <= 0) continue;
|
|
2164
|
+
const rate = mrt / mit * 100;
|
|
2165
|
+
if (hitRate < 0) {
|
|
2166
|
+
hitRate = rate;
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
prevHitRate = rate;
|
|
2170
|
+
break;
|
|
2171
|
+
}
|
|
2172
|
+
return { hitRate, prevHitRate, input, read, write };
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// src/v2/theme.ts
|
|
2176
|
+
function mapTheme(theme) {
|
|
2177
|
+
return {
|
|
2178
|
+
primary: theme.hue.interactive[300],
|
|
2179
|
+
text: theme.text.default,
|
|
2180
|
+
textMuted: theme.text.subdued,
|
|
2181
|
+
success: theme.text.feedback.success.default,
|
|
2182
|
+
warning: theme.text.feedback.warning.default,
|
|
2183
|
+
error: theme.text.feedback.error.default,
|
|
2184
|
+
border: theme.text.subdued
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// src/v2/status.tsx
|
|
2189
|
+
function StatusView(props) {
|
|
2190
|
+
const t = createT(() => props.signals.langCode());
|
|
2191
|
+
const pal = createMemo2(() => {
|
|
2192
|
+
const th = mapTheme(props.context.theme);
|
|
2193
|
+
const sat = (k, fb) => desaturateTo(th[k], MAX_SAT, fb);
|
|
2194
|
+
return {
|
|
2195
|
+
text: sat("text", FALLBACK.text),
|
|
2196
|
+
muted: sat("textMuted", FALLBACK.muted),
|
|
2197
|
+
success: sat("success", FALLBACK.success),
|
|
2198
|
+
warning: sat("warning", FALLBACK.warning),
|
|
2199
|
+
error: sat("error", FALLBACK.error)
|
|
2200
|
+
};
|
|
2201
|
+
});
|
|
2202
|
+
const stats = createMemo2(() => collectLastHitRate(props.context, props.sessionID));
|
|
2203
|
+
const hitColor = createMemo2(() => {
|
|
2204
|
+
const r = stats().hitRate;
|
|
2205
|
+
if (r >= 85) return pal().success;
|
|
2206
|
+
if (r >= 70) return pal().warning;
|
|
2207
|
+
return pal().error;
|
|
2208
|
+
});
|
|
2209
|
+
const trend = createMemo2(() => {
|
|
2210
|
+
const s = stats();
|
|
2211
|
+
if (s.prevHitRate < 0 || s.hitRate < 0) return null;
|
|
2212
|
+
const d = s.hitRate - s.prevHitRate;
|
|
2213
|
+
return Math.abs(d) < 0.05 ? null : d;
|
|
2214
|
+
});
|
|
2215
|
+
const balanceText = createMemo2(() => {
|
|
2216
|
+
const s = props.signals.balanceState();
|
|
2217
|
+
if (s.status === "ok" && s.data) return formatBalanceText(s.data, props.signals.balanceCurrency(), props.signals.exchangeRate());
|
|
2218
|
+
if (s.status === "loading") return "\u2026";
|
|
2219
|
+
if (s.status === "error") return "\u26A0";
|
|
2220
|
+
return "-";
|
|
2221
|
+
});
|
|
2222
|
+
const segs = createMemo2(() => {
|
|
2223
|
+
const s = stats();
|
|
2224
|
+
const hr = s.hitRate >= 0 ? (Math.floor(s.hitRate * 10) / 10).toFixed(1) + "%" : "--";
|
|
2225
|
+
const out = [{
|
|
2226
|
+
text: t("barHit") + " ",
|
|
2227
|
+
color: pal().muted
|
|
2228
|
+
}, {
|
|
2229
|
+
text: hr,
|
|
2230
|
+
color: hitColor()
|
|
2231
|
+
}];
|
|
2232
|
+
const tr = trend();
|
|
2233
|
+
if (tr !== null) {
|
|
2234
|
+
out.push({
|
|
2235
|
+
text: " " + (tr > 0 ? "\u2191" : "\u2193") + Math.abs(tr).toFixed(1) + "%",
|
|
2236
|
+
color: tr > 0 ? pal().success : pal().error
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
out.push({
|
|
2240
|
+
text: " \xB7 " + t("barTok") + " ",
|
|
2241
|
+
color: pal().muted
|
|
2242
|
+
});
|
|
2243
|
+
out.push({
|
|
2244
|
+
text: s ? fmtCompact(s.input + s.read + s.write) : "--",
|
|
2245
|
+
color: pal().text
|
|
2246
|
+
});
|
|
2247
|
+
if (!props.signals.balanceUnsupported()) {
|
|
2248
|
+
out.push({
|
|
2249
|
+
text: " \xB7 " + t("barBal") + " ",
|
|
2250
|
+
color: pal().muted
|
|
2251
|
+
});
|
|
2252
|
+
out.push({
|
|
2253
|
+
text: balanceText(),
|
|
2254
|
+
color: pal().text
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
out.push({
|
|
2258
|
+
text: " \xB7 ",
|
|
2259
|
+
color: pal().muted
|
|
2260
|
+
});
|
|
2261
|
+
return out;
|
|
2262
|
+
});
|
|
2263
|
+
const segsW = createMemo2(() => {
|
|
2264
|
+
let w = 0;
|
|
2265
|
+
for (const sg of segs()) w += visualWidth(sg.text);
|
|
2266
|
+
return w;
|
|
2267
|
+
});
|
|
2268
|
+
return _$createComponent2(Show2, {
|
|
2269
|
+
get when() {
|
|
2270
|
+
return segsW() > 0;
|
|
2271
|
+
},
|
|
2272
|
+
get children() {
|
|
2273
|
+
var _el$ = _$createElement2("text");
|
|
2274
|
+
_$insert2(_el$, _$createComponent2(For, {
|
|
2275
|
+
get each() {
|
|
2276
|
+
return segs();
|
|
2277
|
+
},
|
|
2278
|
+
children: (sg) => (() => {
|
|
2279
|
+
var _el$2 = _$createElement2("span");
|
|
2280
|
+
_$insert2(_el$2, () => sg.text);
|
|
2281
|
+
_$effect2((_$p) => _$setProp2(_el$2, "style", {
|
|
2282
|
+
fg: sg.color
|
|
2283
|
+
}, _$p));
|
|
2284
|
+
return _el$2;
|
|
2285
|
+
})()
|
|
2286
|
+
}));
|
|
2287
|
+
return _el$;
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// src/v2/commands.ts
|
|
2293
|
+
var KV_PREFIX = "cache_panel";
|
|
2294
|
+
function extractToolParts(msg) {
|
|
2295
|
+
if (!Array.isArray(msg?.content)) return [];
|
|
2296
|
+
return msg.content.filter((p) => p?.type === "tool");
|
|
2297
|
+
}
|
|
2298
|
+
function findOpencodeKeyV2(context, provider) {
|
|
2299
|
+
try {
|
|
2300
|
+
const provs = context.data.location.provider.list();
|
|
2301
|
+
const id = provider.id.toLowerCase();
|
|
2302
|
+
const hit = provs.find((p) => String(p.id ?? "").toLowerCase() === id) ?? provs.find((p) => String(p.id ?? "").toLowerCase().startsWith(id));
|
|
2303
|
+
if (!hit) return "";
|
|
2304
|
+
const k = typeof hit.key === "string" ? hit.key : "";
|
|
2305
|
+
if (k) return k;
|
|
2306
|
+
return typeof hit.options?.apiKey === "string" ? hit.options.apiKey : "";
|
|
2307
|
+
} catch {
|
|
2308
|
+
return "";
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
function currentSessionID(context) {
|
|
2312
|
+
try {
|
|
2313
|
+
const rt = context.ui.router.current();
|
|
2314
|
+
if (rt?.type === "session" && rt.sessionID) return String(rt.sessionID);
|
|
2315
|
+
} catch {
|
|
2316
|
+
}
|
|
2317
|
+
return "";
|
|
2318
|
+
}
|
|
2319
|
+
function makeCommands(context, api, signals) {
|
|
2320
|
+
const t = () => createT(() => signals.langCode());
|
|
2321
|
+
const providerOptionTitle = (p, current) => {
|
|
2322
|
+
const hasManual = !!api.kv.get(`${KV_PREFIX}.balance.${p.id}.key`, "");
|
|
2323
|
+
const hasAuto = !hasManual && !!findOpencodeKeyV2(context, p);
|
|
2324
|
+
const mark = hasManual ? t()("keyUser") : hasAuto ? t()("keyOpenCode") : t()("keyNotSet");
|
|
2325
|
+
return p.name + mark + (current && p.id === current ? " *" : "");
|
|
2326
|
+
};
|
|
2327
|
+
const promptBalanceKey = async (provider) => {
|
|
2328
|
+
const current = api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "") ?? "";
|
|
2329
|
+
const val = await context.ui.dialog.prompt({
|
|
2330
|
+
title: provider.name,
|
|
2331
|
+
message: t()("balKeyPrompt", { p: provider.name }),
|
|
2332
|
+
placeholder: provider.keyPlaceholder ?? "sk-..."
|
|
2333
|
+
});
|
|
2334
|
+
if (val === void 0) return;
|
|
2335
|
+
const input = val.trim();
|
|
2336
|
+
let key;
|
|
2337
|
+
if (input === "") {
|
|
2338
|
+
key = "";
|
|
2339
|
+
} else if (input.includes("*")) {
|
|
2340
|
+
key = current;
|
|
2341
|
+
} else {
|
|
2342
|
+
key = input;
|
|
2343
|
+
}
|
|
2344
|
+
await api.kv.set(`${KV_PREFIX}.balance.${provider.id}.key`, key);
|
|
2345
|
+
signals.setBalanceRefresh(signals.balanceRefresh() + 1);
|
|
2346
|
+
context.ui.toast.show({ message: key ? t()("keySaved") : t()("keyCleared") });
|
|
2347
|
+
};
|
|
2348
|
+
return [
|
|
2349
|
+
// ── /cache-currency 切换货币 ──
|
|
2350
|
+
{
|
|
2351
|
+
id: "opencode-visual-cache.cache.currency",
|
|
2352
|
+
title: "Cache: Set Currency",
|
|
2353
|
+
description: "Change the currency unit for cost display",
|
|
2354
|
+
group: "Cache",
|
|
2355
|
+
palette: true,
|
|
2356
|
+
slash: { name: "cache-currency" },
|
|
2357
|
+
run: async () => {
|
|
2358
|
+
const opt = await context.ui.dialog.select({
|
|
2359
|
+
title: "Select Currency",
|
|
2360
|
+
options: Object.entries(CURRENCIES).map(([code, sym2]) => ({
|
|
2361
|
+
title: `${code} (${sym2})`,
|
|
2362
|
+
value: code
|
|
2363
|
+
}))
|
|
2364
|
+
});
|
|
2365
|
+
if (!opt) return;
|
|
2366
|
+
const sym = CURRENCIES[opt] ?? "$";
|
|
2367
|
+
const defRate = DEFAULT_RATES[opt] ?? 1;
|
|
2368
|
+
await api.kv.set(`${KV_PREFIX}.currency`, sym);
|
|
2369
|
+
await api.kv.set(`${KV_PREFIX}.rate`, defRate);
|
|
2370
|
+
await api.kv.set(`${KV_PREFIX}.balance_currency`, opt);
|
|
2371
|
+
signals.setBalanceCurrency(opt);
|
|
2372
|
+
signals.setCurrencySymbol(sym);
|
|
2373
|
+
signals.setExchangeRate(defRate);
|
|
2374
|
+
context.ui.toast.show({ message: t()("currencySet", { v: opt, s: sym, r: defRate }) });
|
|
2375
|
+
}
|
|
2376
|
+
},
|
|
2377
|
+
// ── /cache-rate 调整汇率 ──
|
|
2378
|
+
{
|
|
2379
|
+
id: "opencode-visual-cache.cache.rate",
|
|
2380
|
+
title: "Cache: Set Exchange Rate",
|
|
2381
|
+
description: "Set the exchange rate multiplier for the selected currency",
|
|
2382
|
+
group: "Cache",
|
|
2383
|
+
palette: true,
|
|
2384
|
+
slash: { name: "cache-rate" },
|
|
2385
|
+
run: async () => {
|
|
2386
|
+
const val = await context.ui.dialog.prompt({
|
|
2387
|
+
title: "Exchange Rate",
|
|
2388
|
+
message: "Enter the exchange rate from USD to your currency (e.g. 7.2 for CNY)",
|
|
2389
|
+
placeholder: "1.0"
|
|
2390
|
+
});
|
|
2391
|
+
if (val === void 0) return;
|
|
2392
|
+
const n = parseFloat(val);
|
|
2393
|
+
if (n > 0) {
|
|
2394
|
+
await api.kv.set(`${KV_PREFIX}.rate`, n);
|
|
2395
|
+
signals.setExchangeRate(n);
|
|
2396
|
+
context.ui.toast.show({ message: t()("rateSet", { r: n }) });
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
},
|
|
2400
|
+
// ── /cache-section 开关区块与边框 ──
|
|
2401
|
+
{
|
|
2402
|
+
id: "opencode-visual-cache.cache.section",
|
|
2403
|
+
title: "Cache: Toggle Section",
|
|
2404
|
+
description: "Show or hide a sidebar section",
|
|
2405
|
+
group: "Cache",
|
|
2406
|
+
palette: true,
|
|
2407
|
+
slash: { name: "cache-section" },
|
|
2408
|
+
run: async () => {
|
|
2409
|
+
const detailOn = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
|
|
2410
|
+
const modelOn = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
|
|
2411
|
+
const distOn = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
|
|
2412
|
+
const skillsOn = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
|
|
2413
|
+
const balanceOn = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
|
|
2414
|
+
const bottomOn = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
|
|
2415
|
+
const borderOn = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
|
|
2416
|
+
const labels = {
|
|
2417
|
+
detail: t()("secDetail"),
|
|
2418
|
+
model: t()("secModel"),
|
|
2419
|
+
dist: t()("distTitle"),
|
|
2420
|
+
skills: t()("secSkills"),
|
|
2421
|
+
balance: t()("secBalance"),
|
|
2422
|
+
bottom: t()("secBottom"),
|
|
2423
|
+
border: t()("secBorder")
|
|
2424
|
+
};
|
|
2425
|
+
const optTitle = (label, on) => `${visualPadEnd(label, 15)}[${on ? "ON" : "OFF"}]`;
|
|
2426
|
+
const opt = await context.ui.dialog.select({
|
|
2427
|
+
title: t()("secToggle"),
|
|
2428
|
+
options: [
|
|
2429
|
+
{ title: optTitle(labels.detail, detailOn), value: "detail" },
|
|
2430
|
+
{ title: optTitle(labels.model, modelOn), value: "model" },
|
|
2431
|
+
{ title: optTitle(labels.dist, distOn), value: "dist" },
|
|
2432
|
+
{ title: optTitle(labels.skills, skillsOn), value: "skills" },
|
|
2433
|
+
{ title: optTitle(labels.balance, balanceOn), value: "balance" },
|
|
2434
|
+
{ title: optTitle(labels.bottom, bottomOn), value: "bottom" },
|
|
2435
|
+
{ title: optTitle(labels.border, borderOn), value: "border" }
|
|
2436
|
+
]
|
|
2437
|
+
});
|
|
2438
|
+
if (!opt) return;
|
|
2439
|
+
if (opt === "border") {
|
|
2440
|
+
const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
|
|
2441
|
+
await api.kv.set(`${KV_PREFIX}.border`, !cur);
|
|
2442
|
+
signals.setBorderVisible(!cur);
|
|
2443
|
+
context.ui.toast.show({ message: !cur ? t()("borderShown") : t()("borderHidden") });
|
|
2444
|
+
} else {
|
|
2445
|
+
const key = `${KV_PREFIX}.section.${opt}`;
|
|
2446
|
+
const cur = Boolean(api.kv.get(key, true));
|
|
2447
|
+
await api.kv.set(key, !cur);
|
|
2448
|
+
if (opt === "detail") signals.setSectionDetail(!cur);
|
|
2449
|
+
if (opt === "model") signals.setSectionModel(!cur);
|
|
2450
|
+
if (opt === "dist") signals.setSectionDist(!cur);
|
|
2451
|
+
if (opt === "skills") signals.setSectionSkills(!cur);
|
|
2452
|
+
if (opt === "balance") signals.setSectionBalance(!cur);
|
|
2453
|
+
if (opt === "bottom") signals.setSectionBottom(!cur);
|
|
2454
|
+
const name = labels[opt] ?? opt;
|
|
2455
|
+
context.ui.toast.show({ message: t()(!cur ? "sectionShown" : "sectionHidden", { s: name }) });
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
},
|
|
2459
|
+
// ── /cache-config 查看当前配置 ──
|
|
2460
|
+
{
|
|
2461
|
+
id: "opencode-visual-cache.cache.config",
|
|
2462
|
+
title: "Cache: Show Config",
|
|
2463
|
+
description: "Display the current plugin configuration",
|
|
2464
|
+
group: "Cache",
|
|
2465
|
+
palette: true,
|
|
2466
|
+
slash: { name: "cache-config" },
|
|
2467
|
+
run: () => {
|
|
2468
|
+
const sym = api.kv.get(`${KV_PREFIX}.currency`) ?? "$";
|
|
2469
|
+
const rate = api.kv.get(`${KV_PREFIX}.rate`) ?? 1;
|
|
2470
|
+
const detail = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
|
|
2471
|
+
const model = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
|
|
2472
|
+
const dist = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
|
|
2473
|
+
const skills = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
|
|
2474
|
+
const balance = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
|
|
2475
|
+
const bottom = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
|
|
2476
|
+
const on = (v) => v ? "ON" : "OFF";
|
|
2477
|
+
context.ui.toast.show({
|
|
2478
|
+
title: t()("panelConfigTitle"),
|
|
2479
|
+
message: t()("panelConfigMsg", {
|
|
2480
|
+
c: sym,
|
|
2481
|
+
r: rate,
|
|
2482
|
+
d: on(detail),
|
|
2483
|
+
m: on(model),
|
|
2484
|
+
t: on(dist),
|
|
2485
|
+
k: on(skills),
|
|
2486
|
+
b: on(balance),
|
|
2487
|
+
f: on(bottom)
|
|
2488
|
+
})
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
},
|
|
2492
|
+
// ── /cache-lang 切换显示语言 ──
|
|
2493
|
+
{
|
|
2494
|
+
id: "opencode-visual-cache.cache.lang",
|
|
2495
|
+
title: "Cache: Switch Language",
|
|
2496
|
+
description: "Switch display language (Chinese / English / \u65E5\u672C\u8A9E / \uD55C\uAD6D\uC5B4)",
|
|
2497
|
+
group: "Cache",
|
|
2498
|
+
palette: true,
|
|
2499
|
+
slash: { name: "cache-lang" },
|
|
2500
|
+
run: async () => {
|
|
2501
|
+
const cur = signals.langCode();
|
|
2502
|
+
const opt = await context.ui.dialog.select({
|
|
2503
|
+
title: t()("langTitle"),
|
|
2504
|
+
options: LANG_META.map((m) => ({
|
|
2505
|
+
title: `${visualPadEnd(m.label, 9)}${cur === m.code ? "\u2713" : ""}`,
|
|
2506
|
+
value: m.code
|
|
2507
|
+
}))
|
|
2508
|
+
});
|
|
2509
|
+
if (!opt) return;
|
|
2510
|
+
await api.kv.set(`${KV_PREFIX}.lang`, opt);
|
|
2511
|
+
signals.setLangCode(opt);
|
|
2512
|
+
context.ui.toast.show({ message: t()("langSwitched") });
|
|
2513
|
+
}
|
|
2514
|
+
},
|
|
2515
|
+
// ── /cache-balance 切换余额提供商 / 自动切换 ──
|
|
2516
|
+
{
|
|
2517
|
+
id: "opencode-visual-cache.cache.balance",
|
|
2518
|
+
title: "Cache: Switch Balance Provider",
|
|
2519
|
+
description: "\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546 / \u81EA\u52A8\u5207\u6362\u5F53\u524D\u4F1A\u8BDD\u63D0\u4F9B\u5546 | Switch balance provider / auto-switch session provider",
|
|
2520
|
+
group: "Cache",
|
|
2521
|
+
palette: true,
|
|
2522
|
+
slash: { name: "cache-balance" },
|
|
2523
|
+
run: async () => {
|
|
2524
|
+
const current = signals.balanceProviderId();
|
|
2525
|
+
const auto = signals.autoBalance();
|
|
2526
|
+
const autoLabel = `${t()("autoSwitchOpt")} [${auto ? "ON" : "OFF"}]`;
|
|
2527
|
+
const opt = await context.ui.dialog.select({
|
|
2528
|
+
title: t()("balProvTitle"),
|
|
2529
|
+
options: [
|
|
2530
|
+
{ title: autoLabel, value: "__auto__" },
|
|
2531
|
+
...balanceProviders.map((p) => ({
|
|
2532
|
+
title: providerOptionTitle(p, current),
|
|
2533
|
+
value: p.id
|
|
2534
|
+
}))
|
|
2535
|
+
]
|
|
2536
|
+
});
|
|
2537
|
+
if (!opt) return;
|
|
2538
|
+
if (opt === "__auto__") {
|
|
2539
|
+
const next = !auto;
|
|
2540
|
+
await api.kv.set(`${KV_PREFIX}.balance.auto`, next);
|
|
2541
|
+
signals.setAutoBalance(next);
|
|
2542
|
+
context.ui.toast.show({ message: next ? t()("autoSwitchOn") : t()("autoSwitchOff") });
|
|
2543
|
+
} else {
|
|
2544
|
+
const provider = getBalanceProvider(opt);
|
|
2545
|
+
await api.kv.set(`${KV_PREFIX}.balance.provider`, provider.id);
|
|
2546
|
+
await api.kv.set(`${KV_PREFIX}.balance.auto`, false);
|
|
2547
|
+
signals.setBalanceProviderId(provider.id);
|
|
2548
|
+
signals.setAutoBalance(false);
|
|
2549
|
+
signals.setBalanceUnsupported(false);
|
|
2550
|
+
signals.setBalanceRefresh(signals.balanceRefresh() + 1);
|
|
2551
|
+
const hasKey = !!api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "");
|
|
2552
|
+
if (!hasKey) {
|
|
2553
|
+
await promptBalanceKey(provider);
|
|
2554
|
+
} else {
|
|
2555
|
+
context.ui.toast.show({ message: t()("providerManual", { p: provider.name }) });
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
},
|
|
2560
|
+
// ── /cache-balance-key 设置余额 API Key(两步:选 provider → 输 key)──
|
|
2561
|
+
{
|
|
2562
|
+
id: "opencode-visual-cache.cache.balance.key",
|
|
2563
|
+
title: "Cache: Set Balance API Key",
|
|
2564
|
+
description: "Select a provider and set its API key for balance display",
|
|
2565
|
+
group: "Cache",
|
|
2566
|
+
palette: true,
|
|
2567
|
+
slash: { name: "cache-balance-key" },
|
|
2568
|
+
run: async () => {
|
|
2569
|
+
const opt = await context.ui.dialog.select({
|
|
2570
|
+
title: t()("balSelectTitle"),
|
|
2571
|
+
options: balanceProviders.map((p) => ({
|
|
2572
|
+
title: providerOptionTitle(p),
|
|
2573
|
+
value: p.id
|
|
2574
|
+
}))
|
|
2575
|
+
});
|
|
2576
|
+
if (!opt) return;
|
|
2577
|
+
const provider = getBalanceProvider(opt);
|
|
2578
|
+
await api.kv.set(`${KV_PREFIX}.balance.provider`, provider.id);
|
|
2579
|
+
await api.kv.set(`${KV_PREFIX}.balance.auto`, false);
|
|
2580
|
+
signals.setBalanceProviderId(provider.id);
|
|
2581
|
+
signals.setAutoBalance(false);
|
|
2582
|
+
signals.setBalanceRefresh(signals.balanceRefresh() + 1);
|
|
2583
|
+
await promptBalanceKey(provider);
|
|
2584
|
+
}
|
|
2585
|
+
},
|
|
2586
|
+
// ── /cache-debug-skills 技能检测调试 ──
|
|
2587
|
+
{
|
|
2588
|
+
id: "opencode-visual-cache.cache.debug-skills",
|
|
2589
|
+
title: "Cache: Debug Skills Detection",
|
|
2590
|
+
description: "Dump all tool parts found in the current session for skill detection debugging",
|
|
2591
|
+
group: "Cache",
|
|
2592
|
+
palette: true,
|
|
2593
|
+
slash: { name: "cache-debug-skills" },
|
|
2594
|
+
run: () => {
|
|
2595
|
+
const sid = currentSessionID(context);
|
|
2596
|
+
if (!sid) {
|
|
2597
|
+
context.ui.toast.show({ message: t()("runInSession"), variant: "warning" });
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
const msgs = api.state.session.messages(sid);
|
|
2601
|
+
const byTool = {};
|
|
2602
|
+
const skillParts = [];
|
|
2603
|
+
for (const msg of msgs) {
|
|
2604
|
+
if (msg.role !== "assistant") continue;
|
|
2605
|
+
for (const p of extractToolParts(msg)) {
|
|
2606
|
+
const tool = String(p.tool ?? "?");
|
|
2607
|
+
byTool[tool] = (byTool[tool] ?? 0) + 1;
|
|
2608
|
+
if (tool === "skill") {
|
|
2609
|
+
const meta = p.state?.metadata;
|
|
2610
|
+
const rootMeta = p.metadata;
|
|
2611
|
+
skillParts.push(`state.metadata=${JSON.stringify(meta)} | root.metadata=${JSON.stringify(rootMeta)} | state.title="${p.state?.title}" | state.output[:80]="${String(p.state?.output ?? "").slice(0, 80)}"`);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
const summary = Object.entries(byTool).map(([k, v]) => `${k}: ${v}`).join(" | ");
|
|
2616
|
+
const extra = skillParts.length > 0 ? "\n\nSkill parts:\n" + skillParts.join("\n") : "\n\n\u26A0 No skill tool parts found \u2014 AI may be reading SKILL.md instead. Try: 'Use the skill tool to load karpathy-guidelines'";
|
|
2617
|
+
context.ui.toast.show({
|
|
2618
|
+
title: `Tool Summary (${Object.keys(byTool).length} types)`,
|
|
2619
|
+
message: summary + extra
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
},
|
|
2623
|
+
// ── /cache-session 子代理缓存统计 ──
|
|
2624
|
+
{
|
|
2625
|
+
id: "opencode-visual-cache.cache.session",
|
|
2626
|
+
title: "Cache: Sub-Agent Stats",
|
|
2627
|
+
description: "View token cache statistics for a sub-agent by session ID",
|
|
2628
|
+
group: "Cache",
|
|
2629
|
+
palette: true,
|
|
2630
|
+
slash: { name: "cache-session" },
|
|
2631
|
+
run: async () => {
|
|
2632
|
+
const parentSid = currentSessionID(context);
|
|
2633
|
+
const SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "delegate", "call_omo_agent"]);
|
|
2634
|
+
const children = [];
|
|
2635
|
+
if (parentSid) {
|
|
2636
|
+
try {
|
|
2637
|
+
const msgs = api.state.session.messages(parentSid);
|
|
2638
|
+
for (const msg of msgs) {
|
|
2639
|
+
if (msg.role !== "assistant") continue;
|
|
2640
|
+
for (const p of extractToolParts(msg)) {
|
|
2641
|
+
const tool = String(p.tool ?? "");
|
|
2642
|
+
if (!SUBAGENT_TOOLS.has(tool)) continue;
|
|
2643
|
+
const st = p.state;
|
|
2644
|
+
const stMeta = st?.metadata;
|
|
2645
|
+
const subSid = stMeta?.session_id ?? stMeta?.sessionId;
|
|
2646
|
+
if (!subSid) continue;
|
|
2647
|
+
const sidStr = String(subSid);
|
|
2648
|
+
const input = st?.input;
|
|
2649
|
+
const agent = String(p.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
|
|
2650
|
+
const prompt = String(input?.prompt ?? "");
|
|
2651
|
+
const desc = input?.description ? String(input.description) : "";
|
|
2652
|
+
const title = desc || prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim().slice(0, 40) || agent;
|
|
2653
|
+
children.push({ title, value: sidStr, description: `${agent} \xB7 ${sidStr.slice(0, 24)}\u2026` });
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
} catch {
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2660
|
+
const unique = children.filter((c) => {
|
|
2661
|
+
if (seen.has(c.value)) return false;
|
|
2662
|
+
seen.add(c.value);
|
|
2663
|
+
return true;
|
|
2664
|
+
});
|
|
2665
|
+
if (unique.length > 0) {
|
|
2666
|
+
const currentSid = signals.overrideSessionId() ?? api.kv.get(`${KV_PREFIX}.session`, "");
|
|
2667
|
+
const options = unique.map((c, i) => ({
|
|
2668
|
+
title: `${i + 1}. ${c.title}`,
|
|
2669
|
+
value: c.value,
|
|
2670
|
+
description: c.description
|
|
2671
|
+
}));
|
|
2672
|
+
const backValue = "__main__";
|
|
2673
|
+
const backTitle = `\u2500 ${t()("backToMainTitle")}`;
|
|
2674
|
+
options.unshift({ title: backTitle, value: backValue, description: "" });
|
|
2675
|
+
options.push({ title: backTitle, value: backValue, description: "" });
|
|
2676
|
+
const currentIdx = currentSid ? options.findIndex((o) => o.value === currentSid) : -1;
|
|
2677
|
+
const opt = await context.ui.dialog.select({
|
|
2678
|
+
title: t()("subSelectTitle"),
|
|
2679
|
+
options,
|
|
2680
|
+
current: currentIdx >= 0 ? options[currentIdx].value : void 0
|
|
2681
|
+
});
|
|
2682
|
+
if (!opt) return;
|
|
2683
|
+
if (opt === backValue) {
|
|
2684
|
+
signals.setOverrideSessionId(void 0);
|
|
2685
|
+
await api.kv.set(`${KV_PREFIX}.session`, "");
|
|
2686
|
+
context.ui.toast.show({ message: t()("backToMain") });
|
|
2687
|
+
} else {
|
|
2688
|
+
signals.setOverrideSessionId(opt);
|
|
2689
|
+
await api.kv.set(`${KV_PREFIX}.session`, opt);
|
|
2690
|
+
context.ui.toast.show({ message: t()("subAgentSwitched", { s: opt.slice(0, 24) + "\u2026" }) });
|
|
2691
|
+
}
|
|
2692
|
+
} else {
|
|
2693
|
+
const val = await context.ui.dialog.prompt({
|
|
2694
|
+
title: signals.overrideSessionId() ? t()("subSwitchTitle") : t()("subViewTitle"),
|
|
2695
|
+
message: t()("subNoFound"),
|
|
2696
|
+
placeholder: "ses_..."
|
|
2697
|
+
});
|
|
2698
|
+
if (val === void 0) return;
|
|
2699
|
+
const sid = val.trim();
|
|
2700
|
+
if (sid) {
|
|
2701
|
+
signals.setOverrideSessionId(sid);
|
|
2702
|
+
await api.kv.set(`${KV_PREFIX}.session`, sid);
|
|
2703
|
+
context.ui.toast.show({ message: t()("subAgentSwitched", { s: sid.slice(0, 24) + "\u2026" }) });
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
},
|
|
2708
|
+
// ── /cache-session-back 返回主会话 ──
|
|
2709
|
+
{
|
|
2710
|
+
id: "opencode-visual-cache.cache.session.back",
|
|
2711
|
+
title: "Cache: Back to Main",
|
|
2712
|
+
description: "Return to main session stats",
|
|
2713
|
+
group: "Cache",
|
|
2714
|
+
palette: true,
|
|
2715
|
+
slash: { name: "cache-session-back" },
|
|
2716
|
+
run: async () => {
|
|
2717
|
+
signals.setOverrideSessionId(void 0);
|
|
2718
|
+
await api.kv.set(`${KV_PREFIX}.session`, "");
|
|
2719
|
+
context.ui.toast.show({ message: t()("backToMain") });
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
];
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
// src/v2/index.tsx
|
|
2726
|
+
var KV_PREFIX2 = "cache_panel";
|
|
2727
|
+
var BALANCE_POLL_MS = 5 * 60 * 1e3;
|
|
2728
|
+
var DEBUG_LANG = typeof process !== "undefined" ? process.env?.CACHE_TUI_LANG : void 0;
|
|
2729
|
+
var INIT_LANG = DEBUG_LANG !== void 0 && LANG_META.some((m) => m.code === DEBUG_LANG) ? DEBUG_LANG : detectLang();
|
|
2730
|
+
function createPanelSignals() {
|
|
2731
|
+
const [currencySymbol, setCurrencySymbol] = createSignal2("$");
|
|
2732
|
+
const [exchangeRate, setExchangeRate] = createSignal2(1);
|
|
2733
|
+
const [langCode, setLangCode] = createSignal2(INIT_LANG);
|
|
2734
|
+
const [sectionDetail, setSectionDetail] = createSignal2(true);
|
|
2735
|
+
const [sectionModel, setSectionModel] = createSignal2(true);
|
|
2736
|
+
const [sectionDist, setSectionDist] = createSignal2(true);
|
|
2737
|
+
const [sectionSkills, setSectionSkills] = createSignal2(true);
|
|
2738
|
+
const [sectionBalance, setSectionBalance] = createSignal2(true);
|
|
2739
|
+
const [sectionBottom, setSectionBottom] = createSignal2(true);
|
|
2740
|
+
const [balanceRefresh, setBalanceRefresh] = createSignal2(0);
|
|
2741
|
+
const [balanceProviderId, setBalanceProviderId] = createSignal2("");
|
|
2742
|
+
const [autoBalance, setAutoBalance] = createSignal2(true);
|
|
2743
|
+
const [balanceUnsupported, setBalanceUnsupported] = createSignal2(false);
|
|
2744
|
+
const [balanceState, setBalanceState] = createSignal2({
|
|
2745
|
+
status: "idle",
|
|
2746
|
+
data: null,
|
|
2747
|
+
lastFetch: 0
|
|
2748
|
+
});
|
|
2749
|
+
const [balanceCurrency, setBalanceCurrency] = createSignal2("");
|
|
2750
|
+
const [borderVisible, setBorderVisible] = createSignal2(true);
|
|
2751
|
+
const [overrideSessionId, setOverrideSessionId] = createSignal2(void 0);
|
|
2752
|
+
const [sidebarVisible, setSidebarVisible] = createSignal2(true);
|
|
2753
|
+
return {
|
|
2754
|
+
currencySymbol,
|
|
2755
|
+
setCurrencySymbol,
|
|
2756
|
+
exchangeRate,
|
|
2757
|
+
setExchangeRate,
|
|
2758
|
+
langCode,
|
|
2759
|
+
setLangCode,
|
|
2760
|
+
sectionDetail,
|
|
2761
|
+
setSectionDetail,
|
|
2762
|
+
sectionModel,
|
|
2763
|
+
setSectionModel,
|
|
2764
|
+
sectionDist,
|
|
2765
|
+
setSectionDist,
|
|
2766
|
+
sectionSkills,
|
|
2767
|
+
setSectionSkills,
|
|
2768
|
+
sectionBalance,
|
|
2769
|
+
setSectionBalance,
|
|
2770
|
+
sectionBottom,
|
|
2771
|
+
setSectionBottom,
|
|
2772
|
+
balanceRefresh,
|
|
2773
|
+
setBalanceRefresh,
|
|
2774
|
+
balanceProviderId,
|
|
2775
|
+
setBalanceProviderId,
|
|
2776
|
+
autoBalance,
|
|
2777
|
+
setAutoBalance,
|
|
2778
|
+
balanceUnsupported,
|
|
2779
|
+
setBalanceUnsupported,
|
|
2780
|
+
balanceState,
|
|
2781
|
+
setBalanceState,
|
|
2782
|
+
balanceCurrency,
|
|
2783
|
+
setBalanceCurrency,
|
|
2784
|
+
borderVisible,
|
|
2785
|
+
setBorderVisible,
|
|
2786
|
+
overrideSessionId,
|
|
2787
|
+
setOverrideSessionId,
|
|
2788
|
+
sidebarVisible,
|
|
2789
|
+
setSidebarVisible
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
function PluginRoot(props) {
|
|
2793
|
+
let balanceSeq = 0;
|
|
2794
|
+
props.context.keymap.layer(() => ({
|
|
2795
|
+
mode: "global",
|
|
2796
|
+
commands: makeCommands(props.context, props.api, props.signals)
|
|
2797
|
+
}));
|
|
2798
|
+
onMount2(() => {
|
|
2799
|
+
try {
|
|
2800
|
+
const saved = props.api.kv.get(`${KV_PREFIX2}.lang`);
|
|
2801
|
+
if (saved && LANG_META.some((m) => m.code === saved)) {
|
|
2802
|
+
props.signals.setLangCode(saved);
|
|
2803
|
+
}
|
|
2804
|
+
} catch {
|
|
2805
|
+
}
|
|
2806
|
+
});
|
|
2807
|
+
const pollBalance = async () => {
|
|
2808
|
+
const provider = getBalanceProvider(props.signals.balanceProviderId());
|
|
2809
|
+
const key = props.api.kv.get(`${KV_PREFIX2}.balance.${provider.id}.key`, "") || findOpencodeKeyV2(props.context, provider);
|
|
2810
|
+
const set = props.signals.setBalanceState;
|
|
2811
|
+
if (props.signals.balanceUnsupported()) {
|
|
2812
|
+
set({
|
|
2813
|
+
status: "idle",
|
|
2814
|
+
data: null,
|
|
2815
|
+
lastFetch: 0,
|
|
2816
|
+
error: void 0,
|
|
2817
|
+
key: void 0
|
|
2818
|
+
});
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
if (!key) {
|
|
2822
|
+
set({
|
|
2823
|
+
status: "idle",
|
|
2824
|
+
data: null,
|
|
2825
|
+
lastFetch: 0,
|
|
2826
|
+
error: void 0,
|
|
2827
|
+
key: void 0
|
|
2828
|
+
});
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
const now = Date.now();
|
|
2832
|
+
const prev = props.signals.balanceState();
|
|
2833
|
+
if (prev.status === "ok" && prev.key === key && now - prev.lastFetch < BALANCE_POLL_MS) return;
|
|
2834
|
+
const seq = ++balanceSeq;
|
|
2835
|
+
set({
|
|
2836
|
+
...prev,
|
|
2837
|
+
status: "loading",
|
|
2838
|
+
error: void 0,
|
|
2839
|
+
key
|
|
2840
|
+
});
|
|
2841
|
+
const controller = new AbortController();
|
|
2842
|
+
let timedOut = false;
|
|
2843
|
+
const timer = setTimeout(() => {
|
|
2844
|
+
timedOut = true;
|
|
2845
|
+
controller.abort();
|
|
2846
|
+
}, 1e4);
|
|
2847
|
+
try {
|
|
2848
|
+
const data = await provider.fetchBalance(key, controller.signal);
|
|
2849
|
+
clearTimeout(timer);
|
|
2850
|
+
if (seq !== balanceSeq) return;
|
|
2851
|
+
set({
|
|
2852
|
+
status: "ok",
|
|
2853
|
+
data,
|
|
2854
|
+
lastFetch: Date.now(),
|
|
2855
|
+
error: void 0,
|
|
2856
|
+
key
|
|
2857
|
+
});
|
|
2858
|
+
} catch (err) {
|
|
2859
|
+
clearTimeout(timer);
|
|
2860
|
+
if (seq !== balanceSeq) return;
|
|
2861
|
+
const code = timedOut ? "TIMEOUT" : err instanceof Error ? err.message : "";
|
|
2862
|
+
set({
|
|
2863
|
+
status: "error",
|
|
2864
|
+
data: null,
|
|
2865
|
+
lastFetch: 0,
|
|
2866
|
+
error: code,
|
|
2867
|
+
key
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
createEffect2(() => {
|
|
2872
|
+
void props.signals.balanceRefresh();
|
|
2873
|
+
untrack2(() => {
|
|
2874
|
+
void pollBalance();
|
|
2875
|
+
});
|
|
2876
|
+
});
|
|
2877
|
+
const balanceTimer = setInterval(pollBalance, BALANCE_POLL_MS);
|
|
2878
|
+
onCleanup2(() => clearInterval(balanceTimer));
|
|
2879
|
+
let lastSlotSid = props.sessionID;
|
|
2880
|
+
createEffect2(() => {
|
|
2881
|
+
const sid = props.sessionID;
|
|
2882
|
+
if (sid !== lastSlotSid) {
|
|
2883
|
+
lastSlotSid = sid;
|
|
2884
|
+
if (props.signals.overrideSessionId()) {
|
|
2885
|
+
props.signals.setOverrideSessionId(void 0);
|
|
2886
|
+
void props.api.kv.set(`${KV_PREFIX2}.session`, "");
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
});
|
|
2890
|
+
return _$createComponent3(TokenCachePanel, {
|
|
2891
|
+
get theme() {
|
|
2892
|
+
return mapTheme(props.context.theme);
|
|
2893
|
+
},
|
|
2894
|
+
get api() {
|
|
2895
|
+
return props.api;
|
|
2896
|
+
},
|
|
2897
|
+
get sessionId() {
|
|
2898
|
+
return props.sessionID;
|
|
2899
|
+
},
|
|
2900
|
+
get signals() {
|
|
2901
|
+
return props.signals;
|
|
2902
|
+
}
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
var mod = {
|
|
2906
|
+
id: "opencode-visual-cache",
|
|
2907
|
+
setup(context) {
|
|
2908
|
+
const api = createPanelApi(context);
|
|
2909
|
+
const signals = createPanelSignals();
|
|
2910
|
+
context.ui.slot({
|
|
2911
|
+
prepend: "sidebar.content",
|
|
2912
|
+
render: (props) => _$createComponent3(PluginRoot, {
|
|
2913
|
+
context,
|
|
2914
|
+
api,
|
|
2915
|
+
signals,
|
|
2916
|
+
get sessionID() {
|
|
2917
|
+
return String(props.sessionID ?? "");
|
|
2918
|
+
}
|
|
2919
|
+
})
|
|
2920
|
+
});
|
|
2921
|
+
context.ui.slot({
|
|
2922
|
+
append: "prompt.footer.status",
|
|
2923
|
+
render: (props) => _$createComponent3(StatusView, {
|
|
2924
|
+
context,
|
|
2925
|
+
signals,
|
|
2926
|
+
get sessionID() {
|
|
2927
|
+
return String(props.sessionID ?? "");
|
|
2928
|
+
}
|
|
2929
|
+
})
|
|
2930
|
+
});
|
|
2931
|
+
context.storage.store("opencode-visual-cache.panel", {
|
|
2932
|
+
initial: {
|
|
2933
|
+
collapsed: false
|
|
2934
|
+
}
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
var index_default = mod;
|
|
2939
|
+
export {
|
|
2940
|
+
index_default as default
|
|
2941
|
+
};
|