cc-zh-watcher 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -121
- package/dist/cli.js +2185 -389
- package/package.json +6 -5
package/dist/cli.js
CHANGED
|
@@ -39237,8 +39237,7 @@ var {
|
|
|
39237
39237
|
} = import__.default;
|
|
39238
39238
|
|
|
39239
39239
|
// src/cli.ts
|
|
39240
|
-
import { existsSync as existsSync8, readFileSync as readFileSync8
|
|
39241
|
-
import { dirname as dirname4 } from "node:path";
|
|
39240
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
|
|
39242
39241
|
import { createInterface } from "node:readline/promises";
|
|
39243
39242
|
import { stdin as input, stdout as output } from "node:process";
|
|
39244
39243
|
|
|
@@ -44790,6 +44789,12 @@ var PROMPT_KEYS = [
|
|
|
44790
44789
|
var DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
44791
44790
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
44792
44791
|
var DEFAULT_TIMEOUT = 30;
|
|
44792
|
+
var DEFAULT_NOTIFY_ON_COMPLETE = true;
|
|
44793
|
+
var DEFAULT_TRANSLATE_THINKING = false;
|
|
44794
|
+
var DEFAULT_TRANSLATE_TODOS = true;
|
|
44795
|
+
var DEFAULT_TRANSLATE_AGENT = true;
|
|
44796
|
+
var DEFAULT_CONTEXT_ENABLED = true;
|
|
44797
|
+
var DEFAULT_SHOW_TOOL_CALLS = true;
|
|
44793
44798
|
function readJsonIfExists(path) {
|
|
44794
44799
|
if (!existsSync2(path))
|
|
44795
44800
|
return {};
|
|
@@ -44835,10 +44840,23 @@ function loadConfig(opts) {
|
|
|
44835
44840
|
base_url: layered.base_url ?? DEFAULT_BASE_URL,
|
|
44836
44841
|
model: layered.model ?? DEFAULT_MODEL,
|
|
44837
44842
|
timeout_seconds: layered.timeout_seconds ?? DEFAULT_TIMEOUT,
|
|
44843
|
+
notify_on_complete: validateBool(layered.notify_on_complete, "notify_on_complete", DEFAULT_NOTIFY_ON_COMPLETE),
|
|
44844
|
+
translate_thinking: validateBool(layered.translate_thinking, "translate_thinking", DEFAULT_TRANSLATE_THINKING),
|
|
44845
|
+
translate_todos: validateBool(layered.translate_todos, "translate_todos", DEFAULT_TRANSLATE_TODOS),
|
|
44846
|
+
translate_agent: validateBool(layered.translate_agent, "translate_agent", DEFAULT_TRANSLATE_AGENT),
|
|
44847
|
+
context_enabled: validateBool(layered.context_enabled, "context_enabled", DEFAULT_CONTEXT_ENABLED),
|
|
44848
|
+
show_tool_calls: validateBool(layered.show_tool_calls, "show_tool_calls", DEFAULT_SHOW_TOOL_CALLS),
|
|
44838
44849
|
prompts: validatePromptMap(layered.prompts, "prompts"),
|
|
44839
44850
|
prompt_extensions: validatePromptMap(layered.prompt_extensions, "prompt_extensions")
|
|
44840
44851
|
};
|
|
44841
44852
|
}
|
|
44853
|
+
function validateBool(raw, fieldName, fallback) {
|
|
44854
|
+
if (raw === undefined || raw === null)
|
|
44855
|
+
return fallback;
|
|
44856
|
+
if (typeof raw === "boolean")
|
|
44857
|
+
return raw;
|
|
44858
|
+
throw new Error(`${fieldName} must be a boolean (got ${typeof raw})`);
|
|
44859
|
+
}
|
|
44842
44860
|
function validatePromptMap(raw, fieldName) {
|
|
44843
44861
|
if (raw === undefined || raw === null)
|
|
44844
44862
|
return;
|
|
@@ -44860,6 +44878,30 @@ function validatePromptMap(raw, fieldName) {
|
|
|
44860
44878
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
44861
44879
|
}
|
|
44862
44880
|
|
|
44881
|
+
// src/fs-utils.ts
|
|
44882
|
+
import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
44883
|
+
import { dirname } from "node:path";
|
|
44884
|
+
import { randomBytes } from "node:crypto";
|
|
44885
|
+
function writeAtomic(path, content, opts = {}) {
|
|
44886
|
+
const { mode, mkdir = true } = opts;
|
|
44887
|
+
if (mkdir) {
|
|
44888
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
44889
|
+
}
|
|
44890
|
+
const tmp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
44891
|
+
try {
|
|
44892
|
+
const writeOpts = { encoding: "utf-8" };
|
|
44893
|
+
if (mode !== undefined)
|
|
44894
|
+
writeOpts.mode = mode;
|
|
44895
|
+
writeFileSync(tmp, content, writeOpts);
|
|
44896
|
+
renameSync(tmp, path);
|
|
44897
|
+
} catch (err) {
|
|
44898
|
+
try {
|
|
44899
|
+
unlinkSync(tmp);
|
|
44900
|
+
} catch {}
|
|
44901
|
+
throw err;
|
|
44902
|
+
}
|
|
44903
|
+
}
|
|
44904
|
+
|
|
44863
44905
|
// src/translate.ts
|
|
44864
44906
|
var ZH_TO_EN_BASE = `You are a professional translator. The user types Chinese instructions for Claude Code (an AI coding assistant CLI), and your job is to translate the Chinese into natural, professional technical English that Claude Code will read as the user's prompt.
|
|
44865
44907
|
|
|
@@ -44878,26 +44920,60 @@ Translate only the prose around these.
|
|
|
44878
44920
|
Style:
|
|
44879
44921
|
- Concise. Match the source's length; do not pad with explanations the source does not contain.
|
|
44880
44922
|
- Imperative voice when the source is imperative ("帮我看一下" → "Take a look at"); declarative otherwise.
|
|
44881
|
-
- Silently fix obvious typos, missing words, and ungrammatical fragments.
|
|
44923
|
+
- Silently fix obvious typos, missing words, and ungrammatical fragments. Common patterns from a Chinese IME:
|
|
44924
|
+
· 同音/近音字混用: "在" ↔ "再", "的/地/得", "做/作", "象/像", "因为/应为"
|
|
44925
|
+
· 错别字: "测式" → "测试", "克立" → "克隆", "提价" → "提交"
|
|
44926
|
+
· 漏字 / 多字: "一条录" → "一条记录"
|
|
44927
|
+
· 缺主语或谓语: 句子片段 → 完整祈使句
|
|
44928
|
+
ONLY fix when the intent is unambiguous. If a word could legitimately mean either reading (e.g. "在" really meant 在 not 再), translate AS-IS rather than guess. Conservative repair beats confident wrong-fix.
|
|
44882
44929
|
- If the input is brief or fragmentary, produce a complete grammatical English sentence reflecting the user's likely intent — but do not invent specifics the user did not imply.
|
|
44883
44930
|
- Aim for the register of a competent engineer writing a message to a colleague: clear, direct, no fluff, no hedging.`;
|
|
44884
|
-
var ZH_TO_EN_WITH_CONTEXT_BASE = `You are a professional translator. The user types Chinese instructions for Claude Code (an AI coding assistant CLI). The PRIOR_REPLY block below is Claude Code's previous English reply — use it
|
|
44931
|
+
var ZH_TO_EN_WITH_CONTEXT_BASE = `You are a professional translator AND a quick sanity-check filter. The user types Chinese instructions for Claude Code (an AI coding assistant CLI). The PRIOR_REPLY block below is Claude Code's previous English reply — use it to disambiguate pronouns ("那个" / "你说的"), expand elliptical references ("再加一条" → "add one more entry"), silently correct obvious typos, AND detect when the user's input is unlikely to be actionable in this context.
|
|
44885
44932
|
|
|
44886
|
-
Output
|
|
44933
|
+
Output format (MANDATORY — two parts separated by ONE blank line):
|
|
44887
44934
|
|
|
44888
|
-
|
|
44935
|
+
Part 1 — first line, status:
|
|
44936
|
+
"OK" — normal: the user's input is clear given the prior reply
|
|
44937
|
+
"WARN: <说明>" — flag a likely problem; ONE Chinese sentence (≤30 字)
|
|
44938
|
+
|
|
44939
|
+
(single blank line)
|
|
44940
|
+
|
|
44941
|
+
Part 2 — the English translation of the NEW USER INPUT, exactly as
|
|
44942
|
+
you would translate it. Even when WARN is set, still translate as
|
|
44943
|
+
faithfully as possible — the user may decide to send it anyway.
|
|
44944
|
+
|
|
44945
|
+
WARN ONLY when confident a Chinese-speaking engineer reading the
|
|
44946
|
+
prior reply + the user's input would also see a problem. Triggers:
|
|
44947
|
+
- The user refers to something not in PRIOR_REPLY ("那个文件" but
|
|
44948
|
+
no file mentioned; "刚才说的方案" but Claude proposed nothing)
|
|
44949
|
+
- The user answers ambiguously to a clear multiple-choice question
|
|
44950
|
+
("好的" / "随便" when Claude asked "A or B?")
|
|
44951
|
+
- The user's input contradicts the topic of PRIOR_REPLY (different
|
|
44952
|
+
subject, mismatched verb)
|
|
44953
|
+
- The input is garbled enough that even with context the
|
|
44954
|
+
translation is just a guess
|
|
44955
|
+
|
|
44956
|
+
Output OK when in doubt. Spurious warnings slow the user down for
|
|
44957
|
+
nothing; missed warnings just route through Claude Code, which can
|
|
44958
|
+
ask for clarification itself. Do NOT WARN merely because the input
|
|
44959
|
+
is short — short imperatives ("继续", "好") after a clear prior
|
|
44960
|
+
reply are normal.
|
|
44961
|
+
|
|
44962
|
+
Translate only the prose; preserve verbatim:
|
|
44889
44963
|
- code blocks (fenced or indented), code fragments, anything in backticks
|
|
44890
44964
|
- identifier names, file paths, glob patterns, URLs
|
|
44891
44965
|
- command-line snippets, flags, environment variable names, git refs
|
|
44892
44966
|
- error messages quoted in the source
|
|
44893
44967
|
- numbers, units, version strings, hex / hash values
|
|
44894
44968
|
|
|
44895
|
-
Translate only the prose around these.
|
|
44896
|
-
|
|
44897
44969
|
Style:
|
|
44898
44970
|
- Concise. Match the new input's length; do not pad with explanations.
|
|
44899
44971
|
- Imperative voice when the source is imperative; declarative otherwise.
|
|
44900
|
-
- Silently fix obvious typos and ungrammatical fragments.
|
|
44972
|
+
- Silently fix obvious typos, missing words, and ungrammatical fragments. Common patterns from a Chinese IME:
|
|
44973
|
+
· 同音/近音字混用: "在" ↔ "再", "的/地/得", "做/作", "象/像"
|
|
44974
|
+
· 错别字: "测式" → "测试", "克立" → "克隆", "提价" → "提交"
|
|
44975
|
+
· 漏字 / 多字: "一条录" → "一条记录"
|
|
44976
|
+
The PRIOR_REPLY block is a strong disambiguation signal — if Claude just asked "Should I commit and push?" and the user types "提价 一下", "提价" is almost certainly "提交" given the context. ONLY fix when the intent is unambiguous; if uncertain, translate AS-IS.
|
|
44901
44977
|
- If the input is fragmentary, produce a complete grammatical English sentence reflecting the user's likely intent — using the prior reply as the disambiguation source, not as content to add.
|
|
44902
44978
|
- Aim for the register of a competent engineer writing a message to a colleague.`;
|
|
44903
44979
|
var EN_TO_ZH_BASE = `You are a professional translator. Claude Code (an AI coding assistant CLI) replies in English to a Chinese-speaking developer; your job is to render the English faithfully into natural, professional Simplified Chinese.
|
|
@@ -44958,10 +45034,12 @@ class TranslateError extends Error {
|
|
|
44958
45034
|
this.elapsed_ms = opts?.elapsed_ms ?? 0;
|
|
44959
45035
|
}
|
|
44960
45036
|
}
|
|
44961
|
-
async function translate(text, direction, config,
|
|
45037
|
+
async function translate(text, direction, config, options) {
|
|
44962
45038
|
if (!text.trim()) {
|
|
44963
45039
|
throw new TranslateError("empty input", "config", { code: "config" });
|
|
44964
45040
|
}
|
|
45041
|
+
const contextText = options?.contextText;
|
|
45042
|
+
const onDelta = options?.onDelta;
|
|
44965
45043
|
let systemPrompt;
|
|
44966
45044
|
let userMsg;
|
|
44967
45045
|
if (direction === "zh-to-en" && contextText && contextText.trim()) {
|
|
@@ -44976,20 +45054,47 @@ ${text}`;
|
|
|
44976
45054
|
systemPrompt = resolvePrompt(direction, config);
|
|
44977
45055
|
userMsg = text;
|
|
44978
45056
|
}
|
|
44979
|
-
const
|
|
45057
|
+
const baseBody = {
|
|
44980
45058
|
model: config.model,
|
|
44981
45059
|
messages: [
|
|
44982
45060
|
{ role: "system", content: systemPrompt },
|
|
44983
45061
|
{ role: "user", content: userMsg }
|
|
44984
45062
|
],
|
|
44985
|
-
temperature: 0.2
|
|
44986
|
-
stream: false
|
|
45063
|
+
temperature: 0.2
|
|
44987
45064
|
};
|
|
44988
45065
|
const url = config.base_url.replace(/\/$/, "") + "/chat/completions";
|
|
44989
45066
|
const controller = new AbortController;
|
|
44990
45067
|
const timer = setTimeout(() => controller.abort(), config.timeout_seconds * 1000);
|
|
45068
|
+
const onExternalAbort = () => controller.abort();
|
|
45069
|
+
if (options?.signal) {
|
|
45070
|
+
if (options.signal.aborted)
|
|
45071
|
+
controller.abort();
|
|
45072
|
+
else
|
|
45073
|
+
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
45074
|
+
}
|
|
44991
45075
|
const startedAt = performance.now();
|
|
44992
45076
|
const elapsed = () => Math.round(performance.now() - startedAt);
|
|
45077
|
+
const wantParse = options?.parseConcernHeader === true;
|
|
45078
|
+
try {
|
|
45079
|
+
const result2 = onDelta ? await runStreaming(url, baseBody, config, controller.signal, onDelta, elapsed) : await runNonStreaming(url, baseBody, config, controller.signal, elapsed);
|
|
45080
|
+
if (!wantParse)
|
|
45081
|
+
return result2;
|
|
45082
|
+
const parsed = parseConcernHeader(result2.content);
|
|
45083
|
+
if (parsed.concern === null && parsed.translation === result2.content) {
|
|
45084
|
+
return result2;
|
|
45085
|
+
}
|
|
45086
|
+
return {
|
|
45087
|
+
...result2,
|
|
45088
|
+
content: parsed.translation,
|
|
45089
|
+
...parsed.concern !== null ? { concern: parsed.concern } : {}
|
|
45090
|
+
};
|
|
45091
|
+
} finally {
|
|
45092
|
+
clearTimeout(timer);
|
|
45093
|
+
options?.signal?.removeEventListener("abort", onExternalAbort);
|
|
45094
|
+
}
|
|
45095
|
+
}
|
|
45096
|
+
async function runNonStreaming(url, baseBody, config, signal, elapsed) {
|
|
45097
|
+
const body = { ...baseBody, stream: false };
|
|
44993
45098
|
let resp;
|
|
44994
45099
|
try {
|
|
44995
45100
|
resp = await fetch(url, {
|
|
@@ -44999,12 +45104,11 @@ ${text}`;
|
|
|
44999
45104
|
Authorization: `Bearer ${config.api_key}`
|
|
45000
45105
|
},
|
|
45001
45106
|
body: JSON.stringify(body),
|
|
45002
|
-
signal
|
|
45107
|
+
signal
|
|
45003
45108
|
});
|
|
45004
45109
|
} catch (err) {
|
|
45005
|
-
clearTimeout(timer);
|
|
45006
45110
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45007
|
-
if (msg
|
|
45111
|
+
if (isAbortLike(msg)) {
|
|
45008
45112
|
throw new TranslateError(`request timed out after ${config.timeout_seconds}s`, "network", { code: "timeout", elapsed_ms: elapsed() });
|
|
45009
45113
|
}
|
|
45010
45114
|
throw new TranslateError(`network error: ${msg}`, "network", {
|
|
@@ -45012,7 +45116,6 @@ ${text}`;
|
|
|
45012
45116
|
elapsed_ms: elapsed()
|
|
45013
45117
|
});
|
|
45014
45118
|
}
|
|
45015
|
-
clearTimeout(timer);
|
|
45016
45119
|
let json;
|
|
45017
45120
|
try {
|
|
45018
45121
|
json = await resp.json();
|
|
@@ -45055,6 +45158,163 @@ ${text}`;
|
|
|
45055
45158
|
const usage = parseUsage(usageRaw);
|
|
45056
45159
|
return { content, elapsed_ms: elapsed(), usage };
|
|
45057
45160
|
}
|
|
45161
|
+
async function runStreaming(url, baseBody, config, signal, onDelta, elapsed) {
|
|
45162
|
+
const body = {
|
|
45163
|
+
...baseBody,
|
|
45164
|
+
stream: true,
|
|
45165
|
+
stream_options: { include_usage: true }
|
|
45166
|
+
};
|
|
45167
|
+
let resp;
|
|
45168
|
+
try {
|
|
45169
|
+
resp = await fetch(url, {
|
|
45170
|
+
method: "POST",
|
|
45171
|
+
headers: {
|
|
45172
|
+
"Content-Type": "application/json",
|
|
45173
|
+
Accept: "text/event-stream",
|
|
45174
|
+
Authorization: `Bearer ${config.api_key}`
|
|
45175
|
+
},
|
|
45176
|
+
body: JSON.stringify(body),
|
|
45177
|
+
signal
|
|
45178
|
+
});
|
|
45179
|
+
} catch (err) {
|
|
45180
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45181
|
+
if (isAbortLike(msg)) {
|
|
45182
|
+
throw new TranslateError(`request timed out after ${config.timeout_seconds}s`, "network", { code: "timeout", elapsed_ms: elapsed() });
|
|
45183
|
+
}
|
|
45184
|
+
throw new TranslateError(`network error: ${msg}`, "network", {
|
|
45185
|
+
code: "network",
|
|
45186
|
+
elapsed_ms: elapsed()
|
|
45187
|
+
});
|
|
45188
|
+
}
|
|
45189
|
+
if (!resp.ok) {
|
|
45190
|
+
let detail = `HTTP ${resp.status} ${resp.statusText}`;
|
|
45191
|
+
try {
|
|
45192
|
+
const text = await resp.text();
|
|
45193
|
+
try {
|
|
45194
|
+
const j = JSON.parse(text);
|
|
45195
|
+
const e = j.error;
|
|
45196
|
+
const msg = typeof e === "string" ? e : e?.message ?? null;
|
|
45197
|
+
if (msg)
|
|
45198
|
+
detail = msg;
|
|
45199
|
+
} catch {
|
|
45200
|
+
if (text)
|
|
45201
|
+
detail = text.slice(0, 200);
|
|
45202
|
+
}
|
|
45203
|
+
} catch {}
|
|
45204
|
+
throw new TranslateError(`API error: ${detail}`, "api", {
|
|
45205
|
+
code: `HTTP ${resp.status}`,
|
|
45206
|
+
elapsed_ms: elapsed()
|
|
45207
|
+
});
|
|
45208
|
+
}
|
|
45209
|
+
if (!resp.body) {
|
|
45210
|
+
throw new TranslateError("response has no body", "parse", {
|
|
45211
|
+
code: "parse",
|
|
45212
|
+
elapsed_ms: elapsed()
|
|
45213
|
+
});
|
|
45214
|
+
}
|
|
45215
|
+
const reader = resp.body.getReader();
|
|
45216
|
+
const decoder = new TextDecoder("utf-8");
|
|
45217
|
+
let buf = "";
|
|
45218
|
+
let content = "";
|
|
45219
|
+
let usage = null;
|
|
45220
|
+
let inlineError = null;
|
|
45221
|
+
try {
|
|
45222
|
+
while (true) {
|
|
45223
|
+
const { done, value } = await reader.read();
|
|
45224
|
+
if (done)
|
|
45225
|
+
break;
|
|
45226
|
+
buf += decoder.decode(value, { stream: true });
|
|
45227
|
+
for (;; ) {
|
|
45228
|
+
const m = /\r?\n\r?\n/.exec(buf);
|
|
45229
|
+
if (!m)
|
|
45230
|
+
break;
|
|
45231
|
+
const event = buf.slice(0, m.index);
|
|
45232
|
+
buf = buf.slice(m.index + m[0].length);
|
|
45233
|
+
for (const rawLine of event.split(/\r?\n/)) {
|
|
45234
|
+
const line = rawLine.trimEnd();
|
|
45235
|
+
if (!line || line.startsWith(":"))
|
|
45236
|
+
continue;
|
|
45237
|
+
if (!line.startsWith("data:"))
|
|
45238
|
+
continue;
|
|
45239
|
+
const payload = line.slice(5).trim();
|
|
45240
|
+
if (!payload || payload === "[DONE]")
|
|
45241
|
+
continue;
|
|
45242
|
+
let chunk2;
|
|
45243
|
+
try {
|
|
45244
|
+
chunk2 = JSON.parse(payload);
|
|
45245
|
+
} catch {
|
|
45246
|
+
continue;
|
|
45247
|
+
}
|
|
45248
|
+
const errObj = chunk2["error"];
|
|
45249
|
+
if (errObj !== undefined && errObj !== null) {
|
|
45250
|
+
inlineError = typeof errObj === "string" ? errObj : errObj.message ?? JSON.stringify(errObj);
|
|
45251
|
+
continue;
|
|
45252
|
+
}
|
|
45253
|
+
if (chunk2["usage"] !== undefined) {
|
|
45254
|
+
const u = parseUsage(chunk2["usage"]);
|
|
45255
|
+
if (u)
|
|
45256
|
+
usage = u;
|
|
45257
|
+
}
|
|
45258
|
+
const choices = chunk2["choices"];
|
|
45259
|
+
if (!Array.isArray(choices) || choices.length === 0)
|
|
45260
|
+
continue;
|
|
45261
|
+
const delta = choices[0].delta;
|
|
45262
|
+
const piece = delta?.content;
|
|
45263
|
+
if (typeof piece === "string" && piece.length > 0) {
|
|
45264
|
+
content += piece;
|
|
45265
|
+
onDelta(piece);
|
|
45266
|
+
}
|
|
45267
|
+
}
|
|
45268
|
+
}
|
|
45269
|
+
}
|
|
45270
|
+
} catch (err) {
|
|
45271
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45272
|
+
if (isAbortLike(msg)) {
|
|
45273
|
+
throw new TranslateError(`request timed out after ${config.timeout_seconds}s`, "network", { code: "timeout", elapsed_ms: elapsed() });
|
|
45274
|
+
}
|
|
45275
|
+
throw new TranslateError(`stream read error: ${msg}`, "network", {
|
|
45276
|
+
code: "network",
|
|
45277
|
+
elapsed_ms: elapsed()
|
|
45278
|
+
});
|
|
45279
|
+
}
|
|
45280
|
+
if (inlineError) {
|
|
45281
|
+
throw new TranslateError(`API error: ${inlineError}`, "api", {
|
|
45282
|
+
code: "api",
|
|
45283
|
+
elapsed_ms: elapsed()
|
|
45284
|
+
});
|
|
45285
|
+
}
|
|
45286
|
+
if (content.length === 0) {
|
|
45287
|
+
throw new TranslateError("empty completion content", "parse", {
|
|
45288
|
+
code: "parse",
|
|
45289
|
+
elapsed_ms: elapsed()
|
|
45290
|
+
});
|
|
45291
|
+
}
|
|
45292
|
+
return { content, elapsed_ms: elapsed(), usage };
|
|
45293
|
+
}
|
|
45294
|
+
function isAbortLike(msg) {
|
|
45295
|
+
const m = msg.toLowerCase();
|
|
45296
|
+
return m.includes("abort");
|
|
45297
|
+
}
|
|
45298
|
+
function parseConcernHeader(content) {
|
|
45299
|
+
const newlineIdx = content.indexOf(`
|
|
45300
|
+
`);
|
|
45301
|
+
if (newlineIdx === -1) {
|
|
45302
|
+
return { concern: null, translation: content };
|
|
45303
|
+
}
|
|
45304
|
+
const firstLine = content.slice(0, newlineIdx).trim();
|
|
45305
|
+
let concern;
|
|
45306
|
+
if (firstLine === "OK") {
|
|
45307
|
+
concern = null;
|
|
45308
|
+
} else if (firstLine.startsWith("WARN:")) {
|
|
45309
|
+
concern = firstLine.slice("WARN:".length).trim();
|
|
45310
|
+
if (!concern)
|
|
45311
|
+
concern = null;
|
|
45312
|
+
} else {
|
|
45313
|
+
return { concern: null, translation: content };
|
|
45314
|
+
}
|
|
45315
|
+
const rest2 = content.slice(newlineIdx + 1).replace(/^\s+/, "");
|
|
45316
|
+
return { concern, translation: rest2 };
|
|
45317
|
+
}
|
|
45058
45318
|
function parseUsage(raw) {
|
|
45059
45319
|
if (!raw || typeof raw !== "object")
|
|
45060
45320
|
return null;
|
|
@@ -45072,8 +45332,8 @@ function parseUsage(raw) {
|
|
|
45072
45332
|
}
|
|
45073
45333
|
|
|
45074
45334
|
// src/state.ts
|
|
45075
|
-
import { existsSync as existsSync3,
|
|
45076
|
-
import {
|
|
45335
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
45336
|
+
import { join as join3 } from "node:path";
|
|
45077
45337
|
function stateFilePath(projectDir) {
|
|
45078
45338
|
return join3(projectDir, ".claude", "cache", "cc-zh-watcher-state.json");
|
|
45079
45339
|
}
|
|
@@ -45093,10 +45353,8 @@ function readState(projectDir) {
|
|
|
45093
45353
|
}
|
|
45094
45354
|
}
|
|
45095
45355
|
function writeState(projectDir, state) {
|
|
45096
|
-
|
|
45097
|
-
|
|
45098
|
-
writeFileSync(path, JSON.stringify(state, null, 2) + `
|
|
45099
|
-
`, "utf-8");
|
|
45356
|
+
writeAtomic(stateFilePath(projectDir), JSON.stringify(state, null, 2) + `
|
|
45357
|
+
`);
|
|
45100
45358
|
}
|
|
45101
45359
|
|
|
45102
45360
|
// src/transcript.ts
|
|
@@ -45131,7 +45389,8 @@ function listSessions(projectDir) {
|
|
|
45131
45389
|
last_activity_at: null,
|
|
45132
45390
|
turn_count: 0,
|
|
45133
45391
|
first_message: null,
|
|
45134
|
-
mtime_ms: stat.mtimeMs
|
|
45392
|
+
mtime_ms: stat.mtimeMs,
|
|
45393
|
+
cwd: null
|
|
45135
45394
|
});
|
|
45136
45395
|
}
|
|
45137
45396
|
}
|
|
@@ -45151,6 +45410,7 @@ function parseSessionMeta(path) {
|
|
|
45151
45410
|
let last_activity_at = null;
|
|
45152
45411
|
let turn_count = 0;
|
|
45153
45412
|
let first_message = null;
|
|
45413
|
+
let cwd2 = null;
|
|
45154
45414
|
for (const line of lines) {
|
|
45155
45415
|
if (!line.trim())
|
|
45156
45416
|
continue;
|
|
@@ -45166,6 +45426,9 @@ function parseSessionMeta(path) {
|
|
|
45166
45426
|
started_at = ts;
|
|
45167
45427
|
last_activity_at = ts;
|
|
45168
45428
|
}
|
|
45429
|
+
if (cwd2 === null && typeof obj["cwd"] === "string") {
|
|
45430
|
+
cwd2 = obj["cwd"];
|
|
45431
|
+
}
|
|
45169
45432
|
if (obj["type"] === "user") {
|
|
45170
45433
|
const message = obj["message"];
|
|
45171
45434
|
const content = message?.content;
|
|
@@ -45183,7 +45446,8 @@ function parseSessionMeta(path) {
|
|
|
45183
45446
|
last_activity_at,
|
|
45184
45447
|
turn_count,
|
|
45185
45448
|
first_message,
|
|
45186
|
-
mtime_ms: stat.mtimeMs
|
|
45449
|
+
mtime_ms: stat.mtimeMs,
|
|
45450
|
+
cwd: cwd2
|
|
45187
45451
|
};
|
|
45188
45452
|
}
|
|
45189
45453
|
function extractRealPromptText(content) {
|
|
@@ -45248,8 +45512,7 @@ import {
|
|
|
45248
45512
|
existsSync as existsSync5,
|
|
45249
45513
|
mkdirSync as mkdirSync2,
|
|
45250
45514
|
readFileSync as readFileSync5,
|
|
45251
|
-
unlinkSync
|
|
45252
|
-
writeFileSync as writeFileSync2
|
|
45515
|
+
unlinkSync as unlinkSync2
|
|
45253
45516
|
} from "node:fs";
|
|
45254
45517
|
import { dirname as dirname2, join as join5 } from "node:path";
|
|
45255
45518
|
var USER_INPUT_FILENAME = "user-input.md";
|
|
@@ -45274,10 +45537,10 @@ function slashCommandPath(projectDir) {
|
|
|
45274
45537
|
}
|
|
45275
45538
|
function writeUserInput(projectDir, englishText) {
|
|
45276
45539
|
const path = userInputPath(projectDir);
|
|
45277
|
-
|
|
45278
|
-
writeFileSync2(path, englishText.endsWith(`
|
|
45540
|
+
const body = englishText.endsWith(`
|
|
45279
45541
|
`) ? englishText : englishText + `
|
|
45280
|
-
|
|
45542
|
+
`;
|
|
45543
|
+
writeAtomic(path, body);
|
|
45281
45544
|
return path;
|
|
45282
45545
|
}
|
|
45283
45546
|
function appendHistory(projectDir, entry) {
|
|
@@ -45289,8 +45552,7 @@ function appendHistory(projectDir, entry) {
|
|
|
45289
45552
|
function installSlashCommand(projectDir) {
|
|
45290
45553
|
const target = slashCommandPath(projectDir);
|
|
45291
45554
|
const existed = existsSync5(target);
|
|
45292
|
-
|
|
45293
|
-
writeFileSync2(target, SLASH_COMMAND_TEMPLATE, "utf-8");
|
|
45555
|
+
writeAtomic(target, SLASH_COMMAND_TEMPLATE);
|
|
45294
45556
|
return { installedPath: target, existed };
|
|
45295
45557
|
}
|
|
45296
45558
|
function uninstallSlashCommand(projectDir, opts) {
|
|
@@ -45298,7 +45560,7 @@ function uninstallSlashCommand(projectDir, opts) {
|
|
|
45298
45560
|
const skipped = [];
|
|
45299
45561
|
const sc = slashCommandPath(projectDir);
|
|
45300
45562
|
if (existsSync5(sc)) {
|
|
45301
|
-
|
|
45563
|
+
unlinkSync2(sc);
|
|
45302
45564
|
removed.push(sc);
|
|
45303
45565
|
} else {
|
|
45304
45566
|
skipped.push(sc);
|
|
@@ -45306,7 +45568,7 @@ function uninstallSlashCommand(projectDir, opts) {
|
|
|
45306
45568
|
if (opts?.wipeCache) {
|
|
45307
45569
|
for (const p of [userInputPath(projectDir), historyPath(projectDir)]) {
|
|
45308
45570
|
if (existsSync5(p)) {
|
|
45309
|
-
|
|
45571
|
+
unlinkSync2(p);
|
|
45310
45572
|
removed.push(p);
|
|
45311
45573
|
} else {
|
|
45312
45574
|
skipped.push(p);
|
|
@@ -45317,9 +45579,8 @@ function uninstallSlashCommand(projectDir, opts) {
|
|
|
45317
45579
|
}
|
|
45318
45580
|
|
|
45319
45581
|
// src/app.tsx
|
|
45320
|
-
var
|
|
45321
|
-
import { existsSync as existsSync7,
|
|
45322
|
-
import { dirname as dirname3 } from "node:path";
|
|
45582
|
+
var import_react30 = __toESM(require_react(), 1);
|
|
45583
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
45323
45584
|
|
|
45324
45585
|
// src/tail.ts
|
|
45325
45586
|
import {
|
|
@@ -45419,54 +45680,337 @@ class TranscriptTail {
|
|
|
45419
45680
|
for (const line of lines) {
|
|
45420
45681
|
if (!line.trim())
|
|
45421
45682
|
continue;
|
|
45422
|
-
|
|
45423
|
-
if (ev)
|
|
45424
|
-
events.push(ev);
|
|
45683
|
+
events.push(...parseAssistantContent(line));
|
|
45425
45684
|
}
|
|
45426
45685
|
for (const ev of events.slice(-n)) {
|
|
45427
|
-
this.handlers.
|
|
45686
|
+
this.handlers.onContent(ev);
|
|
45428
45687
|
}
|
|
45429
45688
|
} catch (err) {
|
|
45430
45689
|
this.handlers.onError?.(err);
|
|
45431
45690
|
}
|
|
45432
45691
|
}
|
|
45433
45692
|
tryEmit(line) {
|
|
45434
|
-
const ev
|
|
45435
|
-
|
|
45436
|
-
|
|
45693
|
+
for (const ev of parseAssistantContent(line)) {
|
|
45694
|
+
this.handlers.onContent(ev);
|
|
45695
|
+
}
|
|
45437
45696
|
}
|
|
45438
45697
|
}
|
|
45439
|
-
function
|
|
45698
|
+
function parseAssistantContent(line) {
|
|
45440
45699
|
let obj;
|
|
45441
45700
|
try {
|
|
45442
45701
|
obj = JSON.parse(line);
|
|
45443
45702
|
} catch {
|
|
45444
|
-
return
|
|
45703
|
+
return [];
|
|
45704
|
+
}
|
|
45705
|
+
if (obj["type"] === "user") {
|
|
45706
|
+
return parseUserToolResults(obj);
|
|
45445
45707
|
}
|
|
45446
45708
|
if (obj["type"] !== "assistant")
|
|
45447
|
-
return
|
|
45709
|
+
return [];
|
|
45448
45710
|
const message = obj["message"];
|
|
45449
45711
|
const content = message?.content;
|
|
45450
45712
|
if (!Array.isArray(content))
|
|
45451
|
-
return
|
|
45713
|
+
return [];
|
|
45452
45714
|
const textParts = [];
|
|
45715
|
+
const thinkingParts = [];
|
|
45716
|
+
const questions = [];
|
|
45717
|
+
const todos = [];
|
|
45718
|
+
const agents = [];
|
|
45719
|
+
const rawTools = [];
|
|
45453
45720
|
for (const entry of content) {
|
|
45454
|
-
if (entry
|
|
45455
|
-
|
|
45721
|
+
if (!entry || typeof entry !== "object")
|
|
45722
|
+
continue;
|
|
45723
|
+
const e = entry;
|
|
45724
|
+
if (e["type"] === "text" && typeof e["text"] === "string" && e["text"].length > 0) {
|
|
45725
|
+
textParts.push(e["text"]);
|
|
45726
|
+
} else if (e["type"] === "thinking" && typeof e["thinking"] === "string" && e["thinking"].length > 0) {
|
|
45727
|
+
thinkingParts.push(e["thinking"]);
|
|
45728
|
+
} else if (e["type"] === "tool_use") {
|
|
45729
|
+
const toolId = typeof e["id"] === "string" ? e["id"] : null;
|
|
45730
|
+
const name = e["name"];
|
|
45731
|
+
if (!toolId || typeof name !== "string")
|
|
45732
|
+
continue;
|
|
45733
|
+
if (name === "AskUserQuestion") {
|
|
45734
|
+
const payload = parseQuestionInput(e["input"]);
|
|
45735
|
+
if (payload)
|
|
45736
|
+
questions.push({ id: toolId, payload });
|
|
45737
|
+
} else if (name === "TodoWrite") {
|
|
45738
|
+
const payload = parseTodoInput(e["input"]);
|
|
45739
|
+
if (payload)
|
|
45740
|
+
todos.push({ id: toolId, payload });
|
|
45741
|
+
} else if (name === "Agent" || name === "Task") {
|
|
45742
|
+
const payload = parseAgentInput(e["input"]);
|
|
45743
|
+
if (payload)
|
|
45744
|
+
agents.push({ id: toolId, payload });
|
|
45745
|
+
} else {
|
|
45746
|
+
rawTools.push({
|
|
45747
|
+
id: toolId,
|
|
45748
|
+
payload: { name, input: e["input"] }
|
|
45749
|
+
});
|
|
45750
|
+
}
|
|
45456
45751
|
}
|
|
45457
45752
|
}
|
|
45458
|
-
if (textParts.length === 0)
|
|
45459
|
-
return
|
|
45753
|
+
if (textParts.length === 0 && thinkingParts.length === 0 && questions.length === 0 && todos.length === 0 && agents.length === 0 && rawTools.length === 0) {
|
|
45754
|
+
return [];
|
|
45755
|
+
}
|
|
45460
45756
|
const ts = typeof obj["timestamp"] === "string" ? obj["timestamp"] : new Date().toISOString();
|
|
45461
45757
|
const uuid = typeof obj["uuid"] === "string" ? obj["uuid"] : null;
|
|
45462
|
-
const
|
|
45463
|
-
|
|
45464
|
-
|
|
45465
|
-
text: textParts.join(`
|
|
45758
|
+
const out = [];
|
|
45759
|
+
if (textParts.length > 0) {
|
|
45760
|
+
const text = textParts.join(`
|
|
45466
45761
|
|
|
45467
|
-
`)
|
|
45468
|
-
|
|
45469
|
-
|
|
45762
|
+
`);
|
|
45763
|
+
out.push({
|
|
45764
|
+
kind: "text",
|
|
45765
|
+
timestamp: ts,
|
|
45766
|
+
text,
|
|
45767
|
+
id: uuid ?? `${ts}-${textParts[0].slice(0, 16)}`
|
|
45768
|
+
});
|
|
45769
|
+
}
|
|
45770
|
+
if (thinkingParts.length > 0) {
|
|
45771
|
+
const text = thinkingParts.join(`
|
|
45772
|
+
|
|
45773
|
+
`);
|
|
45774
|
+
out.push({
|
|
45775
|
+
kind: "thinking",
|
|
45776
|
+
timestamp: ts,
|
|
45777
|
+
text,
|
|
45778
|
+
id: uuid ? `${uuid}#thinking` : `${ts}-thinking-${thinkingParts[0].slice(0, 16)}`
|
|
45779
|
+
});
|
|
45780
|
+
}
|
|
45781
|
+
for (const q of questions) {
|
|
45782
|
+
out.push({
|
|
45783
|
+
kind: "question",
|
|
45784
|
+
timestamp: ts,
|
|
45785
|
+
id: q.id,
|
|
45786
|
+
question: q.payload
|
|
45787
|
+
});
|
|
45788
|
+
}
|
|
45789
|
+
for (const t of todos) {
|
|
45790
|
+
out.push({
|
|
45791
|
+
kind: "todo",
|
|
45792
|
+
timestamp: ts,
|
|
45793
|
+
id: t.id,
|
|
45794
|
+
todo: t.payload
|
|
45795
|
+
});
|
|
45796
|
+
}
|
|
45797
|
+
for (const a of agents) {
|
|
45798
|
+
out.push({
|
|
45799
|
+
kind: "agent",
|
|
45800
|
+
timestamp: ts,
|
|
45801
|
+
id: a.id,
|
|
45802
|
+
agent: a.payload
|
|
45803
|
+
});
|
|
45804
|
+
}
|
|
45805
|
+
for (const t of rawTools) {
|
|
45806
|
+
out.push({
|
|
45807
|
+
kind: "tool_use",
|
|
45808
|
+
timestamp: ts,
|
|
45809
|
+
id: t.id,
|
|
45810
|
+
toolUse: t.payload
|
|
45811
|
+
});
|
|
45812
|
+
}
|
|
45813
|
+
return out;
|
|
45814
|
+
}
|
|
45815
|
+
function parseUserToolResults(obj) {
|
|
45816
|
+
const message = obj["message"];
|
|
45817
|
+
const content = message?.content;
|
|
45818
|
+
if (!Array.isArray(content))
|
|
45819
|
+
return [];
|
|
45820
|
+
const ts = typeof obj["timestamp"] === "string" ? obj["timestamp"] : new Date().toISOString();
|
|
45821
|
+
const out = [];
|
|
45822
|
+
for (const entry of content) {
|
|
45823
|
+
if (!entry || typeof entry !== "object")
|
|
45824
|
+
continue;
|
|
45825
|
+
const e = entry;
|
|
45826
|
+
if (e["type"] !== "tool_result")
|
|
45827
|
+
continue;
|
|
45828
|
+
const toolUseId = typeof e["tool_use_id"] === "string" ? e["tool_use_id"] : null;
|
|
45829
|
+
if (!toolUseId)
|
|
45830
|
+
continue;
|
|
45831
|
+
out.push({
|
|
45832
|
+
kind: "tool_result",
|
|
45833
|
+
timestamp: ts,
|
|
45834
|
+
id: `${toolUseId}#result`,
|
|
45835
|
+
toolResult: {
|
|
45836
|
+
tool_use_id: toolUseId,
|
|
45837
|
+
content: e["content"],
|
|
45838
|
+
isError: e["is_error"] === true
|
|
45839
|
+
}
|
|
45840
|
+
});
|
|
45841
|
+
}
|
|
45842
|
+
return out;
|
|
45843
|
+
}
|
|
45844
|
+
function parseQuestionInput(raw) {
|
|
45845
|
+
if (!raw || typeof raw !== "object")
|
|
45846
|
+
return null;
|
|
45847
|
+
const r = raw;
|
|
45848
|
+
const rawQs = r["questions"];
|
|
45849
|
+
if (!Array.isArray(rawQs) || rawQs.length === 0)
|
|
45850
|
+
return null;
|
|
45851
|
+
const questions = [];
|
|
45852
|
+
for (const rq of rawQs) {
|
|
45853
|
+
if (!rq || typeof rq !== "object")
|
|
45854
|
+
continue;
|
|
45855
|
+
const q = rq;
|
|
45856
|
+
const question = typeof q["question"] === "string" ? q["question"] : "";
|
|
45857
|
+
const header = typeof q["header"] === "string" ? q["header"] : "";
|
|
45858
|
+
const multiSelect = q["multiSelect"] === true;
|
|
45859
|
+
const rawOpts = q["options"];
|
|
45860
|
+
if (!Array.isArray(rawOpts) || rawOpts.length === 0)
|
|
45861
|
+
continue;
|
|
45862
|
+
const options = [];
|
|
45863
|
+
for (const ro of rawOpts) {
|
|
45864
|
+
if (!ro || typeof ro !== "object")
|
|
45865
|
+
continue;
|
|
45866
|
+
const o = ro;
|
|
45867
|
+
const label = typeof o["label"] === "string" ? o["label"] : "";
|
|
45868
|
+
const description = typeof o["description"] === "string" ? o["description"] : "";
|
|
45869
|
+
const preview = typeof o["preview"] === "string" ? o["preview"] : undefined;
|
|
45870
|
+
const opt = { label, description };
|
|
45871
|
+
if (preview !== undefined)
|
|
45872
|
+
opt.preview = preview;
|
|
45873
|
+
options.push(opt);
|
|
45874
|
+
}
|
|
45875
|
+
if (options.length === 0)
|
|
45876
|
+
continue;
|
|
45877
|
+
questions.push({ question, header, multiSelect, options });
|
|
45878
|
+
}
|
|
45879
|
+
if (questions.length === 0)
|
|
45880
|
+
return null;
|
|
45881
|
+
return { questions };
|
|
45882
|
+
}
|
|
45883
|
+
function parseTodoInput(raw) {
|
|
45884
|
+
if (!raw || typeof raw !== "object")
|
|
45885
|
+
return null;
|
|
45886
|
+
const r = raw;
|
|
45887
|
+
const rawTodos = r["todos"];
|
|
45888
|
+
if (!Array.isArray(rawTodos) || rawTodos.length === 0)
|
|
45889
|
+
return null;
|
|
45890
|
+
const todos = [];
|
|
45891
|
+
for (const rt of rawTodos) {
|
|
45892
|
+
if (!rt || typeof rt !== "object")
|
|
45893
|
+
continue;
|
|
45894
|
+
const t = rt;
|
|
45895
|
+
const content = typeof t["content"] === "string" ? t["content"] : "";
|
|
45896
|
+
const activeForm = typeof t["activeForm"] === "string" ? t["activeForm"] : "";
|
|
45897
|
+
const rawStatus = t["status"];
|
|
45898
|
+
const status = rawStatus === "in_progress" || rawStatus === "completed" ? rawStatus : "pending";
|
|
45899
|
+
if (!content && !activeForm)
|
|
45900
|
+
continue;
|
|
45901
|
+
todos.push({ content, activeForm, status });
|
|
45902
|
+
}
|
|
45903
|
+
if (todos.length === 0)
|
|
45904
|
+
return null;
|
|
45905
|
+
return { todos };
|
|
45906
|
+
}
|
|
45907
|
+
function parseAgentInput(raw) {
|
|
45908
|
+
if (!raw || typeof raw !== "object")
|
|
45909
|
+
return null;
|
|
45910
|
+
const r = raw;
|
|
45911
|
+
const description = typeof r["description"] === "string" ? r["description"] : "";
|
|
45912
|
+
const prompt = typeof r["prompt"] === "string" ? r["prompt"] : "";
|
|
45913
|
+
const subagent_type = typeof r["subagent_type"] === "string" ? r["subagent_type"] : "";
|
|
45914
|
+
if (!description && !prompt)
|
|
45915
|
+
return null;
|
|
45916
|
+
return { description, prompt, subagent_type };
|
|
45917
|
+
}
|
|
45918
|
+
|
|
45919
|
+
// src/notify.ts
|
|
45920
|
+
var BEL2 = "\x07";
|
|
45921
|
+
function ringBell() {
|
|
45922
|
+
try {
|
|
45923
|
+
process.stderr.write(BEL2);
|
|
45924
|
+
} catch {}
|
|
45925
|
+
}
|
|
45926
|
+
|
|
45927
|
+
class DebouncedBell {
|
|
45928
|
+
delayMs;
|
|
45929
|
+
playFn;
|
|
45930
|
+
timer = null;
|
|
45931
|
+
constructor(delayMs, playFn = ringBell) {
|
|
45932
|
+
this.delayMs = delayMs;
|
|
45933
|
+
this.playFn = playFn;
|
|
45934
|
+
}
|
|
45935
|
+
schedule() {
|
|
45936
|
+
if (this.timer)
|
|
45937
|
+
clearTimeout(this.timer);
|
|
45938
|
+
this.timer = setTimeout(() => {
|
|
45939
|
+
this.timer = null;
|
|
45940
|
+
try {
|
|
45941
|
+
this.playFn();
|
|
45942
|
+
} catch {}
|
|
45943
|
+
}, this.delayMs);
|
|
45944
|
+
}
|
|
45945
|
+
cancel() {
|
|
45946
|
+
if (this.timer) {
|
|
45947
|
+
clearTimeout(this.timer);
|
|
45948
|
+
this.timer = null;
|
|
45949
|
+
}
|
|
45950
|
+
}
|
|
45951
|
+
get pending() {
|
|
45952
|
+
return this.timer !== null;
|
|
45953
|
+
}
|
|
45954
|
+
}
|
|
45955
|
+
|
|
45956
|
+
// src/queue.ts
|
|
45957
|
+
class SerialQueue {
|
|
45958
|
+
chain = Promise.resolve();
|
|
45959
|
+
pendingCount = 0;
|
|
45960
|
+
enqueue(task) {
|
|
45961
|
+
this.pendingCount += 1;
|
|
45962
|
+
const wrapped = async () => {
|
|
45963
|
+
try {
|
|
45964
|
+
return await task();
|
|
45965
|
+
} finally {
|
|
45966
|
+
this.pendingCount -= 1;
|
|
45967
|
+
}
|
|
45968
|
+
};
|
|
45969
|
+
const next = this.chain.then(wrapped, wrapped);
|
|
45970
|
+
this.chain = next.catch(() => {
|
|
45971
|
+
return;
|
|
45972
|
+
});
|
|
45973
|
+
return next;
|
|
45974
|
+
}
|
|
45975
|
+
get pending() {
|
|
45976
|
+
return this.pendingCount;
|
|
45977
|
+
}
|
|
45978
|
+
}
|
|
45979
|
+
|
|
45980
|
+
// src/lang-detect.ts
|
|
45981
|
+
function cjkRatio(s) {
|
|
45982
|
+
let cjk = 0;
|
|
45983
|
+
let counted = 0;
|
|
45984
|
+
for (const ch of s) {
|
|
45985
|
+
const cp = ch.codePointAt(0);
|
|
45986
|
+
if (cp === undefined)
|
|
45987
|
+
continue;
|
|
45988
|
+
if (cp <= 32 || cp === 127)
|
|
45989
|
+
continue;
|
|
45990
|
+
if (cp >= 33 && cp <= 47)
|
|
45991
|
+
continue;
|
|
45992
|
+
if (cp >= 58 && cp <= 64)
|
|
45993
|
+
continue;
|
|
45994
|
+
if (cp >= 91 && cp <= 96)
|
|
45995
|
+
continue;
|
|
45996
|
+
if (cp >= 123 && cp <= 126)
|
|
45997
|
+
continue;
|
|
45998
|
+
counted += 1;
|
|
45999
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 13312 && cp <= 19903 || cp >= 131072 && cp <= 173791 || cp >= 12288 && cp <= 12351 || cp >= 65280 && cp <= 65519) {
|
|
46000
|
+
cjk += 1;
|
|
46001
|
+
}
|
|
46002
|
+
}
|
|
46003
|
+
return counted === 0 ? 0 : cjk / counted;
|
|
46004
|
+
}
|
|
46005
|
+
function isProbablyChinese(s, threshold = 0.3) {
|
|
46006
|
+
return cjkRatio(s) >= threshold;
|
|
46007
|
+
}
|
|
46008
|
+
|
|
46009
|
+
// src/translatable.ts
|
|
46010
|
+
function seedString(en) {
|
|
46011
|
+
if (!en.trim() || isProbablyChinese(en))
|
|
46012
|
+
return { en, zh: en };
|
|
46013
|
+
return { en, zh: "" };
|
|
45470
46014
|
}
|
|
45471
46015
|
|
|
45472
46016
|
// src/components/status-bar.tsx
|
|
@@ -45491,7 +46035,7 @@ function ContextBadge({
|
|
|
45491
46035
|
bold: true
|
|
45492
46036
|
}, "off"), /* @__PURE__ */ import_react22.default.createElement(Text, {
|
|
45493
46037
|
dimColor: true
|
|
45494
|
-
}, " (Ctrl+
|
|
46038
|
+
}, " (Ctrl+E → toggle)"));
|
|
45495
46039
|
}
|
|
45496
46040
|
if (size2 === 0) {
|
|
45497
46041
|
return /* @__PURE__ */ import_react22.default.createElement(Text, null, "ctx:", " ", /* @__PURE__ */ import_react22.default.createElement(Text, {
|
|
@@ -45506,7 +46050,7 @@ function ContextBadge({
|
|
|
45506
46050
|
bold: true
|
|
45507
46051
|
}, "on"), /* @__PURE__ */ import_react22.default.createElement(Text, {
|
|
45508
46052
|
dimColor: true
|
|
45509
|
-
}, " (", size2, " chars
|
|
46053
|
+
}, " (", size2, " chars)"));
|
|
45510
46054
|
}
|
|
45511
46055
|
function formatElapsed(ms) {
|
|
45512
46056
|
if (ms < 1000)
|
|
@@ -45726,7 +46270,7 @@ function InputPane({
|
|
|
45726
46270
|
flexDirection: "column"
|
|
45727
46271
|
}, /* @__PURE__ */ import_react24.default.createElement(Text, {
|
|
45728
46272
|
dimColor: true
|
|
45729
|
-
}, "中文输入(Enter 翻译 · Ctrl+
|
|
46273
|
+
}, "中文输入(Enter 翻译 · Ctrl+E 设置 · Esc 清空 · Ctrl+C 退出)"), /* @__PURE__ */ import_react24.default.createElement(Box_default, null, /* @__PURE__ */ import_react24.default.createElement(Text, {
|
|
45730
46274
|
color: "green",
|
|
45731
46275
|
bold: true
|
|
45732
46276
|
}, ">", " "), disabled ? /* @__PURE__ */ import_react24.default.createElement(Text, {
|
|
@@ -45741,7 +46285,13 @@ function InputPane({
|
|
|
45741
46285
|
|
|
45742
46286
|
// src/components/preview-pane.tsx
|
|
45743
46287
|
var import_react25 = __toESM(require_react(), 1);
|
|
45744
|
-
function PreviewPane({
|
|
46288
|
+
function PreviewPane({
|
|
46289
|
+
text,
|
|
46290
|
+
translating,
|
|
46291
|
+
streamingText,
|
|
46292
|
+
error,
|
|
46293
|
+
concern
|
|
46294
|
+
}) {
|
|
45745
46295
|
let body;
|
|
45746
46296
|
let borderColor;
|
|
45747
46297
|
if (error) {
|
|
@@ -45756,8 +46306,36 @@ function PreviewPane({ text, translating, error }) {
|
|
|
45756
46306
|
dimColor: true
|
|
45757
46307
|
}, "Press Esc to clear and try again."));
|
|
45758
46308
|
borderColor = "red";
|
|
46309
|
+
} else if (concern) {
|
|
46310
|
+
body = /* @__PURE__ */ import_react25.default.createElement(Box_default, {
|
|
46311
|
+
flexDirection: "column"
|
|
46312
|
+
}, /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46313
|
+
color: "yellow",
|
|
46314
|
+
bold: true
|
|
46315
|
+
}, "⚠ 上下文检查发现可能的问题"), /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46316
|
+
color: "yellow"
|
|
46317
|
+
}, concern), /* @__PURE__ */ import_react25.default.createElement(Box_default, {
|
|
46318
|
+
marginTop: 1
|
|
46319
|
+
}, /* @__PURE__ */ import_react25.default.createElement(Text, null, /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46320
|
+
color: "green",
|
|
46321
|
+
bold: true
|
|
46322
|
+
}, "↳ 翻译已就绪:"), " ", text)), /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46323
|
+
dimColor: true
|
|
46324
|
+
}, "Enter 再按一次确认发送 · Esc 取消 · 或直接改输入重译"));
|
|
46325
|
+
borderColor = "yellow";
|
|
45759
46326
|
} else if (translating) {
|
|
45760
|
-
|
|
46327
|
+
const partial2 = streamingText ?? "";
|
|
46328
|
+
body = partial2 ? /* @__PURE__ */ import_react25.default.createElement(Box_default, {
|
|
46329
|
+
flexDirection: "column"
|
|
46330
|
+
}, /* @__PURE__ */ import_react25.default.createElement(Text, null, /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46331
|
+
color: "yellow",
|
|
46332
|
+
bold: true
|
|
46333
|
+
}, "⏳ [zh→en] streaming:", " "), partial2, /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46334
|
+
color: "yellow",
|
|
46335
|
+
bold: true
|
|
46336
|
+
}, "▎")), /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
46337
|
+
dimColor: true
|
|
46338
|
+
}, "翻译完成后会写入 user-input.md,再切到 Claude Code 输 /i ↵")) : /* @__PURE__ */ import_react25.default.createElement(Text, {
|
|
45761
46339
|
color: "yellow"
|
|
45762
46340
|
}, "⏳ translating…");
|
|
45763
46341
|
borderColor = "yellow";
|
|
@@ -45791,44 +46369,532 @@ function PreviewPane({ text, translating, error }) {
|
|
|
45791
46369
|
|
|
45792
46370
|
// src/components/output-pane.tsx
|
|
45793
46371
|
var import_react26 = __toESM(require_react(), 1);
|
|
45794
|
-
|
|
45795
|
-
|
|
45796
|
-
|
|
45797
|
-
|
|
46372
|
+
|
|
46373
|
+
// src/tool-display.ts
|
|
46374
|
+
function summarizeToolInput(name, input) {
|
|
46375
|
+
const i = input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
46376
|
+
switch (name) {
|
|
46377
|
+
case "Bash":
|
|
46378
|
+
case "PowerShell":
|
|
46379
|
+
return str(i["command"]);
|
|
46380
|
+
case "Read":
|
|
46381
|
+
return joinNonEmpty(str(i["file_path"]), num(i["limit"]) !== null ? `limit=${num(i["limit"])}` : "", num(i["offset"]) !== null ? `offset=${num(i["offset"])}` : "");
|
|
46382
|
+
case "Edit":
|
|
46383
|
+
case "Write":
|
|
46384
|
+
case "NotebookEdit":
|
|
46385
|
+
return str(i["file_path"]) || str(i["notebook_path"]);
|
|
46386
|
+
case "Glob":
|
|
46387
|
+
return joinNonEmpty(str(i["pattern"]), str(i["path"]) ? `in ${str(i["path"])}` : "");
|
|
46388
|
+
case "Grep":
|
|
46389
|
+
return joinNonEmpty(JSON.stringify(str(i["pattern"])), str(i["path"]) ? `in ${str(i["path"])}` : "");
|
|
46390
|
+
case "WebFetch":
|
|
46391
|
+
return str(i["url"]);
|
|
46392
|
+
case "WebSearch":
|
|
46393
|
+
return JSON.stringify(str(i["query"]));
|
|
46394
|
+
case "ExitWorktree":
|
|
46395
|
+
return str(i["action"]);
|
|
46396
|
+
case "ScheduleWakeup":
|
|
46397
|
+
return joinNonEmpty(num(i["delaySeconds"]) !== null ? `${num(i["delaySeconds"])}s` : "", str(i["reason"]));
|
|
46398
|
+
case "Monitor":
|
|
46399
|
+
return str(i["command"]) || str(i["description"]);
|
|
46400
|
+
case "ToolSearch":
|
|
46401
|
+
return JSON.stringify(str(i["query"]));
|
|
46402
|
+
case "Skill":
|
|
46403
|
+
return joinNonEmpty(str(i["skill"]), str(i["args"]) ? `args=${str(i["args"])}` : "");
|
|
46404
|
+
case "TaskCreate":
|
|
46405
|
+
return str(i["subject"]) || str(i["description"]);
|
|
46406
|
+
case "TaskUpdate":
|
|
46407
|
+
return joinNonEmpty(str(i["taskId"]) ? `id=${str(i["taskId"])}` : "", str(i["status"]), str(i["description"]));
|
|
46408
|
+
case "TaskStop":
|
|
46409
|
+
case "TaskOutput":
|
|
46410
|
+
return str(i["task_id"]);
|
|
46411
|
+
case "TaskList":
|
|
46412
|
+
return "";
|
|
46413
|
+
default:
|
|
46414
|
+
return genericSummary(i);
|
|
46415
|
+
}
|
|
45798
46416
|
}
|
|
45799
|
-
|
|
45800
|
-
|
|
45801
|
-
|
|
45802
|
-
|
|
45803
|
-
|
|
45804
|
-
|
|
45805
|
-
|
|
45806
|
-
|
|
45807
|
-
|
|
45808
|
-
|
|
45809
|
-
|
|
46417
|
+
var LINE_LIMIT = 3;
|
|
46418
|
+
function formatToolResult(content) {
|
|
46419
|
+
const text = stringifyResult(content);
|
|
46420
|
+
const all = text.split(`
|
|
46421
|
+
`);
|
|
46422
|
+
while (all.length > 0 && all[all.length - 1].trim() === "")
|
|
46423
|
+
all.pop();
|
|
46424
|
+
if (all.length <= LINE_LIMIT) {
|
|
46425
|
+
return { lines: all, truncated: 0 };
|
|
46426
|
+
}
|
|
46427
|
+
return {
|
|
46428
|
+
lines: all.slice(0, LINE_LIMIT),
|
|
46429
|
+
truncated: all.length - LINE_LIMIT
|
|
46430
|
+
};
|
|
45810
46431
|
}
|
|
45811
|
-
function
|
|
45812
|
-
return
|
|
46432
|
+
function str(v) {
|
|
46433
|
+
return typeof v === "string" ? v : "";
|
|
46434
|
+
}
|
|
46435
|
+
function num(v) {
|
|
46436
|
+
return typeof v === "number" ? v : null;
|
|
46437
|
+
}
|
|
46438
|
+
function joinNonEmpty(...parts) {
|
|
46439
|
+
return parts.filter((p) => p && p.length > 0).join(", ");
|
|
46440
|
+
}
|
|
46441
|
+
var GENERIC_KEY_LIMIT = 3;
|
|
46442
|
+
var GENERIC_VALUE_LIMIT = 40;
|
|
46443
|
+
function genericSummary(input) {
|
|
46444
|
+
const keys2 = Object.keys(input).slice(0, GENERIC_KEY_LIMIT);
|
|
46445
|
+
return keys2.map((k) => {
|
|
46446
|
+
const v = input[k];
|
|
46447
|
+
const sv = typeof v === "string" ? v : JSON.stringify(v);
|
|
46448
|
+
const trimmed = sv.length > GENERIC_VALUE_LIMIT ? sv.slice(0, GENERIC_VALUE_LIMIT - 1) + "…" : sv;
|
|
46449
|
+
return `${k}=${trimmed}`;
|
|
46450
|
+
}).join(", ");
|
|
46451
|
+
}
|
|
46452
|
+
function stringifyResult(content) {
|
|
46453
|
+
if (typeof content === "string")
|
|
46454
|
+
return content;
|
|
46455
|
+
if (Array.isArray(content)) {
|
|
46456
|
+
const parts = [];
|
|
46457
|
+
for (const c of content) {
|
|
46458
|
+
if (!c || typeof c !== "object")
|
|
46459
|
+
continue;
|
|
46460
|
+
const block = c;
|
|
46461
|
+
if (block["type"] === "text" && typeof block["text"] === "string") {
|
|
46462
|
+
parts.push(block["text"]);
|
|
46463
|
+
} else if (block["type"] === "image") {
|
|
46464
|
+
parts.push("[image]");
|
|
46465
|
+
} else {
|
|
46466
|
+
parts.push(JSON.stringify(block));
|
|
46467
|
+
}
|
|
46468
|
+
}
|
|
46469
|
+
return parts.join(`
|
|
46470
|
+
`);
|
|
46471
|
+
}
|
|
46472
|
+
if (content === null || content === undefined)
|
|
46473
|
+
return "";
|
|
46474
|
+
try {
|
|
46475
|
+
return JSON.stringify(content);
|
|
46476
|
+
} catch {
|
|
46477
|
+
return String(content);
|
|
46478
|
+
}
|
|
46479
|
+
}
|
|
46480
|
+
|
|
46481
|
+
// src/components/output-pane.tsx
|
|
46482
|
+
function OutputPane({
|
|
46483
|
+
entries,
|
|
46484
|
+
streaming
|
|
46485
|
+
}) {
|
|
46486
|
+
return /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement(Static, {
|
|
46487
|
+
items: entries
|
|
46488
|
+
}, (entry) => /* @__PURE__ */ import_react26.default.createElement(OutputEntryRow, {
|
|
46489
|
+
key: entry.id,
|
|
46490
|
+
entry
|
|
46491
|
+
})), streaming.length > 0 ? /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46492
|
+
flexDirection: "column"
|
|
46493
|
+
}, streaming.map((s) => /* @__PURE__ */ import_react26.default.createElement(StreamingEntryRow, {
|
|
46494
|
+
key: s.id,
|
|
46495
|
+
entry: s
|
|
46496
|
+
}))) : null);
|
|
46497
|
+
}
|
|
46498
|
+
var BULLET_COLOR = {
|
|
46499
|
+
text: "white",
|
|
46500
|
+
thinking: "magenta",
|
|
46501
|
+
question: "yellow",
|
|
46502
|
+
todo: "blue",
|
|
46503
|
+
agent: "green",
|
|
46504
|
+
tool_use: "cyan",
|
|
46505
|
+
tool_result: "gray"
|
|
46506
|
+
};
|
|
46507
|
+
function Bullet({ kind }) {
|
|
46508
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46509
|
+
color: BULLET_COLOR[kind],
|
|
46510
|
+
bold: true
|
|
46511
|
+
}, "●");
|
|
46512
|
+
}
|
|
46513
|
+
function StructuredHeader({
|
|
46514
|
+
kind,
|
|
46515
|
+
agentType
|
|
46516
|
+
}) {
|
|
46517
|
+
if (kind === "question") {
|
|
46518
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46519
|
+
kind: "question"
|
|
46520
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46521
|
+
bold: true
|
|
46522
|
+
}, " ❓ 提问:"));
|
|
46523
|
+
}
|
|
46524
|
+
if (kind === "todo") {
|
|
46525
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46526
|
+
kind: "todo"
|
|
46527
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46528
|
+
bold: true
|
|
46529
|
+
}, " \uD83D\uDCDD 计划更新:"));
|
|
46530
|
+
}
|
|
46531
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46532
|
+
kind: "agent"
|
|
46533
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46534
|
+
bold: true
|
|
46535
|
+
}, " \uD83E\uDD16 子代理", agentType ? ` [${agentType}]` : "", ":"));
|
|
46536
|
+
}
|
|
46537
|
+
function OutputEntryRow({ entry }) {
|
|
46538
|
+
if (entry.kind === "tool_use") {
|
|
46539
|
+
return /* @__PURE__ */ import_react26.default.createElement(ToolUseRow, {
|
|
46540
|
+
toolUse: entry.toolUse
|
|
46541
|
+
});
|
|
46542
|
+
}
|
|
46543
|
+
if (entry.kind === "tool_result") {
|
|
46544
|
+
return /* @__PURE__ */ import_react26.default.createElement(ToolResultRow, {
|
|
46545
|
+
toolResult: entry.toolResult
|
|
46546
|
+
});
|
|
46547
|
+
}
|
|
46548
|
+
if (entry.kind === "question") {
|
|
46549
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46550
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46551
|
+
kind: "question"
|
|
46552
|
+
}),
|
|
46553
|
+
error: entry.state === "error" ? entry.error : undefined,
|
|
46554
|
+
fallback: `(原文 questions: ${entry.englishQuestion.questions.length})`
|
|
46555
|
+
}, /* @__PURE__ */ import_react26.default.createElement(QuestionBody, {
|
|
46556
|
+
payload: entry.chineseQuestion
|
|
46557
|
+
}));
|
|
46558
|
+
}
|
|
46559
|
+
if (entry.kind === "todo") {
|
|
46560
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46561
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46562
|
+
kind: "todo"
|
|
46563
|
+
}),
|
|
46564
|
+
error: entry.state === "error" ? entry.error : undefined,
|
|
46565
|
+
fallback: `(原文 todos: ${entry.englishTodo.todos.length})`
|
|
46566
|
+
}, /* @__PURE__ */ import_react26.default.createElement(TodoBody, {
|
|
46567
|
+
payload: entry.chineseTodo
|
|
46568
|
+
}));
|
|
46569
|
+
}
|
|
46570
|
+
if (entry.kind === "agent") {
|
|
46571
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46572
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46573
|
+
kind: "agent",
|
|
46574
|
+
agentType: entry.englishAgent.subagent_type
|
|
46575
|
+
}),
|
|
46576
|
+
error: entry.state === "error" ? entry.error : undefined,
|
|
46577
|
+
fallback: `(原文 agent: ${entry.englishAgent.subagent_type})`
|
|
46578
|
+
}, /* @__PURE__ */ import_react26.default.createElement(AgentBody, {
|
|
46579
|
+
payload: entry.chineseAgent
|
|
46580
|
+
}));
|
|
46581
|
+
}
|
|
46582
|
+
const dim = entry.kind === "thinking";
|
|
46583
|
+
if (entry.state === "error") {
|
|
46584
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46585
|
+
flexDirection: "column",
|
|
46586
|
+
marginY: 1,
|
|
46587
|
+
paddingX: 1
|
|
46588
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46589
|
+
kind: entry.kind
|
|
46590
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46591
|
+
color: "red"
|
|
46592
|
+
}, " [translate failed] ", entry.error)), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46593
|
+
dimColor: true
|
|
46594
|
+
}, " (原文) ", entry.english));
|
|
46595
|
+
}
|
|
46596
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
45813
46597
|
flexDirection: "column",
|
|
45814
46598
|
marginY: 1,
|
|
45815
46599
|
paddingX: 1
|
|
45816
|
-
}, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(
|
|
46600
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46601
|
+
kind: entry.kind
|
|
46602
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, null, " "), dim ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46603
|
+
dimColor: true,
|
|
46604
|
+
italic: true
|
|
46605
|
+
}, "\uD83D\uDCAD ", entry.chinese) : /* @__PURE__ */ import_react26.default.createElement(Text, null, entry.chinese)));
|
|
46606
|
+
}
|
|
46607
|
+
function StructuredFrame({
|
|
46608
|
+
header,
|
|
46609
|
+
error,
|
|
46610
|
+
fallback,
|
|
46611
|
+
children
|
|
46612
|
+
}) {
|
|
46613
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46614
|
+
flexDirection: "column",
|
|
46615
|
+
marginY: 1,
|
|
46616
|
+
paddingX: 1
|
|
46617
|
+
}, header, error ? /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46618
|
+
flexDirection: "column"
|
|
46619
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46620
|
+
color: "red"
|
|
46621
|
+
}, " [translate failed] ", error), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46622
|
+
dimColor: true
|
|
46623
|
+
}, " ", fallback)) : children);
|
|
46624
|
+
}
|
|
46625
|
+
function StreamingEntryRow({ entry }) {
|
|
46626
|
+
if (entry.kind === "question") {
|
|
46627
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46628
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46629
|
+
kind: "question"
|
|
46630
|
+
}),
|
|
46631
|
+
fallback: ""
|
|
46632
|
+
}, /* @__PURE__ */ import_react26.default.createElement(QuestionBody, {
|
|
46633
|
+
payload: entry.chineseQuestion,
|
|
46634
|
+
streaming: true
|
|
46635
|
+
}));
|
|
46636
|
+
}
|
|
46637
|
+
if (entry.kind === "todo") {
|
|
46638
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46639
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46640
|
+
kind: "todo"
|
|
46641
|
+
}),
|
|
46642
|
+
fallback: ""
|
|
46643
|
+
}, /* @__PURE__ */ import_react26.default.createElement(TodoBody, {
|
|
46644
|
+
payload: entry.chineseTodo,
|
|
46645
|
+
streaming: true
|
|
46646
|
+
}));
|
|
46647
|
+
}
|
|
46648
|
+
if (entry.kind === "agent") {
|
|
46649
|
+
return /* @__PURE__ */ import_react26.default.createElement(StructuredFrame, {
|
|
46650
|
+
header: /* @__PURE__ */ import_react26.default.createElement(StructuredHeader, {
|
|
46651
|
+
kind: "agent",
|
|
46652
|
+
agentType: entry.englishAgent.subagent_type
|
|
46653
|
+
}),
|
|
46654
|
+
fallback: ""
|
|
46655
|
+
}, /* @__PURE__ */ import_react26.default.createElement(AgentBody, {
|
|
46656
|
+
payload: entry.chineseAgent,
|
|
46657
|
+
streaming: true
|
|
46658
|
+
}));
|
|
46659
|
+
}
|
|
46660
|
+
const empty = entry.chineseSoFar.length === 0;
|
|
46661
|
+
const dim = entry.kind === "thinking";
|
|
46662
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46663
|
+
flexDirection: "column",
|
|
46664
|
+
marginY: 1,
|
|
46665
|
+
paddingX: 1
|
|
46666
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Bullet, {
|
|
46667
|
+
kind: entry.kind
|
|
46668
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Text, null, " "), empty ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46669
|
+
dimColor: true
|
|
46670
|
+
}, dim ? "\uD83D\uDCAD " : "", "翻译中…") : dim ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46671
|
+
dimColor: true,
|
|
46672
|
+
italic: true
|
|
46673
|
+
}, "\uD83D\uDCAD ", entry.chineseSoFar, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46674
|
+
color: "yellow",
|
|
46675
|
+
bold: true
|
|
46676
|
+
}, "▎")) : /* @__PURE__ */ import_react26.default.createElement(Text, null, entry.chineseSoFar, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46677
|
+
color: "yellow",
|
|
46678
|
+
bold: true
|
|
46679
|
+
}, "▎"))));
|
|
46680
|
+
}
|
|
46681
|
+
var CIRCLED_NUMS = ["①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨"];
|
|
46682
|
+
function optionMarker(i) {
|
|
46683
|
+
return i < CIRCLED_NUMS.length ? CIRCLED_NUMS[i] : `${i + 1}.`;
|
|
46684
|
+
}
|
|
46685
|
+
function fieldText(f) {
|
|
46686
|
+
if (f.zh)
|
|
46687
|
+
return { text: f.zh, pending: false };
|
|
46688
|
+
return { text: f.en, pending: true };
|
|
46689
|
+
}
|
|
46690
|
+
function QuestionBody({
|
|
46691
|
+
payload,
|
|
46692
|
+
streaming = false
|
|
46693
|
+
}) {
|
|
46694
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46695
|
+
flexDirection: "column"
|
|
46696
|
+
}, payload.questions.map((q, qi) => /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46697
|
+
key: qi,
|
|
46698
|
+
flexDirection: "column",
|
|
46699
|
+
marginTop: qi > 0 ? 1 : 0
|
|
46700
|
+
}, /* @__PURE__ */ import_react26.default.createElement(QuestionLine, {
|
|
46701
|
+
field: q.question,
|
|
46702
|
+
streaming
|
|
46703
|
+
}), q.options.map((opt, oi) => /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46704
|
+
key: oi,
|
|
46705
|
+
flexDirection: "column",
|
|
46706
|
+
marginLeft: 2,
|
|
46707
|
+
marginTop: 1
|
|
46708
|
+
}, /* @__PURE__ */ import_react26.default.createElement(OptionLabelLine, {
|
|
46709
|
+
marker: optionMarker(oi),
|
|
46710
|
+
field: opt.label,
|
|
46711
|
+
streaming
|
|
46712
|
+
}), /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46713
|
+
marginLeft: 3
|
|
46714
|
+
}, /* @__PURE__ */ import_react26.default.createElement(OptionDescLine, {
|
|
46715
|
+
field: opt.description,
|
|
46716
|
+
streaming
|
|
46717
|
+
})))))));
|
|
46718
|
+
}
|
|
46719
|
+
function QuestionLine({
|
|
46720
|
+
field,
|
|
46721
|
+
streaming
|
|
46722
|
+
}) {
|
|
46723
|
+
const { text, pending } = fieldText(field);
|
|
46724
|
+
if (pending && streaming) {
|
|
46725
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46726
|
+
dimColor: true
|
|
46727
|
+
}, text, " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46728
|
+
color: "yellow"
|
|
46729
|
+
}, "⏳"));
|
|
46730
|
+
}
|
|
46731
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, null, text);
|
|
46732
|
+
}
|
|
46733
|
+
function OptionLabelLine({
|
|
46734
|
+
marker,
|
|
46735
|
+
field,
|
|
46736
|
+
streaming
|
|
46737
|
+
}) {
|
|
46738
|
+
const { text, pending } = fieldText(field);
|
|
46739
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
45817
46740
|
color: "cyan",
|
|
45818
46741
|
bold: true
|
|
45819
|
-
},
|
|
46742
|
+
}, marker, " "), pending && streaming ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46743
|
+
dimColor: true
|
|
46744
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46745
|
+
bold: true
|
|
46746
|
+
}, text), " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46747
|
+
color: "yellow"
|
|
46748
|
+
}, "⏳")) : /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
45820
46749
|
bold: true
|
|
45821
|
-
},
|
|
46750
|
+
}, text));
|
|
46751
|
+
}
|
|
46752
|
+
function OptionDescLine({
|
|
46753
|
+
field,
|
|
46754
|
+
streaming
|
|
46755
|
+
}) {
|
|
46756
|
+
const { text, pending } = fieldText(field);
|
|
46757
|
+
if (pending && streaming) {
|
|
46758
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46759
|
+
dimColor: true
|
|
46760
|
+
}, text, " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46761
|
+
color: "yellow"
|
|
46762
|
+
}, "⏳"));
|
|
46763
|
+
}
|
|
46764
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, null, text);
|
|
46765
|
+
}
|
|
46766
|
+
function statusGlyph(s) {
|
|
46767
|
+
if (s === "completed") {
|
|
46768
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46769
|
+
color: "green",
|
|
46770
|
+
bold: true
|
|
46771
|
+
}, "✓");
|
|
46772
|
+
}
|
|
46773
|
+
if (s === "in_progress") {
|
|
46774
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46775
|
+
color: "yellow",
|
|
46776
|
+
bold: true
|
|
46777
|
+
}, "⟳");
|
|
46778
|
+
}
|
|
46779
|
+
return /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46780
|
+
dimColor: true
|
|
46781
|
+
}, "○");
|
|
46782
|
+
}
|
|
46783
|
+
function TodoBody({
|
|
46784
|
+
payload,
|
|
46785
|
+
streaming = false
|
|
46786
|
+
}) {
|
|
46787
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46788
|
+
flexDirection: "column"
|
|
46789
|
+
}, payload.todos.map((t, i) => {
|
|
46790
|
+
const f = t.status === "in_progress" ? t.activeForm : t.content;
|
|
46791
|
+
const { text, pending } = fieldText(f);
|
|
46792
|
+
const dim = t.status === "completed";
|
|
46793
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46794
|
+
key: i
|
|
46795
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, null, " "), statusGlyph(t.status), /* @__PURE__ */ import_react26.default.createElement(Text, null, " "), pending && streaming ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46796
|
+
dimColor: true
|
|
46797
|
+
}, text, " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46798
|
+
color: "yellow"
|
|
46799
|
+
}, "⏳")) : /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46800
|
+
dimColor: dim,
|
|
46801
|
+
strikethrough: dim
|
|
46802
|
+
}, text));
|
|
46803
|
+
}));
|
|
46804
|
+
}
|
|
46805
|
+
function AgentBody({
|
|
46806
|
+
payload,
|
|
46807
|
+
streaming = false
|
|
46808
|
+
}) {
|
|
46809
|
+
const desc = fieldText(payload.description);
|
|
46810
|
+
const prompt = fieldText(payload.prompt);
|
|
46811
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46812
|
+
flexDirection: "column"
|
|
46813
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46814
|
+
bold: true
|
|
46815
|
+
}, "任务: "), desc.pending && streaming ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46816
|
+
dimColor: true
|
|
46817
|
+
}, desc.text, " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46818
|
+
color: "yellow"
|
|
46819
|
+
}, "⏳")) : /* @__PURE__ */ import_react26.default.createElement(Text, null, desc.text)), /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46820
|
+
marginTop: 1,
|
|
45822
46821
|
flexDirection: "column"
|
|
45823
46822
|
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
45824
|
-
|
|
45825
|
-
}, "
|
|
46823
|
+
bold: true
|
|
46824
|
+
}, "指令:"), /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46825
|
+
marginLeft: 2
|
|
46826
|
+
}, prompt.pending && streaming ? /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
45826
46827
|
dimColor: true
|
|
45827
|
-
}, "
|
|
46828
|
+
}, prompt.text, " ", /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46829
|
+
color: "yellow"
|
|
46830
|
+
}, "⏳")) : /* @__PURE__ */ import_react26.default.createElement(Text, null, prompt.text))));
|
|
46831
|
+
}
|
|
46832
|
+
var TOOL_USE_LINE_CAP = 2;
|
|
46833
|
+
function ToolUseRow({ toolUse }) {
|
|
46834
|
+
const summary = summarizeToolInput(toolUse.name, toolUse.input);
|
|
46835
|
+
const allLines = summary.length > 0 ? summary.split(`
|
|
46836
|
+
`) : [""];
|
|
46837
|
+
const truncated = allLines.length > TOOL_USE_LINE_CAP;
|
|
46838
|
+
const lines = truncated ? allLines.slice(0, TOOL_USE_LINE_CAP) : allLines;
|
|
46839
|
+
const closer = truncated ? "…)" : ")";
|
|
46840
|
+
const continuationIndent = " ".repeat(2 + toolUse.name.length + 1);
|
|
46841
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46842
|
+
flexDirection: "column",
|
|
46843
|
+
marginTop: 1,
|
|
46844
|
+
marginBottom: 0,
|
|
46845
|
+
paddingX: 1
|
|
46846
|
+
}, lines.map((line, i) => {
|
|
46847
|
+
const isLast = i === lines.length - 1;
|
|
46848
|
+
if (i === 0) {
|
|
46849
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46850
|
+
key: i
|
|
46851
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46852
|
+
color: "cyan",
|
|
46853
|
+
bold: true
|
|
46854
|
+
}, "●"), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46855
|
+
bold: true
|
|
46856
|
+
}, " ", toolUse.name, "(", line, isLast ? closer : ""));
|
|
46857
|
+
}
|
|
46858
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46859
|
+
key: i
|
|
46860
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, null, continuationIndent), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46861
|
+
bold: true
|
|
46862
|
+
}, line, isLast ? closer : ""));
|
|
46863
|
+
}));
|
|
46864
|
+
}
|
|
46865
|
+
function ToolResultRow({
|
|
46866
|
+
toolResult
|
|
46867
|
+
}) {
|
|
46868
|
+
const r = formatToolResult(toolResult.content);
|
|
46869
|
+
const errorColor = toolResult.isError ? "red" : undefined;
|
|
46870
|
+
if (r.lines.length === 0) {
|
|
46871
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46872
|
+
marginY: 0,
|
|
46873
|
+
paddingX: 1
|
|
46874
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46875
|
+
color: errorColor ?? "gray"
|
|
46876
|
+
}, " ⎿ "), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46877
|
+
dimColor: true
|
|
46878
|
+
}, "(no output)"));
|
|
46879
|
+
}
|
|
46880
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46881
|
+
flexDirection: "column",
|
|
46882
|
+
marginY: 0,
|
|
46883
|
+
paddingX: 1
|
|
46884
|
+
}, r.lines.map((line, i) => /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46885
|
+
key: i
|
|
46886
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46887
|
+
color: i === 0 ? errorColor ?? "gray" : undefined
|
|
46888
|
+
}, i === 0 ? " ⎿ " : " "), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46889
|
+
dimColor: true,
|
|
46890
|
+
color: errorColor
|
|
46891
|
+
}, line))), r.truncated > 0 ? /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Text, null, " "), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46892
|
+
dimColor: true
|
|
46893
|
+
}, "… +", r.truncated, " lines")) : null);
|
|
45828
46894
|
}
|
|
45829
46895
|
|
|
45830
46896
|
// src/components/settings-modal.tsx
|
|
45831
|
-
var
|
|
46897
|
+
var import_react29 = __toESM(require_react(), 1);
|
|
45832
46898
|
|
|
45833
46899
|
// src/components/multiline-input.tsx
|
|
45834
46900
|
var import_react27 = __toESM(require_react(), 1);
|
|
@@ -46033,6 +47099,125 @@ function MultilineDisplay({
|
|
|
46033
47099
|
}, lines.length, " line", lines.length === 1 ? "" : "s", " · ", charCount, " chars"))));
|
|
46034
47100
|
}
|
|
46035
47101
|
|
|
47102
|
+
// src/components/session-picker.tsx
|
|
47103
|
+
var import_react28 = __toESM(require_react(), 1);
|
|
47104
|
+
function shortId2(id) {
|
|
47105
|
+
if (id.length <= 12)
|
|
47106
|
+
return id;
|
|
47107
|
+
return id.slice(0, 8) + "..." + id.slice(-4);
|
|
47108
|
+
}
|
|
47109
|
+
function SessionPicker({ sessions, onPick, onCancel }) {
|
|
47110
|
+
const [cursor, setCursor] = import_react28.useState(0);
|
|
47111
|
+
const [search, setSearch] = import_react28.useState("");
|
|
47112
|
+
const [searching, setSearching] = import_react28.useState(false);
|
|
47113
|
+
const filtered = search ? sessions.filter((s) => {
|
|
47114
|
+
const haystack = [
|
|
47115
|
+
s.id,
|
|
47116
|
+
s.first_message ?? "",
|
|
47117
|
+
s.started_at ?? "",
|
|
47118
|
+
s.last_activity_at ?? ""
|
|
47119
|
+
].join(" ").toLowerCase();
|
|
47120
|
+
return haystack.includes(search.toLowerCase());
|
|
47121
|
+
}) : sessions;
|
|
47122
|
+
use_input_default((input, key) => {
|
|
47123
|
+
if (searching) {
|
|
47124
|
+
if (key.escape) {
|
|
47125
|
+
setSearching(false);
|
|
47126
|
+
setSearch("");
|
|
47127
|
+
return;
|
|
47128
|
+
}
|
|
47129
|
+
if (key.return) {
|
|
47130
|
+
setSearching(false);
|
|
47131
|
+
return;
|
|
47132
|
+
}
|
|
47133
|
+
if (key.backspace || key.delete) {
|
|
47134
|
+
setSearch((s) => s.slice(0, -1));
|
|
47135
|
+
return;
|
|
47136
|
+
}
|
|
47137
|
+
if (input && !key.ctrl && !key.meta) {
|
|
47138
|
+
setSearch((s) => s + input);
|
|
47139
|
+
}
|
|
47140
|
+
return;
|
|
47141
|
+
}
|
|
47142
|
+
if (key.escape) {
|
|
47143
|
+
onCancel();
|
|
47144
|
+
return;
|
|
47145
|
+
}
|
|
47146
|
+
if (key.upArrow) {
|
|
47147
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
47148
|
+
return;
|
|
47149
|
+
}
|
|
47150
|
+
if (key.downArrow) {
|
|
47151
|
+
setCursor((c) => Math.min(filtered.length - 1, c + 1));
|
|
47152
|
+
return;
|
|
47153
|
+
}
|
|
47154
|
+
if (key.return) {
|
|
47155
|
+
const target = filtered[cursor];
|
|
47156
|
+
if (target)
|
|
47157
|
+
onPick(target);
|
|
47158
|
+
return;
|
|
47159
|
+
}
|
|
47160
|
+
if (input === "/") {
|
|
47161
|
+
setSearching(true);
|
|
47162
|
+
}
|
|
47163
|
+
});
|
|
47164
|
+
if (sessions.length === 0) {
|
|
47165
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47166
|
+
flexDirection: "column",
|
|
47167
|
+
padding: 1
|
|
47168
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47169
|
+
color: "yellow"
|
|
47170
|
+
}, "No Claude Code sessions found in this project."), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47171
|
+
dimColor: true
|
|
47172
|
+
}, "Start `claude` in this project first, then run `cc-zh-watcher` again."), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47173
|
+
dimColor: true
|
|
47174
|
+
}, "Press Esc to exit."));
|
|
47175
|
+
}
|
|
47176
|
+
const visible = filtered.slice(0, 8);
|
|
47177
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47178
|
+
flexDirection: "column"
|
|
47179
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47180
|
+
borderStyle: "round",
|
|
47181
|
+
borderColor: "cyan",
|
|
47182
|
+
flexDirection: "column",
|
|
47183
|
+
paddingX: 1
|
|
47184
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47185
|
+
bold: true,
|
|
47186
|
+
color: "cyan"
|
|
47187
|
+
}, "Select a Claude Code session to watch"), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47188
|
+
dimColor: true
|
|
47189
|
+
}, "project: ", process.cwd(), " · ", sessions.length, " session", sessions.length === 1 ? "" : "s"), /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47190
|
+
marginTop: 1,
|
|
47191
|
+
flexDirection: "column"
|
|
47192
|
+
}, visible.map((s, i) => {
|
|
47193
|
+
const selected = i === cursor;
|
|
47194
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47195
|
+
key: s.id,
|
|
47196
|
+
flexDirection: "column",
|
|
47197
|
+
marginBottom: 1
|
|
47198
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47199
|
+
color: selected ? "cyan" : undefined,
|
|
47200
|
+
bold: selected
|
|
47201
|
+
}, selected ? "⮕ " : " ", i + 1, ". ", shortId2(s.id), " [active", " ", relativeTime(s.last_activity_at), " · ", s.turn_count, " turn", s.turn_count === 1 ? "" : "s", "]"), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47202
|
+
dimColor: true
|
|
47203
|
+
}, " started: ", formatLocalTime(s.started_at)), s.first_message ? /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47204
|
+
dimColor: true
|
|
47205
|
+
}, " first msg: ", JSON.stringify(s.first_message)) : null);
|
|
47206
|
+
}), filtered.length > visible.length ? /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47207
|
+
dimColor: true
|
|
47208
|
+
}, "... and ", filtered.length - visible.length, " more (use / to search)") : null), /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47209
|
+
marginTop: 1
|
|
47210
|
+
}, searching ? /* @__PURE__ */ import_react28.default.createElement(Text, null, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47211
|
+
color: "yellow"
|
|
47212
|
+
}, "/"), search, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47213
|
+
color: "yellow"
|
|
47214
|
+
}, "_"), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47215
|
+
dimColor: true
|
|
47216
|
+
}, " (Esc clear · Enter accept)")) : /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47217
|
+
dimColor: true
|
|
47218
|
+
}, "[↑/↓] move [Enter] select [/] search [Esc] cancel"))));
|
|
47219
|
+
}
|
|
47220
|
+
|
|
46036
47221
|
// src/components/settings-modal.tsx
|
|
46037
47222
|
var FIELD_KEYS = [
|
|
46038
47223
|
"api_key",
|
|
@@ -46051,8 +47236,16 @@ var PROMPT_LABEL = {
|
|
|
46051
47236
|
"zh-to-en-with-context": "zh-to-en-w/ctx",
|
|
46052
47237
|
"en-to-zh": "en-to-zh"
|
|
46053
47238
|
};
|
|
46054
|
-
var
|
|
46055
|
-
var
|
|
47239
|
+
var ROW_SESSION_END = 1;
|
|
47240
|
+
var ROW_API_END = ROW_SESSION_END + FIELD_KEYS.length;
|
|
47241
|
+
var ROW_NOTIFY_END = ROW_API_END + 1;
|
|
47242
|
+
var ROW_THINKING_END = ROW_API_END + 2;
|
|
47243
|
+
var ROW_TODOS_END = ROW_API_END + 3;
|
|
47244
|
+
var ROW_AGENT_END = ROW_API_END + 4;
|
|
47245
|
+
var ROW_CONTEXT_END = ROW_API_END + 5;
|
|
47246
|
+
var TOGGLE_ROW_COUNT = 6;
|
|
47247
|
+
var ROW_TOGGLES_END = ROW_API_END + TOGGLE_ROW_COUNT;
|
|
47248
|
+
var ROW_USER_END = ROW_TOGGLES_END + PROMPT_KEYS.length;
|
|
46056
47249
|
var ROW_DEFAULT_END = ROW_USER_END + PROMPT_KEYS.length;
|
|
46057
47250
|
var TOTAL_ROWS = ROW_DEFAULT_END;
|
|
46058
47251
|
function maskApiKey(key) {
|
|
@@ -46078,24 +47271,33 @@ function previewDefault(text, width) {
|
|
|
46078
47271
|
}
|
|
46079
47272
|
function SettingsModal({
|
|
46080
47273
|
initialConfig,
|
|
46081
|
-
onSave
|
|
47274
|
+
onSave,
|
|
47275
|
+
currentSession,
|
|
47276
|
+
projectDir,
|
|
47277
|
+
onSessionChange
|
|
46082
47278
|
}) {
|
|
46083
|
-
const [fields, setFields] =
|
|
47279
|
+
const [fields, setFields] = import_react29.useState(() => ({
|
|
46084
47280
|
api_key: initialConfig.api_key,
|
|
46085
47281
|
base_url: initialConfig.base_url,
|
|
46086
47282
|
model: initialConfig.model,
|
|
46087
47283
|
timeout_seconds: String(initialConfig.timeout_seconds),
|
|
47284
|
+
notify_on_complete: initialConfig.notify_on_complete,
|
|
47285
|
+
translate_thinking: initialConfig.translate_thinking,
|
|
47286
|
+
translate_todos: initialConfig.translate_todos,
|
|
47287
|
+
translate_agent: initialConfig.translate_agent,
|
|
47288
|
+
context_enabled: initialConfig.context_enabled,
|
|
47289
|
+
show_tool_calls: initialConfig.show_tool_calls,
|
|
46088
47290
|
prompts: {
|
|
46089
47291
|
"zh-to-en": initialConfig.prompts?.["zh-to-en"] ?? "",
|
|
46090
47292
|
"zh-to-en-with-context": initialConfig.prompts?.["zh-to-en-with-context"] ?? "",
|
|
46091
47293
|
"en-to-zh": initialConfig.prompts?.["en-to-zh"] ?? ""
|
|
46092
47294
|
}
|
|
46093
47295
|
}));
|
|
46094
|
-
const [cursor, setCursor] =
|
|
46095
|
-
const [target, setTarget] =
|
|
46096
|
-
const [editBuffer, setEditBuffer] =
|
|
46097
|
-
const [error, setError] =
|
|
46098
|
-
const [saving, setSaving] =
|
|
47296
|
+
const [cursor, setCursor] = import_react29.useState(0);
|
|
47297
|
+
const [target, setTarget] = import_react29.useState(null);
|
|
47298
|
+
const [editBuffer, setEditBuffer] = import_react29.useState("");
|
|
47299
|
+
const [error, setError] = import_react29.useState(null);
|
|
47300
|
+
const [saving, setSaving] = import_react29.useState(false);
|
|
46099
47301
|
function commitScalar(idx, value) {
|
|
46100
47302
|
const fk = FIELD_KEYS[idx];
|
|
46101
47303
|
if (!fk)
|
|
@@ -46153,17 +47355,52 @@ function SettingsModal({
|
|
|
46153
47355
|
return;
|
|
46154
47356
|
}
|
|
46155
47357
|
if (key.return) {
|
|
47358
|
+
if (cursor < ROW_SESSION_END) {
|
|
47359
|
+
setTarget({ kind: "session-pick" });
|
|
47360
|
+
setError(null);
|
|
47361
|
+
return;
|
|
47362
|
+
}
|
|
46156
47363
|
if (cursor < ROW_API_END) {
|
|
46157
|
-
const fk = FIELD_KEYS[cursor];
|
|
47364
|
+
const fk = FIELD_KEYS[cursor - ROW_SESSION_END];
|
|
46158
47365
|
if (!fk)
|
|
46159
47366
|
return;
|
|
46160
47367
|
setEditBuffer(fields[fk]);
|
|
46161
|
-
setTarget({ kind: "scalar", index: cursor });
|
|
47368
|
+
setTarget({ kind: "scalar", index: cursor - ROW_SESSION_END });
|
|
47369
|
+
setError(null);
|
|
47370
|
+
return;
|
|
47371
|
+
}
|
|
47372
|
+
if (cursor < ROW_NOTIFY_END) {
|
|
47373
|
+
setFields((f) => ({ ...f, notify_on_complete: !f.notify_on_complete }));
|
|
47374
|
+
setError(null);
|
|
47375
|
+
return;
|
|
47376
|
+
}
|
|
47377
|
+
if (cursor < ROW_THINKING_END) {
|
|
47378
|
+
setFields((f) => ({ ...f, translate_thinking: !f.translate_thinking }));
|
|
47379
|
+
setError(null);
|
|
47380
|
+
return;
|
|
47381
|
+
}
|
|
47382
|
+
if (cursor < ROW_TODOS_END) {
|
|
47383
|
+
setFields((f) => ({ ...f, translate_todos: !f.translate_todos }));
|
|
47384
|
+
setError(null);
|
|
47385
|
+
return;
|
|
47386
|
+
}
|
|
47387
|
+
if (cursor < ROW_AGENT_END) {
|
|
47388
|
+
setFields((f) => ({ ...f, translate_agent: !f.translate_agent }));
|
|
47389
|
+
setError(null);
|
|
47390
|
+
return;
|
|
47391
|
+
}
|
|
47392
|
+
if (cursor < ROW_CONTEXT_END) {
|
|
47393
|
+
setFields((f) => ({ ...f, context_enabled: !f.context_enabled }));
|
|
47394
|
+
setError(null);
|
|
47395
|
+
return;
|
|
47396
|
+
}
|
|
47397
|
+
if (cursor < ROW_TOGGLES_END) {
|
|
47398
|
+
setFields((f) => ({ ...f, show_tool_calls: !f.show_tool_calls }));
|
|
46162
47399
|
setError(null);
|
|
46163
47400
|
return;
|
|
46164
47401
|
}
|
|
46165
47402
|
if (cursor < ROW_USER_END) {
|
|
46166
|
-
const idx2 = cursor -
|
|
47403
|
+
const idx2 = cursor - ROW_TOGGLES_END;
|
|
46167
47404
|
setTarget({ kind: "user-prompt", rowIndex: idx2 });
|
|
46168
47405
|
setError(null);
|
|
46169
47406
|
return;
|
|
@@ -46190,9 +47427,9 @@ function SettingsModal({
|
|
|
46190
47427
|
const direction = PROMPT_KEYS[target.rowIndex];
|
|
46191
47428
|
if (!direction) {
|
|
46192
47429
|
setTarget(null);
|
|
46193
|
-
return /* @__PURE__ */
|
|
47430
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null);
|
|
46194
47431
|
}
|
|
46195
|
-
return /* @__PURE__ */
|
|
47432
|
+
return /* @__PURE__ */ import_react29.default.createElement(MultilineInput, {
|
|
46196
47433
|
title: `user.${direction}`,
|
|
46197
47434
|
initial: fields.prompts[direction],
|
|
46198
47435
|
onSubmit: (value) => {
|
|
@@ -46206,138 +47443,347 @@ function SettingsModal({
|
|
|
46206
47443
|
const direction = PROMPT_KEYS[target.rowIndex];
|
|
46207
47444
|
if (!direction) {
|
|
46208
47445
|
setTarget(null);
|
|
46209
|
-
return /* @__PURE__ */
|
|
47446
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null);
|
|
46210
47447
|
}
|
|
46211
|
-
return /* @__PURE__ */
|
|
47448
|
+
return /* @__PURE__ */ import_react29.default.createElement(MultilineDisplay, {
|
|
46212
47449
|
title: `default.${direction}`,
|
|
46213
47450
|
text: basePrompt(direction),
|
|
46214
47451
|
onClose: () => setTarget(null)
|
|
46215
47452
|
});
|
|
46216
47453
|
}
|
|
46217
|
-
|
|
47454
|
+
if (target?.kind === "session-pick") {
|
|
47455
|
+
const sessions = listSessions(projectDir);
|
|
47456
|
+
return /* @__PURE__ */ import_react29.default.createElement(SessionPicker, {
|
|
47457
|
+
sessions,
|
|
47458
|
+
onPick: (s) => {
|
|
47459
|
+
setTarget(null);
|
|
47460
|
+
if (s.id !== currentSession.id) {
|
|
47461
|
+
onSessionChange(s);
|
|
47462
|
+
}
|
|
47463
|
+
},
|
|
47464
|
+
onCancel: () => setTarget(null)
|
|
47465
|
+
});
|
|
47466
|
+
}
|
|
47467
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46218
47468
|
flexDirection: "column",
|
|
46219
47469
|
padding: 1
|
|
46220
|
-
}, /* @__PURE__ */
|
|
47470
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46221
47471
|
borderStyle: "round",
|
|
46222
47472
|
borderColor: "cyan",
|
|
46223
47473
|
paddingX: 1,
|
|
46224
47474
|
flexDirection: "column"
|
|
46225
|
-
}, /* @__PURE__ */
|
|
47475
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46226
47476
|
bold: true,
|
|
46227
47477
|
color: "cyan"
|
|
46228
|
-
}, "Settings"), /* @__PURE__ */
|
|
47478
|
+
}, "Settings"), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46229
47479
|
dimColor: true
|
|
46230
|
-
}, "↑/↓ select · Enter edit · Esc save & close"), /* @__PURE__ */
|
|
47480
|
+
}, "↑/↓ select · Enter edit · Esc save & close"), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46231
47481
|
marginTop: 1,
|
|
46232
47482
|
flexDirection: "column"
|
|
46233
|
-
},
|
|
46234
|
-
const selected = cursor ===
|
|
47483
|
+
}, (() => {
|
|
47484
|
+
const selected = cursor === 0;
|
|
47485
|
+
const sid = currentSession.id;
|
|
47486
|
+
const short = sid.length > 12 ? sid.slice(0, 8) + "…" + sid.slice(-4) : sid;
|
|
47487
|
+
const turns = currentSession.turn_count;
|
|
47488
|
+
const active = relativeTime(currentSession.last_activity_at);
|
|
47489
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47490
|
+
color: selected ? "cyan" : undefined,
|
|
47491
|
+
bold: selected
|
|
47492
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47493
|
+
width: 20
|
|
47494
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47495
|
+
color: selected ? "cyan" : undefined
|
|
47496
|
+
}, "session:")), /* @__PURE__ */ import_react29.default.createElement(Text, null, short, " ", /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47497
|
+
dimColor: true
|
|
47498
|
+
}, "(", turns, " turn", turns === 1 ? "" : "s", " · active ", active, "; Enter 切换)")));
|
|
47499
|
+
})(), FIELD_KEYS.map((fk, i) => {
|
|
47500
|
+
const rowIdx = ROW_SESSION_END + i;
|
|
47501
|
+
const selected = cursor === rowIdx;
|
|
46235
47502
|
const editingThis = selected && target?.kind === "scalar" && target.index === i;
|
|
46236
|
-
return /* @__PURE__ */
|
|
47503
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46237
47504
|
key: fk
|
|
46238
|
-
}, /* @__PURE__ */
|
|
47505
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46239
47506
|
color: selected ? "cyan" : undefined,
|
|
46240
47507
|
bold: selected
|
|
46241
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47508
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46242
47509
|
width: 20
|
|
46243
|
-
}, /* @__PURE__ */
|
|
47510
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46244
47511
|
color: selected ? "cyan" : undefined
|
|
46245
|
-
}, FIELD_LABELS[fk], ":")), editingThis ? /* @__PURE__ */
|
|
47512
|
+
}, FIELD_LABELS[fk], ":")), editingThis ? /* @__PURE__ */ import_react29.default.createElement(build_default, {
|
|
46246
47513
|
value: editBuffer,
|
|
46247
47514
|
onChange: setEditBuffer,
|
|
46248
47515
|
onSubmit: handleScalarSubmit
|
|
46249
|
-
}) : /* @__PURE__ */
|
|
46250
|
-
}), /* @__PURE__ */
|
|
47516
|
+
}) : /* @__PURE__ */ import_react29.default.createElement(Text, null, fk === "api_key" ? maskApiKey(fields[fk]) : fields[fk]));
|
|
47517
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46251
47518
|
marginTop: 1
|
|
46252
|
-
}, /* @__PURE__ */
|
|
47519
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47520
|
+
dimColor: true
|
|
47521
|
+
}, "── Toggles ──")), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47522
|
+
rowIdx: ROW_API_END,
|
|
47523
|
+
cursor,
|
|
47524
|
+
label: "notify_on_complete",
|
|
47525
|
+
on: fields.notify_on_complete,
|
|
47526
|
+
hintOn: "(终端响铃;Enter 切换)",
|
|
47527
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47528
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47529
|
+
rowIdx: ROW_NOTIFY_END,
|
|
47530
|
+
cursor,
|
|
47531
|
+
label: "translate_thinking",
|
|
47532
|
+
on: fields.translate_thinking,
|
|
47533
|
+
hintOn: "(译 thinking 块,多花 API;Enter 切换)",
|
|
47534
|
+
hintOff: "(默认关;Enter 切换)"
|
|
47535
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47536
|
+
rowIdx: ROW_THINKING_END,
|
|
47537
|
+
cursor,
|
|
47538
|
+
label: "translate_todos",
|
|
47539
|
+
on: fields.translate_todos,
|
|
47540
|
+
hintOn: "(译 TodoWrite 计划;Enter 切换)",
|
|
47541
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47542
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47543
|
+
rowIdx: ROW_TODOS_END,
|
|
47544
|
+
cursor,
|
|
47545
|
+
label: "translate_agent",
|
|
47546
|
+
on: fields.translate_agent,
|
|
47547
|
+
hintOn: "(译 Agent/Task 子代理;Enter 切换)",
|
|
47548
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47549
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47550
|
+
rowIdx: ROW_AGENT_END,
|
|
47551
|
+
cursor,
|
|
47552
|
+
label: "context_enabled",
|
|
47553
|
+
on: fields.context_enabled,
|
|
47554
|
+
hintOn: "(zh→en 用 Claude 上一条作 context;Enter 切换)",
|
|
47555
|
+
hintOff: "(字面翻译不带 context;Enter 切换)"
|
|
47556
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47557
|
+
rowIdx: ROW_CONTEXT_END,
|
|
47558
|
+
cursor,
|
|
47559
|
+
label: "show_tool_calls",
|
|
47560
|
+
on: fields.show_tool_calls,
|
|
47561
|
+
hintOn: "(显示原始 tool 调用 + 结果(CC 同款);Enter 切换)",
|
|
47562
|
+
hintOff: "(只看翻译内容;Enter 切换)"
|
|
47563
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47564
|
+
marginTop: 1
|
|
47565
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46253
47566
|
dimColor: true
|
|
46254
47567
|
}, "── User prompt (empty = use default) ──")), PROMPT_KEYS.map((dir, i) => {
|
|
46255
|
-
const rowIdx =
|
|
47568
|
+
const rowIdx = ROW_TOGGLES_END + i;
|
|
46256
47569
|
const selected = cursor === rowIdx;
|
|
46257
47570
|
const value = fields.prompts[dir];
|
|
46258
|
-
return /* @__PURE__ */
|
|
47571
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46259
47572
|
key: `user-${dir}`
|
|
46260
|
-
}, /* @__PURE__ */
|
|
47573
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46261
47574
|
color: selected ? "cyan" : undefined,
|
|
46262
47575
|
bold: selected
|
|
46263
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47576
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46264
47577
|
width: 20
|
|
46265
|
-
}, /* @__PURE__ */
|
|
47578
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46266
47579
|
color: selected ? "cyan" : undefined
|
|
46267
|
-
}, "user.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */
|
|
47580
|
+
}, "user.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46268
47581
|
dimColor: !value
|
|
46269
47582
|
}, previewPrompt(value, 60)));
|
|
46270
|
-
}), /* @__PURE__ */
|
|
47583
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46271
47584
|
marginTop: 1
|
|
46272
|
-
}, /* @__PURE__ */
|
|
47585
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46273
47586
|
dimColor: true
|
|
46274
47587
|
}, "── Default prompt (read-only) ──")), PROMPT_KEYS.map((dir, i) => {
|
|
46275
47588
|
const rowIdx = ROW_USER_END + i;
|
|
46276
47589
|
const selected = cursor === rowIdx;
|
|
46277
|
-
return /* @__PURE__ */
|
|
47590
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46278
47591
|
key: `default-${dir}`
|
|
46279
|
-
}, /* @__PURE__ */
|
|
47592
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46280
47593
|
color: selected ? "cyan" : undefined,
|
|
46281
47594
|
bold: selected
|
|
46282
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47595
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46283
47596
|
width: 20
|
|
46284
|
-
}, /* @__PURE__ */
|
|
47597
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46285
47598
|
color: selected ? "cyan" : undefined,
|
|
46286
47599
|
dimColor: true
|
|
46287
|
-
}, "default.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */
|
|
47600
|
+
}, "default.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46288
47601
|
dimColor: true
|
|
46289
47602
|
}, previewDefault(basePrompt(dir), 60)));
|
|
46290
|
-
})), error ? /* @__PURE__ */
|
|
47603
|
+
})), error ? /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46291
47604
|
marginTop: 1
|
|
46292
|
-
}, /* @__PURE__ */
|
|
47605
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46293
47606
|
color: "red"
|
|
46294
|
-
}, "✗ ", error)) : null, saving ? /* @__PURE__ */
|
|
47607
|
+
}, "✗ ", error)) : null, saving ? /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46295
47608
|
marginTop: 1
|
|
46296
|
-
}, /* @__PURE__ */
|
|
47609
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46297
47610
|
color: "yellow"
|
|
46298
47611
|
}, "⏳ saving…")) : null));
|
|
46299
47612
|
}
|
|
47613
|
+
function ToggleRow({
|
|
47614
|
+
rowIdx,
|
|
47615
|
+
cursor,
|
|
47616
|
+
label,
|
|
47617
|
+
on,
|
|
47618
|
+
hintOn,
|
|
47619
|
+
hintOff
|
|
47620
|
+
}) {
|
|
47621
|
+
const selected = cursor === rowIdx;
|
|
47622
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47623
|
+
color: selected ? "cyan" : undefined,
|
|
47624
|
+
bold: selected
|
|
47625
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47626
|
+
width: 22
|
|
47627
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47628
|
+
color: selected ? "cyan" : undefined
|
|
47629
|
+
}, label, ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47630
|
+
color: on ? "green" : "gray",
|
|
47631
|
+
bold: true
|
|
47632
|
+
}, on ? "on" : "off"), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47633
|
+
dimColor: true
|
|
47634
|
+
}, " ", on ? hintOn : hintOff));
|
|
47635
|
+
}
|
|
46300
47636
|
|
|
46301
47637
|
// src/app.tsx
|
|
46302
47638
|
function App2({
|
|
46303
|
-
session,
|
|
47639
|
+
session: initialSession,
|
|
46304
47640
|
config: initialConfig,
|
|
46305
47641
|
projectDir
|
|
46306
47642
|
}) {
|
|
46307
47643
|
const { exit } = use_app_default();
|
|
46308
|
-
const [
|
|
46309
|
-
const [
|
|
46310
|
-
const [
|
|
46311
|
-
const [
|
|
46312
|
-
const [
|
|
46313
|
-
const [
|
|
46314
|
-
const [
|
|
46315
|
-
const [
|
|
46316
|
-
const [
|
|
46317
|
-
const [
|
|
46318
|
-
const
|
|
46319
|
-
const
|
|
46320
|
-
const [
|
|
46321
|
-
const
|
|
46322
|
-
const
|
|
46323
|
-
const
|
|
47644
|
+
const [session, setSession] = import_react30.useState(() => initialSession);
|
|
47645
|
+
const [config, setConfig] = import_react30.useState(() => initialConfig);
|
|
47646
|
+
const [showSettings, setShowSettings] = import_react30.useState(false);
|
|
47647
|
+
const [appState, setAppState] = import_react30.useState("idle");
|
|
47648
|
+
const [chineseInput, setChineseInput] = import_react30.useState("");
|
|
47649
|
+
const [lastEnglish, setLastEnglish] = import_react30.useState("");
|
|
47650
|
+
const [error, setError] = import_react30.useState(null);
|
|
47651
|
+
const [statusLine, setStatusLine] = import_react30.useState(undefined);
|
|
47652
|
+
const [outputEntries, setOutputEntries] = import_react30.useState([]);
|
|
47653
|
+
const [streamingOutputs, setStreamingOutputs] = import_react30.useState([]);
|
|
47654
|
+
const [tailError, setTailError] = import_react30.useState(null);
|
|
47655
|
+
const [streamingEnglish, setStreamingEnglish] = import_react30.useState("");
|
|
47656
|
+
const [pendingConcern, setPendingConcern] = import_react30.useState(null);
|
|
47657
|
+
const translatedInputRef = import_react30.useRef("");
|
|
47658
|
+
const tailRef = import_react30.useRef(null);
|
|
47659
|
+
const seenIdsRef = import_react30.useRef(new Set);
|
|
47660
|
+
const enToZhQueueRef = import_react30.useRef(null);
|
|
47661
|
+
if (enToZhQueueRef.current === null) {
|
|
47662
|
+
enToZhQueueRef.current = new SerialQueue;
|
|
47663
|
+
}
|
|
47664
|
+
const notifyEnabledRef = import_react30.useRef(initialConfig.notify_on_complete);
|
|
47665
|
+
import_react30.useEffect(() => {
|
|
47666
|
+
notifyEnabledRef.current = config.notify_on_complete;
|
|
47667
|
+
}, [config.notify_on_complete]);
|
|
47668
|
+
const translateThinkingRef = import_react30.useRef(initialConfig.translate_thinking);
|
|
47669
|
+
import_react30.useEffect(() => {
|
|
47670
|
+
translateThinkingRef.current = config.translate_thinking;
|
|
47671
|
+
}, [config.translate_thinking]);
|
|
47672
|
+
const translateTodosRef = import_react30.useRef(initialConfig.translate_todos);
|
|
47673
|
+
import_react30.useEffect(() => {
|
|
47674
|
+
translateTodosRef.current = config.translate_todos;
|
|
47675
|
+
}, [config.translate_todos]);
|
|
47676
|
+
const translateAgentRef = import_react30.useRef(initialConfig.translate_agent);
|
|
47677
|
+
import_react30.useEffect(() => {
|
|
47678
|
+
translateAgentRef.current = config.translate_agent;
|
|
47679
|
+
}, [config.translate_agent]);
|
|
47680
|
+
const showToolCallsRef = import_react30.useRef(initialConfig.show_tool_calls);
|
|
47681
|
+
import_react30.useEffect(() => {
|
|
47682
|
+
showToolCallsRef.current = config.show_tool_calls;
|
|
47683
|
+
}, [config.show_tool_calls]);
|
|
47684
|
+
const bellRef = import_react30.useRef(null);
|
|
47685
|
+
if (bellRef.current === null) {
|
|
47686
|
+
bellRef.current = new DebouncedBell(2000, () => {
|
|
47687
|
+
if (notifyEnabledRef.current)
|
|
47688
|
+
ringBell();
|
|
47689
|
+
});
|
|
47690
|
+
}
|
|
47691
|
+
import_react30.useEffect(() => {
|
|
47692
|
+
const bell = bellRef.current;
|
|
47693
|
+
return () => {
|
|
47694
|
+
bell?.cancel();
|
|
47695
|
+
};
|
|
47696
|
+
}, []);
|
|
47697
|
+
const [lastAssistantText, setLastAssistantText] = import_react30.useState("");
|
|
47698
|
+
const [lastRequest, setLastRequest] = import_react30.useState(null);
|
|
47699
|
+
const [sessionMetrics, setSessionMetrics] = import_react30.useState({
|
|
46324
47700
|
started_at: Date.now(),
|
|
46325
47701
|
total_calls: 0,
|
|
46326
47702
|
successful_calls: 0,
|
|
46327
47703
|
error_count: 0,
|
|
46328
47704
|
total_tokens: 0
|
|
46329
47705
|
});
|
|
46330
|
-
|
|
46331
|
-
const historyCursorRef = import_react29.useRef(-1);
|
|
46332
|
-
const HISTORY_MAX = 50;
|
|
46333
|
-
import_react29.useEffect(() => {
|
|
47706
|
+
import_react30.useEffect(() => {
|
|
46334
47707
|
const tail2 = new TranscriptTail(session.path, {
|
|
46335
|
-
|
|
47708
|
+
onContent: (ev) => {
|
|
46336
47709
|
if (seenIdsRef.current.has(ev.id))
|
|
46337
47710
|
return;
|
|
46338
47711
|
seenIdsRef.current.add(ev.id);
|
|
46339
|
-
|
|
46340
|
-
|
|
47712
|
+
bellRef.current?.schedule();
|
|
47713
|
+
if (ev.kind === "text") {
|
|
47714
|
+
setLastAssistantText(ev.text);
|
|
47715
|
+
}
|
|
47716
|
+
if (ev.kind === "thinking" && !translateThinkingRef.current) {
|
|
47717
|
+
return;
|
|
47718
|
+
}
|
|
47719
|
+
if (ev.kind === "question") {
|
|
47720
|
+
const seed = seedTranslatedPayload(ev.question);
|
|
47721
|
+
setStreamingOutputs((arr) => [
|
|
47722
|
+
...arr,
|
|
47723
|
+
{
|
|
47724
|
+
id: ev.id,
|
|
47725
|
+
timestamp: ev.timestamp,
|
|
47726
|
+
kind: "question",
|
|
47727
|
+
englishQuestion: ev.question,
|
|
47728
|
+
chineseQuestion: seed
|
|
47729
|
+
}
|
|
47730
|
+
]);
|
|
47731
|
+
enToZhQueueRef.current.enqueue(() => translateQuestionAndAppend(ev, seed));
|
|
47732
|
+
return;
|
|
47733
|
+
}
|
|
47734
|
+
if (ev.kind === "todo") {
|
|
47735
|
+
if (!translateTodosRef.current)
|
|
47736
|
+
return;
|
|
47737
|
+
const seed = seedTranslatedTodos(ev.todo);
|
|
47738
|
+
setStreamingOutputs((arr) => [
|
|
47739
|
+
...arr,
|
|
47740
|
+
{
|
|
47741
|
+
id: ev.id,
|
|
47742
|
+
timestamp: ev.timestamp,
|
|
47743
|
+
kind: "todo",
|
|
47744
|
+
englishTodo: ev.todo,
|
|
47745
|
+
chineseTodo: seed
|
|
47746
|
+
}
|
|
47747
|
+
]);
|
|
47748
|
+
enToZhQueueRef.current.enqueue(() => translateTodoAndAppend(ev, seed));
|
|
47749
|
+
return;
|
|
47750
|
+
}
|
|
47751
|
+
if (ev.kind === "agent") {
|
|
47752
|
+
if (!translateAgentRef.current)
|
|
47753
|
+
return;
|
|
47754
|
+
const seed = seedTranslatedAgent(ev.agent);
|
|
47755
|
+
setStreamingOutputs((arr) => [
|
|
47756
|
+
...arr,
|
|
47757
|
+
{
|
|
47758
|
+
id: ev.id,
|
|
47759
|
+
timestamp: ev.timestamp,
|
|
47760
|
+
kind: "agent",
|
|
47761
|
+
englishAgent: ev.agent,
|
|
47762
|
+
chineseAgent: seed
|
|
47763
|
+
}
|
|
47764
|
+
]);
|
|
47765
|
+
enToZhQueueRef.current.enqueue(() => translateAgentAndAppend(ev, seed));
|
|
47766
|
+
return;
|
|
47767
|
+
}
|
|
47768
|
+
if (ev.kind === "tool_use" || ev.kind === "tool_result") {
|
|
47769
|
+
if (!showToolCallsRef.current)
|
|
47770
|
+
return;
|
|
47771
|
+
enToZhQueueRef.current.enqueue(async () => {
|
|
47772
|
+
appendRawToolEntry(ev);
|
|
47773
|
+
});
|
|
47774
|
+
return;
|
|
47775
|
+
}
|
|
47776
|
+
setStreamingOutputs((arr) => [
|
|
47777
|
+
...arr,
|
|
47778
|
+
{
|
|
47779
|
+
id: ev.id,
|
|
47780
|
+
timestamp: ev.timestamp,
|
|
47781
|
+
kind: ev.kind,
|
|
47782
|
+
english: ev.text,
|
|
47783
|
+
chineseSoFar: ""
|
|
47784
|
+
}
|
|
47785
|
+
]);
|
|
47786
|
+
enToZhQueueRef.current.enqueue(() => translateAndAppend(ev));
|
|
46341
47787
|
},
|
|
46342
47788
|
onError: (err) => {
|
|
46343
47789
|
setTailError(err.message);
|
|
@@ -46355,46 +47801,334 @@ function App2({
|
|
|
46355
47801
|
};
|
|
46356
47802
|
}, [session.path]);
|
|
46357
47803
|
async function translateAndAppend(ev) {
|
|
46358
|
-
setPendingOutputCount((c) => c + 1);
|
|
46359
47804
|
markRequestStarted("en-to-zh");
|
|
46360
47805
|
try {
|
|
46361
|
-
const result2 = await translate(ev.text, "en-to-zh", config
|
|
47806
|
+
const result2 = await translate(ev.text, "en-to-zh", config, {
|
|
47807
|
+
onDelta: (delta) => {
|
|
47808
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && (e.kind === "text" || e.kind === "thinking") ? { ...e, chineseSoFar: e.chineseSoFar + delta } : e));
|
|
47809
|
+
}
|
|
47810
|
+
});
|
|
46362
47811
|
appendHistory(projectDir, {
|
|
46363
47812
|
timestamp: new Date().toISOString(),
|
|
46364
47813
|
direction: "en-to-zh",
|
|
46365
47814
|
input: ev.text,
|
|
46366
47815
|
output: result2.content
|
|
46367
47816
|
});
|
|
47817
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47818
|
+
setOutputEntries((entries) => [
|
|
47819
|
+
...entries,
|
|
47820
|
+
{
|
|
47821
|
+
id: ev.id,
|
|
47822
|
+
timestamp: ev.timestamp,
|
|
47823
|
+
kind: ev.kind,
|
|
47824
|
+
english: ev.text,
|
|
47825
|
+
chinese: result2.content,
|
|
47826
|
+
state: "translated"
|
|
47827
|
+
}
|
|
47828
|
+
]);
|
|
47829
|
+
markRequestSuccess("en-to-zh", result2.elapsed_ms, result2.usage?.total_tokens ?? null);
|
|
47830
|
+
} catch (err) {
|
|
47831
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47832
|
+
const msg = te?.message ?? err.message;
|
|
47833
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47834
|
+
setOutputEntries((entries) => [
|
|
47835
|
+
...entries,
|
|
47836
|
+
{
|
|
47837
|
+
id: ev.id,
|
|
47838
|
+
timestamp: ev.timestamp,
|
|
47839
|
+
kind: ev.kind,
|
|
47840
|
+
english: ev.text,
|
|
47841
|
+
chinese: "",
|
|
47842
|
+
state: "error",
|
|
47843
|
+
error: msg
|
|
47844
|
+
}
|
|
47845
|
+
]);
|
|
47846
|
+
markRequestError("en-to-zh", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
47847
|
+
}
|
|
47848
|
+
}
|
|
47849
|
+
async function translateQuestionAndAppend(ev, seed) {
|
|
47850
|
+
markRequestStarted("en-to-zh");
|
|
47851
|
+
let totalElapsed = 0;
|
|
47852
|
+
let totalTokens = 0;
|
|
47853
|
+
const pending = [];
|
|
47854
|
+
for (let qi = 0;qi < seed.questions.length; qi += 1) {
|
|
47855
|
+
const q = seed.questions[qi];
|
|
47856
|
+
if (!q.question.zh) {
|
|
47857
|
+
pending.push({
|
|
47858
|
+
en: q.question.en,
|
|
47859
|
+
patch: (zh, cur) => patchField(cur, qi, "question", null, zh)
|
|
47860
|
+
});
|
|
47861
|
+
}
|
|
47862
|
+
for (let oi = 0;oi < q.options.length; oi += 1) {
|
|
47863
|
+
const opt = q.options[oi];
|
|
47864
|
+
if (!opt.label.zh) {
|
|
47865
|
+
pending.push({
|
|
47866
|
+
en: opt.label.en,
|
|
47867
|
+
patch: (zh, cur) => patchField(cur, qi, "label", oi, zh)
|
|
47868
|
+
});
|
|
47869
|
+
}
|
|
47870
|
+
if (!opt.description.zh) {
|
|
47871
|
+
pending.push({
|
|
47872
|
+
en: opt.description.en,
|
|
47873
|
+
patch: (zh, cur) => patchField(cur, qi, "description", oi, zh)
|
|
47874
|
+
});
|
|
47875
|
+
}
|
|
47876
|
+
}
|
|
47877
|
+
}
|
|
47878
|
+
try {
|
|
47879
|
+
for (const p of pending) {
|
|
47880
|
+
const result2 = await translate(p.en, "en-to-zh", config);
|
|
47881
|
+
totalElapsed += result2.elapsed_ms;
|
|
47882
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
47883
|
+
appendHistory(projectDir, {
|
|
47884
|
+
timestamp: new Date().toISOString(),
|
|
47885
|
+
direction: "en-to-zh",
|
|
47886
|
+
input: p.en,
|
|
47887
|
+
output: result2.content
|
|
47888
|
+
});
|
|
47889
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "question" ? { ...e, chineseQuestion: p.patch(result2.content, e.chineseQuestion) } : e));
|
|
47890
|
+
}
|
|
47891
|
+
setStreamingOutputs((arr) => {
|
|
47892
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "question");
|
|
47893
|
+
if (finalEntry && finalEntry.kind === "question") {
|
|
47894
|
+
setOutputEntries((entries) => [
|
|
47895
|
+
...entries,
|
|
47896
|
+
{
|
|
47897
|
+
id: ev.id,
|
|
47898
|
+
timestamp: ev.timestamp,
|
|
47899
|
+
kind: "question",
|
|
47900
|
+
englishQuestion: ev.question,
|
|
47901
|
+
chineseQuestion: finalEntry.chineseQuestion,
|
|
47902
|
+
state: "translated"
|
|
47903
|
+
}
|
|
47904
|
+
]);
|
|
47905
|
+
}
|
|
47906
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
47907
|
+
});
|
|
47908
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
47909
|
+
} catch (err) {
|
|
47910
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47911
|
+
const msg = te?.message ?? err.message;
|
|
47912
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47913
|
+
setOutputEntries((entries) => [
|
|
47914
|
+
...entries,
|
|
47915
|
+
{
|
|
47916
|
+
id: ev.id,
|
|
47917
|
+
timestamp: ev.timestamp,
|
|
47918
|
+
kind: "question",
|
|
47919
|
+
englishQuestion: ev.question,
|
|
47920
|
+
chineseQuestion: seed,
|
|
47921
|
+
state: "error",
|
|
47922
|
+
error: msg
|
|
47923
|
+
}
|
|
47924
|
+
]);
|
|
47925
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
47926
|
+
}
|
|
47927
|
+
}
|
|
47928
|
+
async function translateTodoAndAppend(ev, seed) {
|
|
47929
|
+
markRequestStarted("en-to-zh");
|
|
47930
|
+
let totalElapsed = 0;
|
|
47931
|
+
let totalTokens = 0;
|
|
47932
|
+
const pending = [];
|
|
47933
|
+
for (let i = 0;i < seed.todos.length; i += 1) {
|
|
47934
|
+
const t = seed.todos[i];
|
|
47935
|
+
const which = t.status === "in_progress" ? "activeForm" : "content";
|
|
47936
|
+
const f = which === "activeForm" ? t.activeForm : t.content;
|
|
47937
|
+
if (!f.zh) {
|
|
47938
|
+
pending.push({
|
|
47939
|
+
en: f.en,
|
|
47940
|
+
patch: (zh, cur) => patchTodoField(cur, i, which, zh)
|
|
47941
|
+
});
|
|
47942
|
+
}
|
|
47943
|
+
}
|
|
47944
|
+
try {
|
|
47945
|
+
for (const p of pending) {
|
|
47946
|
+
const result2 = await translate(p.en, "en-to-zh", config);
|
|
47947
|
+
totalElapsed += result2.elapsed_ms;
|
|
47948
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
47949
|
+
appendHistory(projectDir, {
|
|
47950
|
+
timestamp: new Date().toISOString(),
|
|
47951
|
+
direction: "en-to-zh",
|
|
47952
|
+
input: p.en,
|
|
47953
|
+
output: result2.content
|
|
47954
|
+
});
|
|
47955
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "todo" ? { ...e, chineseTodo: p.patch(result2.content, e.chineseTodo) } : e));
|
|
47956
|
+
}
|
|
47957
|
+
setStreamingOutputs((arr) => {
|
|
47958
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "todo");
|
|
47959
|
+
if (finalEntry && finalEntry.kind === "todo") {
|
|
47960
|
+
setOutputEntries((entries) => [
|
|
47961
|
+
...entries,
|
|
47962
|
+
{
|
|
47963
|
+
id: ev.id,
|
|
47964
|
+
timestamp: ev.timestamp,
|
|
47965
|
+
kind: "todo",
|
|
47966
|
+
englishTodo: ev.todo,
|
|
47967
|
+
chineseTodo: finalEntry.chineseTodo,
|
|
47968
|
+
state: "translated"
|
|
47969
|
+
}
|
|
47970
|
+
]);
|
|
47971
|
+
}
|
|
47972
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
47973
|
+
});
|
|
47974
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
47975
|
+
} catch (err) {
|
|
47976
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47977
|
+
const msg = te?.message ?? err.message;
|
|
47978
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
46368
47979
|
setOutputEntries((entries) => [
|
|
46369
47980
|
...entries,
|
|
46370
47981
|
{
|
|
46371
47982
|
id: ev.id,
|
|
46372
47983
|
timestamp: ev.timestamp,
|
|
46373
|
-
|
|
46374
|
-
|
|
46375
|
-
|
|
47984
|
+
kind: "todo",
|
|
47985
|
+
englishTodo: ev.todo,
|
|
47986
|
+
chineseTodo: seed,
|
|
47987
|
+
state: "error",
|
|
47988
|
+
error: msg
|
|
46376
47989
|
}
|
|
46377
47990
|
]);
|
|
46378
|
-
|
|
47991
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
47992
|
+
}
|
|
47993
|
+
}
|
|
47994
|
+
async function translateAgentAndAppend(ev, seed) {
|
|
47995
|
+
markRequestStarted("en-to-zh");
|
|
47996
|
+
let totalElapsed = 0;
|
|
47997
|
+
let totalTokens = 0;
|
|
47998
|
+
try {
|
|
47999
|
+
if (!seed.description.zh) {
|
|
48000
|
+
const result2 = await translate(seed.description.en, "en-to-zh", config);
|
|
48001
|
+
totalElapsed += result2.elapsed_ms;
|
|
48002
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
48003
|
+
appendHistory(projectDir, {
|
|
48004
|
+
timestamp: new Date().toISOString(),
|
|
48005
|
+
direction: "en-to-zh",
|
|
48006
|
+
input: seed.description.en,
|
|
48007
|
+
output: result2.content
|
|
48008
|
+
});
|
|
48009
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "agent" ? {
|
|
48010
|
+
...e,
|
|
48011
|
+
chineseAgent: {
|
|
48012
|
+
...e.chineseAgent,
|
|
48013
|
+
description: { en: e.chineseAgent.description.en, zh: result2.content }
|
|
48014
|
+
}
|
|
48015
|
+
} : e));
|
|
48016
|
+
}
|
|
48017
|
+
if (!seed.prompt.zh) {
|
|
48018
|
+
const result2 = await translate(seed.prompt.en, "en-to-zh", config, {
|
|
48019
|
+
onDelta: (delta) => {
|
|
48020
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "agent" ? {
|
|
48021
|
+
...e,
|
|
48022
|
+
chineseAgent: {
|
|
48023
|
+
...e.chineseAgent,
|
|
48024
|
+
prompt: {
|
|
48025
|
+
en: e.chineseAgent.prompt.en,
|
|
48026
|
+
zh: e.chineseAgent.prompt.zh + delta
|
|
48027
|
+
}
|
|
48028
|
+
}
|
|
48029
|
+
} : e));
|
|
48030
|
+
}
|
|
48031
|
+
});
|
|
48032
|
+
totalElapsed += result2.elapsed_ms;
|
|
48033
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
48034
|
+
appendHistory(projectDir, {
|
|
48035
|
+
timestamp: new Date().toISOString(),
|
|
48036
|
+
direction: "en-to-zh",
|
|
48037
|
+
input: seed.prompt.en,
|
|
48038
|
+
output: result2.content
|
|
48039
|
+
});
|
|
48040
|
+
}
|
|
48041
|
+
setStreamingOutputs((arr) => {
|
|
48042
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "agent");
|
|
48043
|
+
if (finalEntry && finalEntry.kind === "agent") {
|
|
48044
|
+
setOutputEntries((entries) => [
|
|
48045
|
+
...entries,
|
|
48046
|
+
{
|
|
48047
|
+
id: ev.id,
|
|
48048
|
+
timestamp: ev.timestamp,
|
|
48049
|
+
kind: "agent",
|
|
48050
|
+
englishAgent: ev.agent,
|
|
48051
|
+
chineseAgent: finalEntry.chineseAgent,
|
|
48052
|
+
state: "translated"
|
|
48053
|
+
}
|
|
48054
|
+
]);
|
|
48055
|
+
}
|
|
48056
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
48057
|
+
});
|
|
48058
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
46379
48059
|
} catch (err) {
|
|
46380
48060
|
const te = err instanceof TranslateError ? err : null;
|
|
46381
48061
|
const msg = te?.message ?? err.message;
|
|
48062
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
46382
48063
|
setOutputEntries((entries) => [
|
|
46383
48064
|
...entries,
|
|
46384
48065
|
{
|
|
46385
48066
|
id: ev.id,
|
|
46386
48067
|
timestamp: ev.timestamp,
|
|
46387
|
-
|
|
46388
|
-
|
|
48068
|
+
kind: "agent",
|
|
48069
|
+
englishAgent: ev.agent,
|
|
48070
|
+
chineseAgent: seed,
|
|
46389
48071
|
state: "error",
|
|
46390
48072
|
error: msg
|
|
46391
48073
|
}
|
|
46392
48074
|
]);
|
|
46393
|
-
markRequestError("en-to-zh", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
46394
|
-
}
|
|
46395
|
-
|
|
48075
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
48076
|
+
}
|
|
48077
|
+
}
|
|
48078
|
+
function appendRawToolEntry(ev) {
|
|
48079
|
+
if (ev.kind === "tool_use") {
|
|
48080
|
+
setOutputEntries((entries) => [
|
|
48081
|
+
...entries,
|
|
48082
|
+
{
|
|
48083
|
+
id: ev.id,
|
|
48084
|
+
timestamp: ev.timestamp,
|
|
48085
|
+
kind: "tool_use",
|
|
48086
|
+
toolUse: ev.toolUse,
|
|
48087
|
+
state: "translated"
|
|
48088
|
+
}
|
|
48089
|
+
]);
|
|
48090
|
+
} else {
|
|
48091
|
+
setOutputEntries((entries) => [
|
|
48092
|
+
...entries,
|
|
48093
|
+
{
|
|
48094
|
+
id: ev.id,
|
|
48095
|
+
timestamp: ev.timestamp,
|
|
48096
|
+
kind: "tool_result",
|
|
48097
|
+
toolResult: ev.toolResult,
|
|
48098
|
+
state: "translated"
|
|
48099
|
+
}
|
|
48100
|
+
]);
|
|
46396
48101
|
}
|
|
46397
48102
|
}
|
|
48103
|
+
function handleSessionChange(next) {
|
|
48104
|
+
try {
|
|
48105
|
+
writeState(projectDir, {
|
|
48106
|
+
selected_session_id: next.id,
|
|
48107
|
+
selected_at: new Date().toISOString(),
|
|
48108
|
+
transcript_path: next.path
|
|
48109
|
+
});
|
|
48110
|
+
} catch (err) {
|
|
48111
|
+
setStatusLine(`✗ failed to persist session pick: ${err.message}`);
|
|
48112
|
+
}
|
|
48113
|
+
seenIdsRef.current.clear();
|
|
48114
|
+
setLastAssistantText("");
|
|
48115
|
+
bellRef.current?.cancel();
|
|
48116
|
+
const shortPrev = session.id.slice(0, 8);
|
|
48117
|
+
const shortNext = next.id.slice(0, 8);
|
|
48118
|
+
setOutputEntries((entries) => [
|
|
48119
|
+
...entries,
|
|
48120
|
+
{
|
|
48121
|
+
id: `__divider_${Date.now()}`,
|
|
48122
|
+
timestamp: new Date().toISOString(),
|
|
48123
|
+
kind: "text",
|
|
48124
|
+
english: "",
|
|
48125
|
+
chinese: `已切换 session: ${shortPrev}… → ${shortNext}…`,
|
|
48126
|
+
state: "translated"
|
|
48127
|
+
}
|
|
48128
|
+
]);
|
|
48129
|
+
setSession(next);
|
|
48130
|
+
setStatusLine(`✓ switched to session ${shortNext}…`);
|
|
48131
|
+
}
|
|
46398
48132
|
async function saveSettingsFromModal(s) {
|
|
46399
48133
|
const path = userConfigPath();
|
|
46400
48134
|
let existing = {};
|
|
@@ -46410,7 +48144,13 @@ function App2({
|
|
|
46410
48144
|
api_key: s.api_key,
|
|
46411
48145
|
base_url: s.base_url,
|
|
46412
48146
|
model: s.model,
|
|
46413
|
-
timeout_seconds: Number(s.timeout_seconds)
|
|
48147
|
+
timeout_seconds: Number(s.timeout_seconds),
|
|
48148
|
+
notify_on_complete: s.notify_on_complete,
|
|
48149
|
+
translate_thinking: s.translate_thinking,
|
|
48150
|
+
translate_todos: s.translate_todos,
|
|
48151
|
+
translate_agent: s.translate_agent,
|
|
48152
|
+
context_enabled: s.context_enabled,
|
|
48153
|
+
show_tool_calls: s.show_tool_calls
|
|
46414
48154
|
};
|
|
46415
48155
|
const promptsClean = stripEmpty(s.prompts);
|
|
46416
48156
|
if (promptsClean)
|
|
@@ -46418,9 +48158,8 @@ function App2({
|
|
|
46418
48158
|
else
|
|
46419
48159
|
delete merged["prompts"];
|
|
46420
48160
|
try {
|
|
46421
|
-
|
|
46422
|
-
|
|
46423
|
-
`, "utf-8");
|
|
48161
|
+
writeAtomic(path, JSON.stringify(merged, null, 2) + `
|
|
48162
|
+
`, { mode: 384 });
|
|
46424
48163
|
} catch (err) {
|
|
46425
48164
|
setStatusLine(`✗ save failed: ${err.message}`);
|
|
46426
48165
|
setShowSettings(false);
|
|
@@ -46487,63 +48226,89 @@ function App2({
|
|
|
46487
48226
|
setError(null);
|
|
46488
48227
|
setStatusLine(undefined);
|
|
46489
48228
|
setAppState("idle");
|
|
46490
|
-
|
|
48229
|
+
setPendingConcern(null);
|
|
48230
|
+
translatedInputRef.current = "";
|
|
46491
48231
|
return;
|
|
46492
48232
|
}
|
|
46493
|
-
|
|
48233
|
+
});
|
|
48234
|
+
function commitTranslation(trimmedInput, en) {
|
|
48235
|
+
try {
|
|
48236
|
+
const path = writeUserInput(projectDir, en);
|
|
48237
|
+
appendHistory(projectDir, {
|
|
48238
|
+
timestamp: new Date().toISOString(),
|
|
48239
|
+
direction: "zh-to-en",
|
|
48240
|
+
input: trimmedInput,
|
|
48241
|
+
output: en
|
|
48242
|
+
});
|
|
48243
|
+
setStreamingEnglish("");
|
|
48244
|
+
setLastEnglish(en);
|
|
48245
|
+
setStatusLine(`✓ wrote ${path} — switch to Claude Code, type /i ↵`);
|
|
46494
48246
|
setChineseInput("");
|
|
46495
|
-
|
|
46496
|
-
|
|
46497
|
-
|
|
46498
|
-
|
|
46499
|
-
|
|
46500
|
-
|
|
46501
|
-
|
|
46502
|
-
if (key.ctrl && input === "p") {
|
|
46503
|
-
const h = historyRef.current;
|
|
46504
|
-
if (h.length === 0)
|
|
46505
|
-
return;
|
|
46506
|
-
let cur = historyCursorRef.current;
|
|
46507
|
-
if (cur === -1)
|
|
46508
|
-
cur = h.length - 1;
|
|
46509
|
-
else if (cur > 0)
|
|
46510
|
-
cur -= 1;
|
|
46511
|
-
historyCursorRef.current = cur;
|
|
46512
|
-
setChineseInput(h[cur] ?? "");
|
|
46513
|
-
return;
|
|
46514
|
-
}
|
|
46515
|
-
if (key.ctrl && input === "n") {
|
|
46516
|
-
const h = historyRef.current;
|
|
46517
|
-
const cur = historyCursorRef.current;
|
|
46518
|
-
if (h.length === 0 || cur === -1)
|
|
46519
|
-
return;
|
|
46520
|
-
if (cur < h.length - 1) {
|
|
46521
|
-
const next = cur + 1;
|
|
46522
|
-
historyCursorRef.current = next;
|
|
46523
|
-
setChineseInput(h[next] ?? "");
|
|
46524
|
-
} else {
|
|
46525
|
-
historyCursorRef.current = -1;
|
|
46526
|
-
setChineseInput("");
|
|
46527
|
-
}
|
|
46528
|
-
return;
|
|
48247
|
+
setPendingConcern(null);
|
|
48248
|
+
translatedInputRef.current = "";
|
|
48249
|
+
setAppState("idle");
|
|
48250
|
+
} catch (err) {
|
|
48251
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
48252
|
+
setError(`failed to write user-input.md: ${msg}`);
|
|
48253
|
+
setAppState("error");
|
|
46529
48254
|
}
|
|
46530
|
-
}
|
|
48255
|
+
}
|
|
46531
48256
|
async function handleSubmit() {
|
|
46532
48257
|
const trimmed = chineseInput.trim();
|
|
46533
48258
|
if (!trimmed)
|
|
46534
48259
|
return;
|
|
46535
48260
|
if (appState !== "idle")
|
|
46536
48261
|
return;
|
|
48262
|
+
if (pendingConcern && trimmed === translatedInputRef.current) {
|
|
48263
|
+
commitTranslation(trimmed, lastEnglish);
|
|
48264
|
+
return;
|
|
48265
|
+
}
|
|
48266
|
+
if (pendingConcern) {
|
|
48267
|
+
setPendingConcern(null);
|
|
48268
|
+
translatedInputRef.current = "";
|
|
48269
|
+
}
|
|
46537
48270
|
setAppState("translating");
|
|
46538
48271
|
setError(null);
|
|
46539
48272
|
setStatusLine(undefined);
|
|
46540
48273
|
setLastEnglish("");
|
|
48274
|
+
setStreamingEnglish("");
|
|
48275
|
+
const ctx = config.context_enabled && lastAssistantText.trim() ? lastAssistantText : undefined;
|
|
48276
|
+
const useContext7 = ctx !== undefined;
|
|
48277
|
+
let buf = "";
|
|
48278
|
+
let translationStart = -1;
|
|
46541
48279
|
let en;
|
|
48280
|
+
let concern = null;
|
|
46542
48281
|
markRequestStarted("zh-to-en");
|
|
46543
48282
|
try {
|
|
46544
|
-
const
|
|
46545
|
-
|
|
48283
|
+
const result2 = await translate(trimmed, "zh-to-en", config, {
|
|
48284
|
+
contextText: ctx,
|
|
48285
|
+
parseConcernHeader: useContext7,
|
|
48286
|
+
onDelta: (delta) => {
|
|
48287
|
+
buf += delta;
|
|
48288
|
+
if (!useContext7) {
|
|
48289
|
+
setStreamingEnglish(buf);
|
|
48290
|
+
return;
|
|
48291
|
+
}
|
|
48292
|
+
if (translationStart === -1) {
|
|
48293
|
+
const nl = buf.indexOf(`
|
|
48294
|
+
`);
|
|
48295
|
+
if (nl === -1)
|
|
48296
|
+
return;
|
|
48297
|
+
const firstLine = buf.slice(0, nl).trim();
|
|
48298
|
+
if (firstLine === "OK" || firstLine.startsWith("WARN:")) {
|
|
48299
|
+
let i = nl + 1;
|
|
48300
|
+
while (i < buf.length && /\s/.test(buf[i]))
|
|
48301
|
+
i += 1;
|
|
48302
|
+
translationStart = i;
|
|
48303
|
+
} else {
|
|
48304
|
+
translationStart = 0;
|
|
48305
|
+
}
|
|
48306
|
+
}
|
|
48307
|
+
setStreamingEnglish(buf.slice(translationStart));
|
|
48308
|
+
}
|
|
48309
|
+
});
|
|
46546
48310
|
en = result2.content;
|
|
48311
|
+
concern = result2.concern ?? null;
|
|
46547
48312
|
markRequestSuccess("zh-to-en", result2.elapsed_ms, result2.usage?.total_tokens ?? null);
|
|
46548
48313
|
} catch (err) {
|
|
46549
48314
|
const te = err instanceof TranslateError ? err : null;
|
|
@@ -46551,34 +48316,20 @@ function App2({
|
|
|
46551
48316
|
markRequestError("zh-to-en", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
46552
48317
|
setError(msg);
|
|
46553
48318
|
setAppState("error");
|
|
48319
|
+
setStreamingEnglish("");
|
|
46554
48320
|
return;
|
|
46555
48321
|
}
|
|
46556
|
-
|
|
46557
|
-
|
|
46558
|
-
appendHistory(projectDir, {
|
|
46559
|
-
timestamp: new Date().toISOString(),
|
|
46560
|
-
direction: "zh-to-en",
|
|
46561
|
-
input: trimmed,
|
|
46562
|
-
output: en
|
|
46563
|
-
});
|
|
46564
|
-
const h = historyRef.current;
|
|
46565
|
-
if (h[h.length - 1] !== trimmed) {
|
|
46566
|
-
h.push(trimmed);
|
|
46567
|
-
if (h.length > HISTORY_MAX)
|
|
46568
|
-
h.shift();
|
|
46569
|
-
}
|
|
46570
|
-
historyCursorRef.current = -1;
|
|
48322
|
+
if (concern) {
|
|
48323
|
+
setPendingConcern(concern);
|
|
46571
48324
|
setLastEnglish(en);
|
|
46572
|
-
|
|
46573
|
-
|
|
48325
|
+
setStreamingEnglish("");
|
|
48326
|
+
translatedInputRef.current = trimmed;
|
|
46574
48327
|
setAppState("idle");
|
|
46575
|
-
|
|
46576
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
46577
|
-
setError(`failed to write user-input.md: ${msg}`);
|
|
46578
|
-
setAppState("error");
|
|
48328
|
+
return;
|
|
46579
48329
|
}
|
|
48330
|
+
commitTranslation(trimmed, en);
|
|
46580
48331
|
}
|
|
46581
|
-
|
|
48332
|
+
import_react30.useEffect(() => {
|
|
46582
48333
|
if (statusLine && appState === "idle") {
|
|
46583
48334
|
const t = setTimeout(() => setStatusLine(undefined), 8000);
|
|
46584
48335
|
return () => clearTimeout(t);
|
|
@@ -46588,35 +48339,40 @@ function App2({
|
|
|
46588
48339
|
const inputDisabled = appState === "translating";
|
|
46589
48340
|
const combinedStatus = statusLine ?? (tailError ? `tail error: ${tailError}` : undefined);
|
|
46590
48341
|
if (showSettings) {
|
|
46591
|
-
return /* @__PURE__ */
|
|
48342
|
+
return /* @__PURE__ */ import_react30.default.createElement(SettingsModal, {
|
|
46592
48343
|
initialConfig: config,
|
|
46593
|
-
onSave: saveSettingsFromModal
|
|
48344
|
+
onSave: saveSettingsFromModal,
|
|
48345
|
+
currentSession: session,
|
|
48346
|
+
projectDir,
|
|
48347
|
+
onSessionChange: handleSessionChange
|
|
46594
48348
|
});
|
|
46595
48349
|
}
|
|
46596
|
-
return /* @__PURE__ */
|
|
48350
|
+
return /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement(OutputPane, {
|
|
46597
48351
|
entries: outputEntries,
|
|
46598
|
-
|
|
46599
|
-
}), /* @__PURE__ */
|
|
48352
|
+
streaming: streamingOutputs
|
|
48353
|
+
}), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46600
48354
|
flexDirection: "column"
|
|
46601
|
-
}, /* @__PURE__ */
|
|
48355
|
+
}, /* @__PURE__ */ import_react30.default.createElement(StatusBar, {
|
|
46602
48356
|
session,
|
|
46603
48357
|
appState,
|
|
46604
48358
|
model: config.model,
|
|
46605
48359
|
statusLine: combinedStatus,
|
|
46606
|
-
contextEnabled,
|
|
48360
|
+
contextEnabled: config.context_enabled,
|
|
46607
48361
|
contextSize: lastAssistantText.length,
|
|
46608
48362
|
lastRequest,
|
|
46609
48363
|
sessionMetrics
|
|
46610
|
-
}), /* @__PURE__ */
|
|
48364
|
+
}), /* @__PURE__ */ import_react30.default.createElement(InputPane, {
|
|
46611
48365
|
value: chineseInput,
|
|
46612
48366
|
onChange: setChineseInput,
|
|
46613
48367
|
onSubmit: () => void handleSubmit(),
|
|
46614
48368
|
disabled: inputDisabled,
|
|
46615
48369
|
placeholder: inputDisabled ? "" : "输入中文,回车直接翻译并发送…"
|
|
46616
|
-
}), /* @__PURE__ */
|
|
48370
|
+
}), /* @__PURE__ */ import_react30.default.createElement(PreviewPane, {
|
|
46617
48371
|
text: appState === "idle" ? lastEnglish : "",
|
|
46618
48372
|
translating: appState === "translating",
|
|
46619
|
-
|
|
48373
|
+
streamingText: streamingEnglish,
|
|
48374
|
+
error: appState === "error" ? error : null,
|
|
48375
|
+
concern: pendingConcern
|
|
46620
48376
|
})));
|
|
46621
48377
|
}
|
|
46622
48378
|
function stripEmpty(map2) {
|
|
@@ -46627,128 +48383,144 @@ function stripEmpty(map2) {
|
|
|
46627
48383
|
}
|
|
46628
48384
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
46629
48385
|
}
|
|
46630
|
-
|
|
46631
|
-
|
|
46632
|
-
|
|
46633
|
-
|
|
46634
|
-
|
|
46635
|
-
|
|
46636
|
-
|
|
48386
|
+
function seedTranslatedPayload(raw) {
|
|
48387
|
+
const seedOption = (o) => {
|
|
48388
|
+
const out = {
|
|
48389
|
+
label: seedString(o.label),
|
|
48390
|
+
description: seedString(o.description)
|
|
48391
|
+
};
|
|
48392
|
+
if (o.preview !== undefined)
|
|
48393
|
+
out.preview = o.preview;
|
|
48394
|
+
return out;
|
|
48395
|
+
};
|
|
48396
|
+
return {
|
|
48397
|
+
questions: raw.questions.map((q) => ({
|
|
48398
|
+
question: seedString(q.question),
|
|
48399
|
+
header: q.header,
|
|
48400
|
+
multiSelect: q.multiSelect,
|
|
48401
|
+
options: q.options.map(seedOption)
|
|
48402
|
+
}))
|
|
48403
|
+
};
|
|
46637
48404
|
}
|
|
46638
|
-
function
|
|
46639
|
-
|
|
46640
|
-
|
|
46641
|
-
|
|
46642
|
-
|
|
46643
|
-
|
|
46644
|
-
|
|
46645
|
-
|
|
46646
|
-
|
|
46647
|
-
|
|
46648
|
-
|
|
46649
|
-
|
|
46650
|
-
|
|
46651
|
-
|
|
46652
|
-
|
|
46653
|
-
|
|
46654
|
-
|
|
46655
|
-
|
|
46656
|
-
|
|
46657
|
-
|
|
46658
|
-
|
|
46659
|
-
|
|
46660
|
-
return;
|
|
46661
|
-
}
|
|
46662
|
-
if (key.backspace || key.delete) {
|
|
46663
|
-
setSearch((s) => s.slice(0, -1));
|
|
46664
|
-
return;
|
|
46665
|
-
}
|
|
46666
|
-
if (input && !key.ctrl && !key.meta) {
|
|
46667
|
-
setSearch((s) => s + input);
|
|
48405
|
+
function seedTranslatedTodos(raw) {
|
|
48406
|
+
return {
|
|
48407
|
+
todos: raw.todos.map((t) => ({
|
|
48408
|
+
content: seedString(t.content),
|
|
48409
|
+
activeForm: seedString(t.activeForm),
|
|
48410
|
+
status: t.status
|
|
48411
|
+
}))
|
|
48412
|
+
};
|
|
48413
|
+
}
|
|
48414
|
+
function seedTranslatedAgent(raw) {
|
|
48415
|
+
return {
|
|
48416
|
+
description: seedString(raw.description),
|
|
48417
|
+
prompt: seedString(raw.prompt),
|
|
48418
|
+
subagent_type: raw.subagent_type
|
|
48419
|
+
};
|
|
48420
|
+
}
|
|
48421
|
+
function patchField(payload, qi, which, oi, zh) {
|
|
48422
|
+
return {
|
|
48423
|
+
questions: payload.questions.map((q, i) => {
|
|
48424
|
+
if (i !== qi)
|
|
48425
|
+
return q;
|
|
48426
|
+
if (which === "question") {
|
|
48427
|
+
return { ...q, question: { ...q.question, zh } };
|
|
46668
48428
|
}
|
|
46669
|
-
return
|
|
46670
|
-
|
|
46671
|
-
|
|
46672
|
-
|
|
46673
|
-
|
|
46674
|
-
|
|
46675
|
-
|
|
46676
|
-
|
|
46677
|
-
|
|
46678
|
-
|
|
46679
|
-
|
|
46680
|
-
|
|
46681
|
-
|
|
46682
|
-
|
|
46683
|
-
|
|
46684
|
-
|
|
46685
|
-
if (
|
|
46686
|
-
|
|
46687
|
-
|
|
46688
|
-
|
|
46689
|
-
|
|
46690
|
-
|
|
46691
|
-
|
|
46692
|
-
});
|
|
46693
|
-
if (sessions.length === 0) {
|
|
46694
|
-
return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46695
|
-
flexDirection: "column",
|
|
46696
|
-
padding: 1
|
|
46697
|
-
}, /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46698
|
-
color: "yellow"
|
|
46699
|
-
}, "No Claude Code sessions found in this project."), /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46700
|
-
dimColor: true
|
|
46701
|
-
}, "Start `claude` in this project first, then run `cc-zh-watcher` again."), /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46702
|
-
dimColor: true
|
|
46703
|
-
}, "Press Esc to exit."));
|
|
46704
|
-
}
|
|
46705
|
-
const visible = filtered.slice(0, 8);
|
|
46706
|
-
return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46707
|
-
flexDirection: "column"
|
|
46708
|
-
}, /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46709
|
-
borderStyle: "round",
|
|
46710
|
-
borderColor: "cyan",
|
|
46711
|
-
flexDirection: "column",
|
|
46712
|
-
paddingX: 1
|
|
46713
|
-
}, /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46714
|
-
bold: true,
|
|
46715
|
-
color: "cyan"
|
|
46716
|
-
}, "Select a Claude Code session to watch"), /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46717
|
-
dimColor: true
|
|
46718
|
-
}, "project: ", process.cwd(), " · ", sessions.length, " session", sessions.length === 1 ? "" : "s"), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46719
|
-
marginTop: 1,
|
|
46720
|
-
flexDirection: "column"
|
|
46721
|
-
}, visible.map((s, i) => {
|
|
46722
|
-
const selected = i === cursor;
|
|
46723
|
-
return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46724
|
-
key: s.id,
|
|
46725
|
-
flexDirection: "column",
|
|
46726
|
-
marginBottom: 1
|
|
46727
|
-
}, /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46728
|
-
color: selected ? "cyan" : undefined,
|
|
46729
|
-
bold: selected
|
|
46730
|
-
}, selected ? "⮕ " : " ", i + 1, ". ", shortId2(s.id), " [active", " ", relativeTime(s.last_activity_at), " · ", s.turn_count, " turn", s.turn_count === 1 ? "" : "s", "]"), /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46731
|
-
dimColor: true
|
|
46732
|
-
}, " started: ", formatLocalTime(s.started_at)), s.first_message ? /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46733
|
-
dimColor: true
|
|
46734
|
-
}, " first msg: ", JSON.stringify(s.first_message)) : null);
|
|
46735
|
-
}), filtered.length > visible.length ? /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46736
|
-
dimColor: true
|
|
46737
|
-
}, "... and ", filtered.length - visible.length, " more (use / to search)") : null), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46738
|
-
marginTop: 1
|
|
46739
|
-
}, searching ? /* @__PURE__ */ import_react30.default.createElement(Text, null, /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46740
|
-
color: "yellow"
|
|
46741
|
-
}, "/"), search, /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46742
|
-
color: "yellow"
|
|
46743
|
-
}, "_"), /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46744
|
-
dimColor: true
|
|
46745
|
-
}, " (Esc clear · Enter accept)")) : /* @__PURE__ */ import_react30.default.createElement(Text, {
|
|
46746
|
-
dimColor: true
|
|
46747
|
-
}, "[↑/↓] move [Enter] select [/] search [Esc] cancel"))));
|
|
48429
|
+
return {
|
|
48430
|
+
...q,
|
|
48431
|
+
options: q.options.map((o, j) => {
|
|
48432
|
+
if (j !== oi)
|
|
48433
|
+
return o;
|
|
48434
|
+
if (which === "label")
|
|
48435
|
+
return { ...o, label: { ...o.label, zh } };
|
|
48436
|
+
return { ...o, description: { ...o.description, zh } };
|
|
48437
|
+
})
|
|
48438
|
+
};
|
|
48439
|
+
})
|
|
48440
|
+
};
|
|
48441
|
+
}
|
|
48442
|
+
function patchTodoField(payload, i, which, zh) {
|
|
48443
|
+
return {
|
|
48444
|
+
todos: payload.todos.map((t, j) => {
|
|
48445
|
+
if (j !== i)
|
|
48446
|
+
return t;
|
|
48447
|
+
if (which === "content")
|
|
48448
|
+
return { ...t, content: { ...t.content, zh } };
|
|
48449
|
+
return { ...t, activeForm: { ...t.activeForm, zh } };
|
|
48450
|
+
})
|
|
48451
|
+
};
|
|
46748
48452
|
}
|
|
48453
|
+
// package.json
|
|
48454
|
+
var package_default = {
|
|
48455
|
+
name: "cc-zh-watcher",
|
|
48456
|
+
version: "0.2.1",
|
|
48457
|
+
description: "Side-channel Chinese ↔ English bridge for Claude Code (single-watcher TUI). Type Chinese, Claude sees English; Claude replies in English, you see Chinese.",
|
|
48458
|
+
type: "module",
|
|
48459
|
+
bin: {
|
|
48460
|
+
"cc-zh-watcher": "dist/cli.js"
|
|
48461
|
+
},
|
|
48462
|
+
main: "./dist/cli.js",
|
|
48463
|
+
files: [
|
|
48464
|
+
"dist/cli.js",
|
|
48465
|
+
"README.md",
|
|
48466
|
+
"LICENSE"
|
|
48467
|
+
],
|
|
48468
|
+
scripts: {
|
|
48469
|
+
dev: "bun run src/cli.ts",
|
|
48470
|
+
build: "bun build src/cli.ts --outfile dist/cli.js --target node",
|
|
48471
|
+
compile: "bun build src/cli.ts --compile --outfile dist/cc-zh-watcher",
|
|
48472
|
+
"compile:linux-x64": "bun build src/cli.ts --compile --target=bun-linux-x64 --outfile dist/cc-zh-watcher-linux-x64",
|
|
48473
|
+
"compile:linux-arm64": "bun build src/cli.ts --compile --target=bun-linux-arm64 --outfile dist/cc-zh-watcher-linux-arm64",
|
|
48474
|
+
"compile:darwin-x64": "bun build src/cli.ts --compile --target=bun-darwin-x64 --outfile dist/cc-zh-watcher-darwin-x64",
|
|
48475
|
+
"compile:darwin-arm64": "bun build src/cli.ts --compile --target=bun-darwin-arm64 --outfile dist/cc-zh-watcher-darwin-arm64",
|
|
48476
|
+
"compile:windows-x64": "bun build src/cli.ts --compile --target=bun-windows-x64 --outfile dist/cc-zh-watcher-windows-x64.exe",
|
|
48477
|
+
"compile:all": "bun run compile:linux-x64 && bun run compile:linux-arm64 && bun run compile:darwin-x64 && bun run compile:darwin-arm64 && bun run compile:windows-x64",
|
|
48478
|
+
test: "bun test",
|
|
48479
|
+
typecheck: "tsc --noEmit",
|
|
48480
|
+
prepublishOnly: "bun run typecheck && bun test && bun run build",
|
|
48481
|
+
"install:local": "bash scripts/install-local.sh",
|
|
48482
|
+
"uninstall:local": "npm uninstall -g cc-zh-watcher"
|
|
48483
|
+
},
|
|
48484
|
+
keywords: [
|
|
48485
|
+
"claude-code",
|
|
48486
|
+
"claude",
|
|
48487
|
+
"translation",
|
|
48488
|
+
"chinese",
|
|
48489
|
+
"i18n",
|
|
48490
|
+
"tui",
|
|
48491
|
+
"ink",
|
|
48492
|
+
"cli",
|
|
48493
|
+
"openai",
|
|
48494
|
+
"deepseek"
|
|
48495
|
+
],
|
|
48496
|
+
author: "storm <xm4763@gmail.com>",
|
|
48497
|
+
license: "MIT",
|
|
48498
|
+
repository: {
|
|
48499
|
+
type: "git",
|
|
48500
|
+
url: "git+https://github.com/CodingForMoney/cc-zh-watcher.git"
|
|
48501
|
+
},
|
|
48502
|
+
homepage: "https://github.com/CodingForMoney/cc-zh-watcher/#readme",
|
|
48503
|
+
bugs: {
|
|
48504
|
+
url: "https://github.com/CodingForMoney/cc-zh-watcher/issues"
|
|
48505
|
+
},
|
|
48506
|
+
engines: {
|
|
48507
|
+
node: ">=20.0.0"
|
|
48508
|
+
},
|
|
48509
|
+
devDependencies: {
|
|
48510
|
+
"@types/node": "^20.14.0",
|
|
48511
|
+
"@types/react": "^18.3.0",
|
|
48512
|
+
"bun-types": "^1.3.13",
|
|
48513
|
+
commander: "^12.1.0",
|
|
48514
|
+
ink: "^5.0.1",
|
|
48515
|
+
"ink-text-input": "^6.0.0",
|
|
48516
|
+
react: "^18.3.1",
|
|
48517
|
+
"react-devtools-core": "^7.0.1",
|
|
48518
|
+
typescript: "^5.5.0"
|
|
48519
|
+
}
|
|
48520
|
+
};
|
|
46749
48521
|
|
|
46750
48522
|
// src/cli.ts
|
|
46751
|
-
var VERSION =
|
|
48523
|
+
var VERSION = package_default.version;
|
|
46752
48524
|
function fail(msg, code = 1) {
|
|
46753
48525
|
process.stderr.write(`error: ${msg}
|
|
46754
48526
|
`);
|
|
@@ -46817,37 +48589,42 @@ async function resolveSession(opts) {
|
|
|
46817
48589
|
return picked;
|
|
46818
48590
|
}
|
|
46819
48591
|
async function runWatch(opts) {
|
|
46820
|
-
const
|
|
48592
|
+
const cwd2 = process.cwd();
|
|
46821
48593
|
let config;
|
|
46822
48594
|
try {
|
|
46823
|
-
config = loadConfig({ projectDir, explicitPath: opts.config });
|
|
48595
|
+
config = loadConfig({ projectDir: cwd2, explicitPath: opts.config });
|
|
46824
48596
|
} catch (err) {
|
|
46825
|
-
|
|
48597
|
+
const msg = err.message;
|
|
48598
|
+
if (msg.includes("no api_key configured")) {
|
|
48599
|
+
await runInlineConfigure();
|
|
48600
|
+
try {
|
|
48601
|
+
config = loadConfig({ projectDir: cwd2, explicitPath: opts.config });
|
|
48602
|
+
} catch (err2) {
|
|
48603
|
+
fail(err2.message);
|
|
48604
|
+
}
|
|
48605
|
+
} else {
|
|
48606
|
+
fail(msg);
|
|
48607
|
+
}
|
|
46826
48608
|
}
|
|
46827
48609
|
const session = await resolveSession({
|
|
46828
48610
|
pick: opts.pick,
|
|
46829
48611
|
sessionPrefix: opts.session,
|
|
46830
|
-
projectDir
|
|
48612
|
+
projectDir: cwd2
|
|
46831
48613
|
});
|
|
46832
48614
|
if (!session) {
|
|
46833
48615
|
fail("no session selected", 0);
|
|
46834
48616
|
}
|
|
48617
|
+
const projectDir = session.cwd ?? cwd2;
|
|
46835
48618
|
if (!existsSync8(slashCommandPath(projectDir))) {
|
|
46836
|
-
|
|
48619
|
+
const accepted = await promptInstallSlashCommand(projectDir);
|
|
48620
|
+
if (!accepted) {
|
|
48621
|
+
process.stderr.write(`note: /i not installed — you'll need to manually paste the
|
|
48622
|
+
` + ` translated English into Claude Code each turn.
|
|
46837
48623
|
`);
|
|
48624
|
+
}
|
|
46838
48625
|
}
|
|
46839
48626
|
render_default(import_react31.default.createElement(App2, { session, config, projectDir }));
|
|
46840
48627
|
}
|
|
46841
|
-
function runInstall() {
|
|
46842
|
-
const projectDir = process.cwd();
|
|
46843
|
-
try {
|
|
46844
|
-
const { installedPath, existed } = installSlashCommand(projectDir);
|
|
46845
|
-
ok(`${existed ? "✓ replaced" : "✓ installed"} ${installedPath}
|
|
46846
|
-
` + ` /i in Claude Code now reads .claude/cache/user-input.md`);
|
|
46847
|
-
} catch (err) {
|
|
46848
|
-
fail(err.message);
|
|
46849
|
-
}
|
|
46850
|
-
}
|
|
46851
48628
|
async function runUninstall(opts) {
|
|
46852
48629
|
const projectDir = process.cwd();
|
|
46853
48630
|
let wipe = opts.wipeCache;
|
|
@@ -46863,18 +48640,18 @@ async function runUninstall(opts) {
|
|
|
46863
48640
|
for (const s of skipped)
|
|
46864
48641
|
ok(` (not present) ${s}`);
|
|
46865
48642
|
}
|
|
46866
|
-
async function
|
|
48643
|
+
async function runInlineConfigure() {
|
|
46867
48644
|
const path = userConfigPath();
|
|
46868
48645
|
let existing = {};
|
|
46869
48646
|
if (existsSync8(path)) {
|
|
46870
48647
|
try {
|
|
46871
48648
|
existing = JSON.parse(readFileSync8(path, "utf-8"));
|
|
46872
|
-
ok(`existing config at ${path}:`);
|
|
46873
|
-
ok(` base_url: ${existing["base_url"] ?? "(unset)"}`);
|
|
46874
|
-
ok(` model: ${existing["model"] ?? "(unset)"}`);
|
|
46875
|
-
ok(` api_key: ${existing["api_key"] ? "(set)" : "(unset)"}`);
|
|
46876
48649
|
} catch {}
|
|
46877
48650
|
}
|
|
48651
|
+
ok("");
|
|
48652
|
+
ok("─ First-time setup ─────────────────────────────────────────");
|
|
48653
|
+
ok("No API endpoint configured yet. Set one up now (Ctrl+C to abort).");
|
|
48654
|
+
ok("");
|
|
46878
48655
|
const rl = createInterface({ input, output });
|
|
46879
48656
|
const ask = async (prompt, fallback) => {
|
|
46880
48657
|
const ans = (await rl.question(prompt)).trim();
|
|
@@ -46894,10 +48671,31 @@ async function runConfigure() {
|
|
|
46894
48671
|
model,
|
|
46895
48672
|
timeout_seconds: Number(timeoutStr) || 30
|
|
46896
48673
|
};
|
|
46897
|
-
|
|
46898
|
-
|
|
46899
|
-
`, "utf-8");
|
|
48674
|
+
writeAtomic(path, JSON.stringify(next, null, 2) + `
|
|
48675
|
+
`, { mode: 384 });
|
|
46900
48676
|
ok(`✓ wrote ${path}`);
|
|
48677
|
+
ok("");
|
|
48678
|
+
}
|
|
48679
|
+
async function promptInstallSlashCommand(projectDir) {
|
|
48680
|
+
const target = slashCommandPath(projectDir);
|
|
48681
|
+
const rl = createInterface({ input, output });
|
|
48682
|
+
const ans = (await rl.question(`
|
|
48683
|
+
/i slash command not yet installed in this project.
|
|
48684
|
+
` + ` project: ${projectDir}
|
|
48685
|
+
` + ` will write: ${target}
|
|
48686
|
+
` + `Install now? [Y/n] `)).trim().toLowerCase();
|
|
48687
|
+
rl.close();
|
|
48688
|
+
if (ans === "n" || ans === "no")
|
|
48689
|
+
return false;
|
|
48690
|
+
try {
|
|
48691
|
+
installSlashCommand(projectDir);
|
|
48692
|
+
ok(`✓ installed ${target}`);
|
|
48693
|
+
return true;
|
|
48694
|
+
} catch (err) {
|
|
48695
|
+
process.stderr.write(`✗ install failed: ${err.message}
|
|
48696
|
+
`);
|
|
48697
|
+
return false;
|
|
48698
|
+
}
|
|
46901
48699
|
}
|
|
46902
48700
|
function runPromptsShow(opts) {
|
|
46903
48701
|
let config = null;
|
|
@@ -47009,17 +48807,15 @@ function runSessionsList() {
|
|
|
47009
48807
|
state file: ${stateFilePath(projectDir)}`);
|
|
47010
48808
|
}
|
|
47011
48809
|
var program2 = new Command;
|
|
47012
|
-
program2.name("cc-zh-watcher").version(VERSION);
|
|
48810
|
+
program2.name("cc-zh-watcher").version(VERSION, "-v, --version", "output the version number");
|
|
47013
48811
|
program2.description("Side-channel Chinese ↔ English bridge for Claude Code").option("--pick", "force session picker (switch session)").option("--session <id>", "select session by uuid or unique prefix").option("--config <path>", "use a specific config file").action(async (opts) => {
|
|
47014
48812
|
await runWatch(opts);
|
|
47015
48813
|
});
|
|
47016
|
-
program2.command("install").description("install /i slash command into <project>/.claude/commands/").action(runInstall);
|
|
47017
48814
|
program2.command("uninstall").description("remove /i (optionally wipe cache files)").option("--wipe-cache", "also delete user-input.md + history.jsonl").option("--keep-cache", "keep cache files").action(async (opts) => {
|
|
47018
48815
|
await runUninstall({
|
|
47019
48816
|
wipeCache: opts.wipeCache ? true : opts.keepCache ? false : undefined
|
|
47020
48817
|
});
|
|
47021
48818
|
});
|
|
47022
|
-
program2.command("configure").description("interactively create / edit user-global config").action(runConfigure);
|
|
47023
48819
|
program2.command("sessions").description("list all sessions in this project").action(runSessionsList);
|
|
47024
48820
|
var promptsCmd = program2.command("prompts").description("inspect / introspect the translation prompts");
|
|
47025
48821
|
promptsCmd.command("show [direction]").description("print the active prompt for a direction (or all if omitted)").option("--base", "show the built-in base, ignoring overrides/extensions").action((direction, opts) => {
|