cc-zh-watcher 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -121
- package/dist/cli.js +2180 -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,527 @@ 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 = 5;
|
|
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
|
+
function ToolUseRow({ toolUse }) {
|
|
46833
|
+
const summary = summarizeToolInput(toolUse.name, toolUse.input);
|
|
46834
|
+
const lines = summary.length > 0 ? summary.split(`
|
|
46835
|
+
`) : [""];
|
|
46836
|
+
const continuationIndent = " ".repeat(2 + toolUse.name.length + 1);
|
|
46837
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46838
|
+
flexDirection: "column",
|
|
46839
|
+
marginY: 1,
|
|
46840
|
+
paddingX: 1
|
|
46841
|
+
}, lines.map((line, i) => {
|
|
46842
|
+
const isLast = i === lines.length - 1;
|
|
46843
|
+
if (i === 0) {
|
|
46844
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46845
|
+
key: i
|
|
46846
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46847
|
+
color: "cyan",
|
|
46848
|
+
bold: true
|
|
46849
|
+
}, "●"), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46850
|
+
bold: true
|
|
46851
|
+
}, " ", toolUse.name, "(", line, isLast ? ")" : ""));
|
|
46852
|
+
}
|
|
46853
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46854
|
+
key: i
|
|
46855
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, null, continuationIndent), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46856
|
+
bold: true
|
|
46857
|
+
}, line, isLast ? ")" : ""));
|
|
46858
|
+
}));
|
|
46859
|
+
}
|
|
46860
|
+
function ToolResultRow({
|
|
46861
|
+
toolResult
|
|
46862
|
+
}) {
|
|
46863
|
+
const r = formatToolResult(toolResult.content);
|
|
46864
|
+
const errorColor = toolResult.isError ? "red" : undefined;
|
|
46865
|
+
if (r.lines.length === 0) {
|
|
46866
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46867
|
+
marginY: 0,
|
|
46868
|
+
paddingX: 1
|
|
46869
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46870
|
+
color: errorColor ?? "gray"
|
|
46871
|
+
}, " ⎿ "), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46872
|
+
dimColor: true
|
|
46873
|
+
}, "(no output)"));
|
|
46874
|
+
}
|
|
46875
|
+
return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46876
|
+
flexDirection: "column",
|
|
46877
|
+
marginY: 0,
|
|
46878
|
+
paddingX: 1
|
|
46879
|
+
}, r.lines.map((line, i) => /* @__PURE__ */ import_react26.default.createElement(Box_default, {
|
|
46880
|
+
key: i
|
|
46881
|
+
}, /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46882
|
+
color: i === 0 ? errorColor ?? "gray" : undefined
|
|
46883
|
+
}, i === 0 ? " ⎿ " : " "), /* @__PURE__ */ import_react26.default.createElement(Text, {
|
|
46884
|
+
dimColor: true,
|
|
46885
|
+
color: errorColor
|
|
46886
|
+
}, 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, {
|
|
46887
|
+
dimColor: true
|
|
46888
|
+
}, "… +", r.truncated, " lines")) : null);
|
|
45828
46889
|
}
|
|
45829
46890
|
|
|
45830
46891
|
// src/components/settings-modal.tsx
|
|
45831
|
-
var
|
|
46892
|
+
var import_react29 = __toESM(require_react(), 1);
|
|
45832
46893
|
|
|
45833
46894
|
// src/components/multiline-input.tsx
|
|
45834
46895
|
var import_react27 = __toESM(require_react(), 1);
|
|
@@ -46033,6 +47094,125 @@ function MultilineDisplay({
|
|
|
46033
47094
|
}, lines.length, " line", lines.length === 1 ? "" : "s", " · ", charCount, " chars"))));
|
|
46034
47095
|
}
|
|
46035
47096
|
|
|
47097
|
+
// src/components/session-picker.tsx
|
|
47098
|
+
var import_react28 = __toESM(require_react(), 1);
|
|
47099
|
+
function shortId2(id) {
|
|
47100
|
+
if (id.length <= 12)
|
|
47101
|
+
return id;
|
|
47102
|
+
return id.slice(0, 8) + "..." + id.slice(-4);
|
|
47103
|
+
}
|
|
47104
|
+
function SessionPicker({ sessions, onPick, onCancel }) {
|
|
47105
|
+
const [cursor, setCursor] = import_react28.useState(0);
|
|
47106
|
+
const [search, setSearch] = import_react28.useState("");
|
|
47107
|
+
const [searching, setSearching] = import_react28.useState(false);
|
|
47108
|
+
const filtered = search ? sessions.filter((s) => {
|
|
47109
|
+
const haystack = [
|
|
47110
|
+
s.id,
|
|
47111
|
+
s.first_message ?? "",
|
|
47112
|
+
s.started_at ?? "",
|
|
47113
|
+
s.last_activity_at ?? ""
|
|
47114
|
+
].join(" ").toLowerCase();
|
|
47115
|
+
return haystack.includes(search.toLowerCase());
|
|
47116
|
+
}) : sessions;
|
|
47117
|
+
use_input_default((input, key) => {
|
|
47118
|
+
if (searching) {
|
|
47119
|
+
if (key.escape) {
|
|
47120
|
+
setSearching(false);
|
|
47121
|
+
setSearch("");
|
|
47122
|
+
return;
|
|
47123
|
+
}
|
|
47124
|
+
if (key.return) {
|
|
47125
|
+
setSearching(false);
|
|
47126
|
+
return;
|
|
47127
|
+
}
|
|
47128
|
+
if (key.backspace || key.delete) {
|
|
47129
|
+
setSearch((s) => s.slice(0, -1));
|
|
47130
|
+
return;
|
|
47131
|
+
}
|
|
47132
|
+
if (input && !key.ctrl && !key.meta) {
|
|
47133
|
+
setSearch((s) => s + input);
|
|
47134
|
+
}
|
|
47135
|
+
return;
|
|
47136
|
+
}
|
|
47137
|
+
if (key.escape) {
|
|
47138
|
+
onCancel();
|
|
47139
|
+
return;
|
|
47140
|
+
}
|
|
47141
|
+
if (key.upArrow) {
|
|
47142
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
47143
|
+
return;
|
|
47144
|
+
}
|
|
47145
|
+
if (key.downArrow) {
|
|
47146
|
+
setCursor((c) => Math.min(filtered.length - 1, c + 1));
|
|
47147
|
+
return;
|
|
47148
|
+
}
|
|
47149
|
+
if (key.return) {
|
|
47150
|
+
const target = filtered[cursor];
|
|
47151
|
+
if (target)
|
|
47152
|
+
onPick(target);
|
|
47153
|
+
return;
|
|
47154
|
+
}
|
|
47155
|
+
if (input === "/") {
|
|
47156
|
+
setSearching(true);
|
|
47157
|
+
}
|
|
47158
|
+
});
|
|
47159
|
+
if (sessions.length === 0) {
|
|
47160
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47161
|
+
flexDirection: "column",
|
|
47162
|
+
padding: 1
|
|
47163
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47164
|
+
color: "yellow"
|
|
47165
|
+
}, "No Claude Code sessions found in this project."), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47166
|
+
dimColor: true
|
|
47167
|
+
}, "Start `claude` in this project first, then run `cc-zh-watcher` again."), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47168
|
+
dimColor: true
|
|
47169
|
+
}, "Press Esc to exit."));
|
|
47170
|
+
}
|
|
47171
|
+
const visible = filtered.slice(0, 8);
|
|
47172
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47173
|
+
flexDirection: "column"
|
|
47174
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47175
|
+
borderStyle: "round",
|
|
47176
|
+
borderColor: "cyan",
|
|
47177
|
+
flexDirection: "column",
|
|
47178
|
+
paddingX: 1
|
|
47179
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47180
|
+
bold: true,
|
|
47181
|
+
color: "cyan"
|
|
47182
|
+
}, "Select a Claude Code session to watch"), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47183
|
+
dimColor: true
|
|
47184
|
+
}, "project: ", process.cwd(), " · ", sessions.length, " session", sessions.length === 1 ? "" : "s"), /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47185
|
+
marginTop: 1,
|
|
47186
|
+
flexDirection: "column"
|
|
47187
|
+
}, visible.map((s, i) => {
|
|
47188
|
+
const selected = i === cursor;
|
|
47189
|
+
return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47190
|
+
key: s.id,
|
|
47191
|
+
flexDirection: "column",
|
|
47192
|
+
marginBottom: 1
|
|
47193
|
+
}, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47194
|
+
color: selected ? "cyan" : undefined,
|
|
47195
|
+
bold: selected
|
|
47196
|
+
}, 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, {
|
|
47197
|
+
dimColor: true
|
|
47198
|
+
}, " started: ", formatLocalTime(s.started_at)), s.first_message ? /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47199
|
+
dimColor: true
|
|
47200
|
+
}, " first msg: ", JSON.stringify(s.first_message)) : null);
|
|
47201
|
+
}), filtered.length > visible.length ? /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47202
|
+
dimColor: true
|
|
47203
|
+
}, "... and ", filtered.length - visible.length, " more (use / to search)") : null), /* @__PURE__ */ import_react28.default.createElement(Box_default, {
|
|
47204
|
+
marginTop: 1
|
|
47205
|
+
}, searching ? /* @__PURE__ */ import_react28.default.createElement(Text, null, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47206
|
+
color: "yellow"
|
|
47207
|
+
}, "/"), search, /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47208
|
+
color: "yellow"
|
|
47209
|
+
}, "_"), /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47210
|
+
dimColor: true
|
|
47211
|
+
}, " (Esc clear · Enter accept)")) : /* @__PURE__ */ import_react28.default.createElement(Text, {
|
|
47212
|
+
dimColor: true
|
|
47213
|
+
}, "[↑/↓] move [Enter] select [/] search [Esc] cancel"))));
|
|
47214
|
+
}
|
|
47215
|
+
|
|
46036
47216
|
// src/components/settings-modal.tsx
|
|
46037
47217
|
var FIELD_KEYS = [
|
|
46038
47218
|
"api_key",
|
|
@@ -46051,8 +47231,16 @@ var PROMPT_LABEL = {
|
|
|
46051
47231
|
"zh-to-en-with-context": "zh-to-en-w/ctx",
|
|
46052
47232
|
"en-to-zh": "en-to-zh"
|
|
46053
47233
|
};
|
|
46054
|
-
var
|
|
46055
|
-
var
|
|
47234
|
+
var ROW_SESSION_END = 1;
|
|
47235
|
+
var ROW_API_END = ROW_SESSION_END + FIELD_KEYS.length;
|
|
47236
|
+
var ROW_NOTIFY_END = ROW_API_END + 1;
|
|
47237
|
+
var ROW_THINKING_END = ROW_API_END + 2;
|
|
47238
|
+
var ROW_TODOS_END = ROW_API_END + 3;
|
|
47239
|
+
var ROW_AGENT_END = ROW_API_END + 4;
|
|
47240
|
+
var ROW_CONTEXT_END = ROW_API_END + 5;
|
|
47241
|
+
var TOGGLE_ROW_COUNT = 6;
|
|
47242
|
+
var ROW_TOGGLES_END = ROW_API_END + TOGGLE_ROW_COUNT;
|
|
47243
|
+
var ROW_USER_END = ROW_TOGGLES_END + PROMPT_KEYS.length;
|
|
46056
47244
|
var ROW_DEFAULT_END = ROW_USER_END + PROMPT_KEYS.length;
|
|
46057
47245
|
var TOTAL_ROWS = ROW_DEFAULT_END;
|
|
46058
47246
|
function maskApiKey(key) {
|
|
@@ -46078,24 +47266,33 @@ function previewDefault(text, width) {
|
|
|
46078
47266
|
}
|
|
46079
47267
|
function SettingsModal({
|
|
46080
47268
|
initialConfig,
|
|
46081
|
-
onSave
|
|
47269
|
+
onSave,
|
|
47270
|
+
currentSession,
|
|
47271
|
+
projectDir,
|
|
47272
|
+
onSessionChange
|
|
46082
47273
|
}) {
|
|
46083
|
-
const [fields, setFields] =
|
|
47274
|
+
const [fields, setFields] = import_react29.useState(() => ({
|
|
46084
47275
|
api_key: initialConfig.api_key,
|
|
46085
47276
|
base_url: initialConfig.base_url,
|
|
46086
47277
|
model: initialConfig.model,
|
|
46087
47278
|
timeout_seconds: String(initialConfig.timeout_seconds),
|
|
47279
|
+
notify_on_complete: initialConfig.notify_on_complete,
|
|
47280
|
+
translate_thinking: initialConfig.translate_thinking,
|
|
47281
|
+
translate_todos: initialConfig.translate_todos,
|
|
47282
|
+
translate_agent: initialConfig.translate_agent,
|
|
47283
|
+
context_enabled: initialConfig.context_enabled,
|
|
47284
|
+
show_tool_calls: initialConfig.show_tool_calls,
|
|
46088
47285
|
prompts: {
|
|
46089
47286
|
"zh-to-en": initialConfig.prompts?.["zh-to-en"] ?? "",
|
|
46090
47287
|
"zh-to-en-with-context": initialConfig.prompts?.["zh-to-en-with-context"] ?? "",
|
|
46091
47288
|
"en-to-zh": initialConfig.prompts?.["en-to-zh"] ?? ""
|
|
46092
47289
|
}
|
|
46093
47290
|
}));
|
|
46094
|
-
const [cursor, setCursor] =
|
|
46095
|
-
const [target, setTarget] =
|
|
46096
|
-
const [editBuffer, setEditBuffer] =
|
|
46097
|
-
const [error, setError] =
|
|
46098
|
-
const [saving, setSaving] =
|
|
47291
|
+
const [cursor, setCursor] = import_react29.useState(0);
|
|
47292
|
+
const [target, setTarget] = import_react29.useState(null);
|
|
47293
|
+
const [editBuffer, setEditBuffer] = import_react29.useState("");
|
|
47294
|
+
const [error, setError] = import_react29.useState(null);
|
|
47295
|
+
const [saving, setSaving] = import_react29.useState(false);
|
|
46099
47296
|
function commitScalar(idx, value) {
|
|
46100
47297
|
const fk = FIELD_KEYS[idx];
|
|
46101
47298
|
if (!fk)
|
|
@@ -46153,17 +47350,52 @@ function SettingsModal({
|
|
|
46153
47350
|
return;
|
|
46154
47351
|
}
|
|
46155
47352
|
if (key.return) {
|
|
47353
|
+
if (cursor < ROW_SESSION_END) {
|
|
47354
|
+
setTarget({ kind: "session-pick" });
|
|
47355
|
+
setError(null);
|
|
47356
|
+
return;
|
|
47357
|
+
}
|
|
46156
47358
|
if (cursor < ROW_API_END) {
|
|
46157
|
-
const fk = FIELD_KEYS[cursor];
|
|
47359
|
+
const fk = FIELD_KEYS[cursor - ROW_SESSION_END];
|
|
46158
47360
|
if (!fk)
|
|
46159
47361
|
return;
|
|
46160
47362
|
setEditBuffer(fields[fk]);
|
|
46161
|
-
setTarget({ kind: "scalar", index: cursor });
|
|
47363
|
+
setTarget({ kind: "scalar", index: cursor - ROW_SESSION_END });
|
|
47364
|
+
setError(null);
|
|
47365
|
+
return;
|
|
47366
|
+
}
|
|
47367
|
+
if (cursor < ROW_NOTIFY_END) {
|
|
47368
|
+
setFields((f) => ({ ...f, notify_on_complete: !f.notify_on_complete }));
|
|
47369
|
+
setError(null);
|
|
47370
|
+
return;
|
|
47371
|
+
}
|
|
47372
|
+
if (cursor < ROW_THINKING_END) {
|
|
47373
|
+
setFields((f) => ({ ...f, translate_thinking: !f.translate_thinking }));
|
|
47374
|
+
setError(null);
|
|
47375
|
+
return;
|
|
47376
|
+
}
|
|
47377
|
+
if (cursor < ROW_TODOS_END) {
|
|
47378
|
+
setFields((f) => ({ ...f, translate_todos: !f.translate_todos }));
|
|
47379
|
+
setError(null);
|
|
47380
|
+
return;
|
|
47381
|
+
}
|
|
47382
|
+
if (cursor < ROW_AGENT_END) {
|
|
47383
|
+
setFields((f) => ({ ...f, translate_agent: !f.translate_agent }));
|
|
47384
|
+
setError(null);
|
|
47385
|
+
return;
|
|
47386
|
+
}
|
|
47387
|
+
if (cursor < ROW_CONTEXT_END) {
|
|
47388
|
+
setFields((f) => ({ ...f, context_enabled: !f.context_enabled }));
|
|
47389
|
+
setError(null);
|
|
47390
|
+
return;
|
|
47391
|
+
}
|
|
47392
|
+
if (cursor < ROW_TOGGLES_END) {
|
|
47393
|
+
setFields((f) => ({ ...f, show_tool_calls: !f.show_tool_calls }));
|
|
46162
47394
|
setError(null);
|
|
46163
47395
|
return;
|
|
46164
47396
|
}
|
|
46165
47397
|
if (cursor < ROW_USER_END) {
|
|
46166
|
-
const idx2 = cursor -
|
|
47398
|
+
const idx2 = cursor - ROW_TOGGLES_END;
|
|
46167
47399
|
setTarget({ kind: "user-prompt", rowIndex: idx2 });
|
|
46168
47400
|
setError(null);
|
|
46169
47401
|
return;
|
|
@@ -46190,9 +47422,9 @@ function SettingsModal({
|
|
|
46190
47422
|
const direction = PROMPT_KEYS[target.rowIndex];
|
|
46191
47423
|
if (!direction) {
|
|
46192
47424
|
setTarget(null);
|
|
46193
|
-
return /* @__PURE__ */
|
|
47425
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null);
|
|
46194
47426
|
}
|
|
46195
|
-
return /* @__PURE__ */
|
|
47427
|
+
return /* @__PURE__ */ import_react29.default.createElement(MultilineInput, {
|
|
46196
47428
|
title: `user.${direction}`,
|
|
46197
47429
|
initial: fields.prompts[direction],
|
|
46198
47430
|
onSubmit: (value) => {
|
|
@@ -46206,138 +47438,347 @@ function SettingsModal({
|
|
|
46206
47438
|
const direction = PROMPT_KEYS[target.rowIndex];
|
|
46207
47439
|
if (!direction) {
|
|
46208
47440
|
setTarget(null);
|
|
46209
|
-
return /* @__PURE__ */
|
|
47441
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null);
|
|
46210
47442
|
}
|
|
46211
|
-
return /* @__PURE__ */
|
|
47443
|
+
return /* @__PURE__ */ import_react29.default.createElement(MultilineDisplay, {
|
|
46212
47444
|
title: `default.${direction}`,
|
|
46213
47445
|
text: basePrompt(direction),
|
|
46214
47446
|
onClose: () => setTarget(null)
|
|
46215
47447
|
});
|
|
46216
47448
|
}
|
|
46217
|
-
|
|
47449
|
+
if (target?.kind === "session-pick") {
|
|
47450
|
+
const sessions = listSessions(projectDir);
|
|
47451
|
+
return /* @__PURE__ */ import_react29.default.createElement(SessionPicker, {
|
|
47452
|
+
sessions,
|
|
47453
|
+
onPick: (s) => {
|
|
47454
|
+
setTarget(null);
|
|
47455
|
+
if (s.id !== currentSession.id) {
|
|
47456
|
+
onSessionChange(s);
|
|
47457
|
+
}
|
|
47458
|
+
},
|
|
47459
|
+
onCancel: () => setTarget(null)
|
|
47460
|
+
});
|
|
47461
|
+
}
|
|
47462
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46218
47463
|
flexDirection: "column",
|
|
46219
47464
|
padding: 1
|
|
46220
|
-
}, /* @__PURE__ */
|
|
47465
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46221
47466
|
borderStyle: "round",
|
|
46222
47467
|
borderColor: "cyan",
|
|
46223
47468
|
paddingX: 1,
|
|
46224
47469
|
flexDirection: "column"
|
|
46225
|
-
}, /* @__PURE__ */
|
|
47470
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46226
47471
|
bold: true,
|
|
46227
47472
|
color: "cyan"
|
|
46228
|
-
}, "Settings"), /* @__PURE__ */
|
|
47473
|
+
}, "Settings"), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46229
47474
|
dimColor: true
|
|
46230
|
-
}, "↑/↓ select · Enter edit · Esc save & close"), /* @__PURE__ */
|
|
47475
|
+
}, "↑/↓ select · Enter edit · Esc save & close"), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46231
47476
|
marginTop: 1,
|
|
46232
47477
|
flexDirection: "column"
|
|
46233
|
-
},
|
|
46234
|
-
const selected = cursor ===
|
|
47478
|
+
}, (() => {
|
|
47479
|
+
const selected = cursor === 0;
|
|
47480
|
+
const sid = currentSession.id;
|
|
47481
|
+
const short = sid.length > 12 ? sid.slice(0, 8) + "…" + sid.slice(-4) : sid;
|
|
47482
|
+
const turns = currentSession.turn_count;
|
|
47483
|
+
const active = relativeTime(currentSession.last_activity_at);
|
|
47484
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47485
|
+
color: selected ? "cyan" : undefined,
|
|
47486
|
+
bold: selected
|
|
47487
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47488
|
+
width: 20
|
|
47489
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47490
|
+
color: selected ? "cyan" : undefined
|
|
47491
|
+
}, "session:")), /* @__PURE__ */ import_react29.default.createElement(Text, null, short, " ", /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47492
|
+
dimColor: true
|
|
47493
|
+
}, "(", turns, " turn", turns === 1 ? "" : "s", " · active ", active, "; Enter 切换)")));
|
|
47494
|
+
})(), FIELD_KEYS.map((fk, i) => {
|
|
47495
|
+
const rowIdx = ROW_SESSION_END + i;
|
|
47496
|
+
const selected = cursor === rowIdx;
|
|
46235
47497
|
const editingThis = selected && target?.kind === "scalar" && target.index === i;
|
|
46236
|
-
return /* @__PURE__ */
|
|
47498
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46237
47499
|
key: fk
|
|
46238
|
-
}, /* @__PURE__ */
|
|
47500
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46239
47501
|
color: selected ? "cyan" : undefined,
|
|
46240
47502
|
bold: selected
|
|
46241
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47503
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46242
47504
|
width: 20
|
|
46243
|
-
}, /* @__PURE__ */
|
|
47505
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46244
47506
|
color: selected ? "cyan" : undefined
|
|
46245
|
-
}, FIELD_LABELS[fk], ":")), editingThis ? /* @__PURE__ */
|
|
47507
|
+
}, FIELD_LABELS[fk], ":")), editingThis ? /* @__PURE__ */ import_react29.default.createElement(build_default, {
|
|
46246
47508
|
value: editBuffer,
|
|
46247
47509
|
onChange: setEditBuffer,
|
|
46248
47510
|
onSubmit: handleScalarSubmit
|
|
46249
|
-
}) : /* @__PURE__ */
|
|
46250
|
-
}), /* @__PURE__ */
|
|
47511
|
+
}) : /* @__PURE__ */ import_react29.default.createElement(Text, null, fk === "api_key" ? maskApiKey(fields[fk]) : fields[fk]));
|
|
47512
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46251
47513
|
marginTop: 1
|
|
46252
|
-
}, /* @__PURE__ */
|
|
47514
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47515
|
+
dimColor: true
|
|
47516
|
+
}, "── Toggles ──")), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47517
|
+
rowIdx: ROW_API_END,
|
|
47518
|
+
cursor,
|
|
47519
|
+
label: "notify_on_complete",
|
|
47520
|
+
on: fields.notify_on_complete,
|
|
47521
|
+
hintOn: "(终端响铃;Enter 切换)",
|
|
47522
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47523
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47524
|
+
rowIdx: ROW_NOTIFY_END,
|
|
47525
|
+
cursor,
|
|
47526
|
+
label: "translate_thinking",
|
|
47527
|
+
on: fields.translate_thinking,
|
|
47528
|
+
hintOn: "(译 thinking 块,多花 API;Enter 切换)",
|
|
47529
|
+
hintOff: "(默认关;Enter 切换)"
|
|
47530
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47531
|
+
rowIdx: ROW_THINKING_END,
|
|
47532
|
+
cursor,
|
|
47533
|
+
label: "translate_todos",
|
|
47534
|
+
on: fields.translate_todos,
|
|
47535
|
+
hintOn: "(译 TodoWrite 计划;Enter 切换)",
|
|
47536
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47537
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47538
|
+
rowIdx: ROW_TODOS_END,
|
|
47539
|
+
cursor,
|
|
47540
|
+
label: "translate_agent",
|
|
47541
|
+
on: fields.translate_agent,
|
|
47542
|
+
hintOn: "(译 Agent/Task 子代理;Enter 切换)",
|
|
47543
|
+
hintOff: "(已关闭;Enter 切换)"
|
|
47544
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47545
|
+
rowIdx: ROW_AGENT_END,
|
|
47546
|
+
cursor,
|
|
47547
|
+
label: "context_enabled",
|
|
47548
|
+
on: fields.context_enabled,
|
|
47549
|
+
hintOn: "(zh→en 用 Claude 上一条作 context;Enter 切换)",
|
|
47550
|
+
hintOff: "(字面翻译不带 context;Enter 切换)"
|
|
47551
|
+
}), /* @__PURE__ */ import_react29.default.createElement(ToggleRow, {
|
|
47552
|
+
rowIdx: ROW_CONTEXT_END,
|
|
47553
|
+
cursor,
|
|
47554
|
+
label: "show_tool_calls",
|
|
47555
|
+
on: fields.show_tool_calls,
|
|
47556
|
+
hintOn: "(显示原始 tool 调用 + 结果(CC 同款);Enter 切换)",
|
|
47557
|
+
hintOff: "(只看翻译内容;Enter 切换)"
|
|
47558
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47559
|
+
marginTop: 1
|
|
47560
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46253
47561
|
dimColor: true
|
|
46254
47562
|
}, "── User prompt (empty = use default) ──")), PROMPT_KEYS.map((dir, i) => {
|
|
46255
|
-
const rowIdx =
|
|
47563
|
+
const rowIdx = ROW_TOGGLES_END + i;
|
|
46256
47564
|
const selected = cursor === rowIdx;
|
|
46257
47565
|
const value = fields.prompts[dir];
|
|
46258
|
-
return /* @__PURE__ */
|
|
47566
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46259
47567
|
key: `user-${dir}`
|
|
46260
|
-
}, /* @__PURE__ */
|
|
47568
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46261
47569
|
color: selected ? "cyan" : undefined,
|
|
46262
47570
|
bold: selected
|
|
46263
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47571
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46264
47572
|
width: 20
|
|
46265
|
-
}, /* @__PURE__ */
|
|
47573
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46266
47574
|
color: selected ? "cyan" : undefined
|
|
46267
|
-
}, "user.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */
|
|
47575
|
+
}, "user.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46268
47576
|
dimColor: !value
|
|
46269
47577
|
}, previewPrompt(value, 60)));
|
|
46270
|
-
}), /* @__PURE__ */
|
|
47578
|
+
}), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46271
47579
|
marginTop: 1
|
|
46272
|
-
}, /* @__PURE__ */
|
|
47580
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46273
47581
|
dimColor: true
|
|
46274
47582
|
}, "── Default prompt (read-only) ──")), PROMPT_KEYS.map((dir, i) => {
|
|
46275
47583
|
const rowIdx = ROW_USER_END + i;
|
|
46276
47584
|
const selected = cursor === rowIdx;
|
|
46277
|
-
return /* @__PURE__ */
|
|
47585
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46278
47586
|
key: `default-${dir}`
|
|
46279
|
-
}, /* @__PURE__ */
|
|
47587
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46280
47588
|
color: selected ? "cyan" : undefined,
|
|
46281
47589
|
bold: selected
|
|
46282
|
-
}, selected ? "▸ " : " "), /* @__PURE__ */
|
|
47590
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46283
47591
|
width: 20
|
|
46284
|
-
}, /* @__PURE__ */
|
|
47592
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46285
47593
|
color: selected ? "cyan" : undefined,
|
|
46286
47594
|
dimColor: true
|
|
46287
|
-
}, "default.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */
|
|
47595
|
+
}, "default.", PROMPT_LABEL[dir], ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46288
47596
|
dimColor: true
|
|
46289
47597
|
}, previewDefault(basePrompt(dir), 60)));
|
|
46290
|
-
})), error ? /* @__PURE__ */
|
|
47598
|
+
})), error ? /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46291
47599
|
marginTop: 1
|
|
46292
|
-
}, /* @__PURE__ */
|
|
47600
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46293
47601
|
color: "red"
|
|
46294
|
-
}, "✗ ", error)) : null, saving ? /* @__PURE__ */
|
|
47602
|
+
}, "✗ ", error)) : null, saving ? /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
46295
47603
|
marginTop: 1
|
|
46296
|
-
}, /* @__PURE__ */
|
|
47604
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
46297
47605
|
color: "yellow"
|
|
46298
47606
|
}, "⏳ saving…")) : null));
|
|
46299
47607
|
}
|
|
47608
|
+
function ToggleRow({
|
|
47609
|
+
rowIdx,
|
|
47610
|
+
cursor,
|
|
47611
|
+
label,
|
|
47612
|
+
on,
|
|
47613
|
+
hintOn,
|
|
47614
|
+
hintOff
|
|
47615
|
+
}) {
|
|
47616
|
+
const selected = cursor === rowIdx;
|
|
47617
|
+
return /* @__PURE__ */ import_react29.default.createElement(Box_default, null, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47618
|
+
color: selected ? "cyan" : undefined,
|
|
47619
|
+
bold: selected
|
|
47620
|
+
}, selected ? "▸ " : " "), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
|
|
47621
|
+
width: 22
|
|
47622
|
+
}, /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47623
|
+
color: selected ? "cyan" : undefined
|
|
47624
|
+
}, label, ":")), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47625
|
+
color: on ? "green" : "gray",
|
|
47626
|
+
bold: true
|
|
47627
|
+
}, on ? "on" : "off"), /* @__PURE__ */ import_react29.default.createElement(Text, {
|
|
47628
|
+
dimColor: true
|
|
47629
|
+
}, " ", on ? hintOn : hintOff));
|
|
47630
|
+
}
|
|
46300
47631
|
|
|
46301
47632
|
// src/app.tsx
|
|
46302
47633
|
function App2({
|
|
46303
|
-
session,
|
|
47634
|
+
session: initialSession,
|
|
46304
47635
|
config: initialConfig,
|
|
46305
47636
|
projectDir
|
|
46306
47637
|
}) {
|
|
46307
47638
|
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
|
|
47639
|
+
const [session, setSession] = import_react30.useState(() => initialSession);
|
|
47640
|
+
const [config, setConfig] = import_react30.useState(() => initialConfig);
|
|
47641
|
+
const [showSettings, setShowSettings] = import_react30.useState(false);
|
|
47642
|
+
const [appState, setAppState] = import_react30.useState("idle");
|
|
47643
|
+
const [chineseInput, setChineseInput] = import_react30.useState("");
|
|
47644
|
+
const [lastEnglish, setLastEnglish] = import_react30.useState("");
|
|
47645
|
+
const [error, setError] = import_react30.useState(null);
|
|
47646
|
+
const [statusLine, setStatusLine] = import_react30.useState(undefined);
|
|
47647
|
+
const [outputEntries, setOutputEntries] = import_react30.useState([]);
|
|
47648
|
+
const [streamingOutputs, setStreamingOutputs] = import_react30.useState([]);
|
|
47649
|
+
const [tailError, setTailError] = import_react30.useState(null);
|
|
47650
|
+
const [streamingEnglish, setStreamingEnglish] = import_react30.useState("");
|
|
47651
|
+
const [pendingConcern, setPendingConcern] = import_react30.useState(null);
|
|
47652
|
+
const translatedInputRef = import_react30.useRef("");
|
|
47653
|
+
const tailRef = import_react30.useRef(null);
|
|
47654
|
+
const seenIdsRef = import_react30.useRef(new Set);
|
|
47655
|
+
const enToZhQueueRef = import_react30.useRef(null);
|
|
47656
|
+
if (enToZhQueueRef.current === null) {
|
|
47657
|
+
enToZhQueueRef.current = new SerialQueue;
|
|
47658
|
+
}
|
|
47659
|
+
const notifyEnabledRef = import_react30.useRef(initialConfig.notify_on_complete);
|
|
47660
|
+
import_react30.useEffect(() => {
|
|
47661
|
+
notifyEnabledRef.current = config.notify_on_complete;
|
|
47662
|
+
}, [config.notify_on_complete]);
|
|
47663
|
+
const translateThinkingRef = import_react30.useRef(initialConfig.translate_thinking);
|
|
47664
|
+
import_react30.useEffect(() => {
|
|
47665
|
+
translateThinkingRef.current = config.translate_thinking;
|
|
47666
|
+
}, [config.translate_thinking]);
|
|
47667
|
+
const translateTodosRef = import_react30.useRef(initialConfig.translate_todos);
|
|
47668
|
+
import_react30.useEffect(() => {
|
|
47669
|
+
translateTodosRef.current = config.translate_todos;
|
|
47670
|
+
}, [config.translate_todos]);
|
|
47671
|
+
const translateAgentRef = import_react30.useRef(initialConfig.translate_agent);
|
|
47672
|
+
import_react30.useEffect(() => {
|
|
47673
|
+
translateAgentRef.current = config.translate_agent;
|
|
47674
|
+
}, [config.translate_agent]);
|
|
47675
|
+
const showToolCallsRef = import_react30.useRef(initialConfig.show_tool_calls);
|
|
47676
|
+
import_react30.useEffect(() => {
|
|
47677
|
+
showToolCallsRef.current = config.show_tool_calls;
|
|
47678
|
+
}, [config.show_tool_calls]);
|
|
47679
|
+
const bellRef = import_react30.useRef(null);
|
|
47680
|
+
if (bellRef.current === null) {
|
|
47681
|
+
bellRef.current = new DebouncedBell(2000, () => {
|
|
47682
|
+
if (notifyEnabledRef.current)
|
|
47683
|
+
ringBell();
|
|
47684
|
+
});
|
|
47685
|
+
}
|
|
47686
|
+
import_react30.useEffect(() => {
|
|
47687
|
+
const bell = bellRef.current;
|
|
47688
|
+
return () => {
|
|
47689
|
+
bell?.cancel();
|
|
47690
|
+
};
|
|
47691
|
+
}, []);
|
|
47692
|
+
const [lastAssistantText, setLastAssistantText] = import_react30.useState("");
|
|
47693
|
+
const [lastRequest, setLastRequest] = import_react30.useState(null);
|
|
47694
|
+
const [sessionMetrics, setSessionMetrics] = import_react30.useState({
|
|
46324
47695
|
started_at: Date.now(),
|
|
46325
47696
|
total_calls: 0,
|
|
46326
47697
|
successful_calls: 0,
|
|
46327
47698
|
error_count: 0,
|
|
46328
47699
|
total_tokens: 0
|
|
46329
47700
|
});
|
|
46330
|
-
|
|
46331
|
-
const historyCursorRef = import_react29.useRef(-1);
|
|
46332
|
-
const HISTORY_MAX = 50;
|
|
46333
|
-
import_react29.useEffect(() => {
|
|
47701
|
+
import_react30.useEffect(() => {
|
|
46334
47702
|
const tail2 = new TranscriptTail(session.path, {
|
|
46335
|
-
|
|
47703
|
+
onContent: (ev) => {
|
|
46336
47704
|
if (seenIdsRef.current.has(ev.id))
|
|
46337
47705
|
return;
|
|
46338
47706
|
seenIdsRef.current.add(ev.id);
|
|
46339
|
-
|
|
46340
|
-
|
|
47707
|
+
bellRef.current?.schedule();
|
|
47708
|
+
if (ev.kind === "text") {
|
|
47709
|
+
setLastAssistantText(ev.text);
|
|
47710
|
+
}
|
|
47711
|
+
if (ev.kind === "thinking" && !translateThinkingRef.current) {
|
|
47712
|
+
return;
|
|
47713
|
+
}
|
|
47714
|
+
if (ev.kind === "question") {
|
|
47715
|
+
const seed = seedTranslatedPayload(ev.question);
|
|
47716
|
+
setStreamingOutputs((arr) => [
|
|
47717
|
+
...arr,
|
|
47718
|
+
{
|
|
47719
|
+
id: ev.id,
|
|
47720
|
+
timestamp: ev.timestamp,
|
|
47721
|
+
kind: "question",
|
|
47722
|
+
englishQuestion: ev.question,
|
|
47723
|
+
chineseQuestion: seed
|
|
47724
|
+
}
|
|
47725
|
+
]);
|
|
47726
|
+
enToZhQueueRef.current.enqueue(() => translateQuestionAndAppend(ev, seed));
|
|
47727
|
+
return;
|
|
47728
|
+
}
|
|
47729
|
+
if (ev.kind === "todo") {
|
|
47730
|
+
if (!translateTodosRef.current)
|
|
47731
|
+
return;
|
|
47732
|
+
const seed = seedTranslatedTodos(ev.todo);
|
|
47733
|
+
setStreamingOutputs((arr) => [
|
|
47734
|
+
...arr,
|
|
47735
|
+
{
|
|
47736
|
+
id: ev.id,
|
|
47737
|
+
timestamp: ev.timestamp,
|
|
47738
|
+
kind: "todo",
|
|
47739
|
+
englishTodo: ev.todo,
|
|
47740
|
+
chineseTodo: seed
|
|
47741
|
+
}
|
|
47742
|
+
]);
|
|
47743
|
+
enToZhQueueRef.current.enqueue(() => translateTodoAndAppend(ev, seed));
|
|
47744
|
+
return;
|
|
47745
|
+
}
|
|
47746
|
+
if (ev.kind === "agent") {
|
|
47747
|
+
if (!translateAgentRef.current)
|
|
47748
|
+
return;
|
|
47749
|
+
const seed = seedTranslatedAgent(ev.agent);
|
|
47750
|
+
setStreamingOutputs((arr) => [
|
|
47751
|
+
...arr,
|
|
47752
|
+
{
|
|
47753
|
+
id: ev.id,
|
|
47754
|
+
timestamp: ev.timestamp,
|
|
47755
|
+
kind: "agent",
|
|
47756
|
+
englishAgent: ev.agent,
|
|
47757
|
+
chineseAgent: seed
|
|
47758
|
+
}
|
|
47759
|
+
]);
|
|
47760
|
+
enToZhQueueRef.current.enqueue(() => translateAgentAndAppend(ev, seed));
|
|
47761
|
+
return;
|
|
47762
|
+
}
|
|
47763
|
+
if (ev.kind === "tool_use" || ev.kind === "tool_result") {
|
|
47764
|
+
if (!showToolCallsRef.current)
|
|
47765
|
+
return;
|
|
47766
|
+
enToZhQueueRef.current.enqueue(async () => {
|
|
47767
|
+
appendRawToolEntry(ev);
|
|
47768
|
+
});
|
|
47769
|
+
return;
|
|
47770
|
+
}
|
|
47771
|
+
setStreamingOutputs((arr) => [
|
|
47772
|
+
...arr,
|
|
47773
|
+
{
|
|
47774
|
+
id: ev.id,
|
|
47775
|
+
timestamp: ev.timestamp,
|
|
47776
|
+
kind: ev.kind,
|
|
47777
|
+
english: ev.text,
|
|
47778
|
+
chineseSoFar: ""
|
|
47779
|
+
}
|
|
47780
|
+
]);
|
|
47781
|
+
enToZhQueueRef.current.enqueue(() => translateAndAppend(ev));
|
|
46341
47782
|
},
|
|
46342
47783
|
onError: (err) => {
|
|
46343
47784
|
setTailError(err.message);
|
|
@@ -46355,46 +47796,334 @@ function App2({
|
|
|
46355
47796
|
};
|
|
46356
47797
|
}, [session.path]);
|
|
46357
47798
|
async function translateAndAppend(ev) {
|
|
46358
|
-
setPendingOutputCount((c) => c + 1);
|
|
46359
47799
|
markRequestStarted("en-to-zh");
|
|
46360
47800
|
try {
|
|
46361
|
-
const result2 = await translate(ev.text, "en-to-zh", config
|
|
47801
|
+
const result2 = await translate(ev.text, "en-to-zh", config, {
|
|
47802
|
+
onDelta: (delta) => {
|
|
47803
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && (e.kind === "text" || e.kind === "thinking") ? { ...e, chineseSoFar: e.chineseSoFar + delta } : e));
|
|
47804
|
+
}
|
|
47805
|
+
});
|
|
46362
47806
|
appendHistory(projectDir, {
|
|
46363
47807
|
timestamp: new Date().toISOString(),
|
|
46364
47808
|
direction: "en-to-zh",
|
|
46365
47809
|
input: ev.text,
|
|
46366
47810
|
output: result2.content
|
|
46367
47811
|
});
|
|
47812
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47813
|
+
setOutputEntries((entries) => [
|
|
47814
|
+
...entries,
|
|
47815
|
+
{
|
|
47816
|
+
id: ev.id,
|
|
47817
|
+
timestamp: ev.timestamp,
|
|
47818
|
+
kind: ev.kind,
|
|
47819
|
+
english: ev.text,
|
|
47820
|
+
chinese: result2.content,
|
|
47821
|
+
state: "translated"
|
|
47822
|
+
}
|
|
47823
|
+
]);
|
|
47824
|
+
markRequestSuccess("en-to-zh", result2.elapsed_ms, result2.usage?.total_tokens ?? null);
|
|
47825
|
+
} catch (err) {
|
|
47826
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47827
|
+
const msg = te?.message ?? err.message;
|
|
47828
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47829
|
+
setOutputEntries((entries) => [
|
|
47830
|
+
...entries,
|
|
47831
|
+
{
|
|
47832
|
+
id: ev.id,
|
|
47833
|
+
timestamp: ev.timestamp,
|
|
47834
|
+
kind: ev.kind,
|
|
47835
|
+
english: ev.text,
|
|
47836
|
+
chinese: "",
|
|
47837
|
+
state: "error",
|
|
47838
|
+
error: msg
|
|
47839
|
+
}
|
|
47840
|
+
]);
|
|
47841
|
+
markRequestError("en-to-zh", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
47842
|
+
}
|
|
47843
|
+
}
|
|
47844
|
+
async function translateQuestionAndAppend(ev, seed) {
|
|
47845
|
+
markRequestStarted("en-to-zh");
|
|
47846
|
+
let totalElapsed = 0;
|
|
47847
|
+
let totalTokens = 0;
|
|
47848
|
+
const pending = [];
|
|
47849
|
+
for (let qi = 0;qi < seed.questions.length; qi += 1) {
|
|
47850
|
+
const q = seed.questions[qi];
|
|
47851
|
+
if (!q.question.zh) {
|
|
47852
|
+
pending.push({
|
|
47853
|
+
en: q.question.en,
|
|
47854
|
+
patch: (zh, cur) => patchField(cur, qi, "question", null, zh)
|
|
47855
|
+
});
|
|
47856
|
+
}
|
|
47857
|
+
for (let oi = 0;oi < q.options.length; oi += 1) {
|
|
47858
|
+
const opt = q.options[oi];
|
|
47859
|
+
if (!opt.label.zh) {
|
|
47860
|
+
pending.push({
|
|
47861
|
+
en: opt.label.en,
|
|
47862
|
+
patch: (zh, cur) => patchField(cur, qi, "label", oi, zh)
|
|
47863
|
+
});
|
|
47864
|
+
}
|
|
47865
|
+
if (!opt.description.zh) {
|
|
47866
|
+
pending.push({
|
|
47867
|
+
en: opt.description.en,
|
|
47868
|
+
patch: (zh, cur) => patchField(cur, qi, "description", oi, zh)
|
|
47869
|
+
});
|
|
47870
|
+
}
|
|
47871
|
+
}
|
|
47872
|
+
}
|
|
47873
|
+
try {
|
|
47874
|
+
for (const p of pending) {
|
|
47875
|
+
const result2 = await translate(p.en, "en-to-zh", config);
|
|
47876
|
+
totalElapsed += result2.elapsed_ms;
|
|
47877
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
47878
|
+
appendHistory(projectDir, {
|
|
47879
|
+
timestamp: new Date().toISOString(),
|
|
47880
|
+
direction: "en-to-zh",
|
|
47881
|
+
input: p.en,
|
|
47882
|
+
output: result2.content
|
|
47883
|
+
});
|
|
47884
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "question" ? { ...e, chineseQuestion: p.patch(result2.content, e.chineseQuestion) } : e));
|
|
47885
|
+
}
|
|
47886
|
+
setStreamingOutputs((arr) => {
|
|
47887
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "question");
|
|
47888
|
+
if (finalEntry && finalEntry.kind === "question") {
|
|
47889
|
+
setOutputEntries((entries) => [
|
|
47890
|
+
...entries,
|
|
47891
|
+
{
|
|
47892
|
+
id: ev.id,
|
|
47893
|
+
timestamp: ev.timestamp,
|
|
47894
|
+
kind: "question",
|
|
47895
|
+
englishQuestion: ev.question,
|
|
47896
|
+
chineseQuestion: finalEntry.chineseQuestion,
|
|
47897
|
+
state: "translated"
|
|
47898
|
+
}
|
|
47899
|
+
]);
|
|
47900
|
+
}
|
|
47901
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
47902
|
+
});
|
|
47903
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
47904
|
+
} catch (err) {
|
|
47905
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47906
|
+
const msg = te?.message ?? err.message;
|
|
47907
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
47908
|
+
setOutputEntries((entries) => [
|
|
47909
|
+
...entries,
|
|
47910
|
+
{
|
|
47911
|
+
id: ev.id,
|
|
47912
|
+
timestamp: ev.timestamp,
|
|
47913
|
+
kind: "question",
|
|
47914
|
+
englishQuestion: ev.question,
|
|
47915
|
+
chineseQuestion: seed,
|
|
47916
|
+
state: "error",
|
|
47917
|
+
error: msg
|
|
47918
|
+
}
|
|
47919
|
+
]);
|
|
47920
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
47921
|
+
}
|
|
47922
|
+
}
|
|
47923
|
+
async function translateTodoAndAppend(ev, seed) {
|
|
47924
|
+
markRequestStarted("en-to-zh");
|
|
47925
|
+
let totalElapsed = 0;
|
|
47926
|
+
let totalTokens = 0;
|
|
47927
|
+
const pending = [];
|
|
47928
|
+
for (let i = 0;i < seed.todos.length; i += 1) {
|
|
47929
|
+
const t = seed.todos[i];
|
|
47930
|
+
const which = t.status === "in_progress" ? "activeForm" : "content";
|
|
47931
|
+
const f = which === "activeForm" ? t.activeForm : t.content;
|
|
47932
|
+
if (!f.zh) {
|
|
47933
|
+
pending.push({
|
|
47934
|
+
en: f.en,
|
|
47935
|
+
patch: (zh, cur) => patchTodoField(cur, i, which, zh)
|
|
47936
|
+
});
|
|
47937
|
+
}
|
|
47938
|
+
}
|
|
47939
|
+
try {
|
|
47940
|
+
for (const p of pending) {
|
|
47941
|
+
const result2 = await translate(p.en, "en-to-zh", config);
|
|
47942
|
+
totalElapsed += result2.elapsed_ms;
|
|
47943
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
47944
|
+
appendHistory(projectDir, {
|
|
47945
|
+
timestamp: new Date().toISOString(),
|
|
47946
|
+
direction: "en-to-zh",
|
|
47947
|
+
input: p.en,
|
|
47948
|
+
output: result2.content
|
|
47949
|
+
});
|
|
47950
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "todo" ? { ...e, chineseTodo: p.patch(result2.content, e.chineseTodo) } : e));
|
|
47951
|
+
}
|
|
47952
|
+
setStreamingOutputs((arr) => {
|
|
47953
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "todo");
|
|
47954
|
+
if (finalEntry && finalEntry.kind === "todo") {
|
|
47955
|
+
setOutputEntries((entries) => [
|
|
47956
|
+
...entries,
|
|
47957
|
+
{
|
|
47958
|
+
id: ev.id,
|
|
47959
|
+
timestamp: ev.timestamp,
|
|
47960
|
+
kind: "todo",
|
|
47961
|
+
englishTodo: ev.todo,
|
|
47962
|
+
chineseTodo: finalEntry.chineseTodo,
|
|
47963
|
+
state: "translated"
|
|
47964
|
+
}
|
|
47965
|
+
]);
|
|
47966
|
+
}
|
|
47967
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
47968
|
+
});
|
|
47969
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
47970
|
+
} catch (err) {
|
|
47971
|
+
const te = err instanceof TranslateError ? err : null;
|
|
47972
|
+
const msg = te?.message ?? err.message;
|
|
47973
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
46368
47974
|
setOutputEntries((entries) => [
|
|
46369
47975
|
...entries,
|
|
46370
47976
|
{
|
|
46371
47977
|
id: ev.id,
|
|
46372
47978
|
timestamp: ev.timestamp,
|
|
46373
|
-
|
|
46374
|
-
|
|
46375
|
-
|
|
47979
|
+
kind: "todo",
|
|
47980
|
+
englishTodo: ev.todo,
|
|
47981
|
+
chineseTodo: seed,
|
|
47982
|
+
state: "error",
|
|
47983
|
+
error: msg
|
|
46376
47984
|
}
|
|
46377
47985
|
]);
|
|
46378
|
-
|
|
47986
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
47987
|
+
}
|
|
47988
|
+
}
|
|
47989
|
+
async function translateAgentAndAppend(ev, seed) {
|
|
47990
|
+
markRequestStarted("en-to-zh");
|
|
47991
|
+
let totalElapsed = 0;
|
|
47992
|
+
let totalTokens = 0;
|
|
47993
|
+
try {
|
|
47994
|
+
if (!seed.description.zh) {
|
|
47995
|
+
const result2 = await translate(seed.description.en, "en-to-zh", config);
|
|
47996
|
+
totalElapsed += result2.elapsed_ms;
|
|
47997
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
47998
|
+
appendHistory(projectDir, {
|
|
47999
|
+
timestamp: new Date().toISOString(),
|
|
48000
|
+
direction: "en-to-zh",
|
|
48001
|
+
input: seed.description.en,
|
|
48002
|
+
output: result2.content
|
|
48003
|
+
});
|
|
48004
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "agent" ? {
|
|
48005
|
+
...e,
|
|
48006
|
+
chineseAgent: {
|
|
48007
|
+
...e.chineseAgent,
|
|
48008
|
+
description: { en: e.chineseAgent.description.en, zh: result2.content }
|
|
48009
|
+
}
|
|
48010
|
+
} : e));
|
|
48011
|
+
}
|
|
48012
|
+
if (!seed.prompt.zh) {
|
|
48013
|
+
const result2 = await translate(seed.prompt.en, "en-to-zh", config, {
|
|
48014
|
+
onDelta: (delta) => {
|
|
48015
|
+
setStreamingOutputs((arr) => arr.map((e) => e.id === ev.id && e.kind === "agent" ? {
|
|
48016
|
+
...e,
|
|
48017
|
+
chineseAgent: {
|
|
48018
|
+
...e.chineseAgent,
|
|
48019
|
+
prompt: {
|
|
48020
|
+
en: e.chineseAgent.prompt.en,
|
|
48021
|
+
zh: e.chineseAgent.prompt.zh + delta
|
|
48022
|
+
}
|
|
48023
|
+
}
|
|
48024
|
+
} : e));
|
|
48025
|
+
}
|
|
48026
|
+
});
|
|
48027
|
+
totalElapsed += result2.elapsed_ms;
|
|
48028
|
+
totalTokens += result2.usage?.total_tokens ?? 0;
|
|
48029
|
+
appendHistory(projectDir, {
|
|
48030
|
+
timestamp: new Date().toISOString(),
|
|
48031
|
+
direction: "en-to-zh",
|
|
48032
|
+
input: seed.prompt.en,
|
|
48033
|
+
output: result2.content
|
|
48034
|
+
});
|
|
48035
|
+
}
|
|
48036
|
+
setStreamingOutputs((arr) => {
|
|
48037
|
+
const finalEntry = arr.find((e) => e.id === ev.id && e.kind === "agent");
|
|
48038
|
+
if (finalEntry && finalEntry.kind === "agent") {
|
|
48039
|
+
setOutputEntries((entries) => [
|
|
48040
|
+
...entries,
|
|
48041
|
+
{
|
|
48042
|
+
id: ev.id,
|
|
48043
|
+
timestamp: ev.timestamp,
|
|
48044
|
+
kind: "agent",
|
|
48045
|
+
englishAgent: ev.agent,
|
|
48046
|
+
chineseAgent: finalEntry.chineseAgent,
|
|
48047
|
+
state: "translated"
|
|
48048
|
+
}
|
|
48049
|
+
]);
|
|
48050
|
+
}
|
|
48051
|
+
return arr.filter((e) => e.id !== ev.id);
|
|
48052
|
+
});
|
|
48053
|
+
markRequestSuccess("en-to-zh", totalElapsed, totalTokens);
|
|
46379
48054
|
} catch (err) {
|
|
46380
48055
|
const te = err instanceof TranslateError ? err : null;
|
|
46381
48056
|
const msg = te?.message ?? err.message;
|
|
48057
|
+
setStreamingOutputs((arr) => arr.filter((e) => e.id !== ev.id));
|
|
46382
48058
|
setOutputEntries((entries) => [
|
|
46383
48059
|
...entries,
|
|
46384
48060
|
{
|
|
46385
48061
|
id: ev.id,
|
|
46386
48062
|
timestamp: ev.timestamp,
|
|
46387
|
-
|
|
46388
|
-
|
|
48063
|
+
kind: "agent",
|
|
48064
|
+
englishAgent: ev.agent,
|
|
48065
|
+
chineseAgent: seed,
|
|
46389
48066
|
state: "error",
|
|
46390
48067
|
error: msg
|
|
46391
48068
|
}
|
|
46392
48069
|
]);
|
|
46393
|
-
markRequestError("en-to-zh", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
46394
|
-
}
|
|
46395
|
-
|
|
48070
|
+
markRequestError("en-to-zh", totalElapsed + (te?.elapsed_ms ?? 0), te?.code ?? "error", msg);
|
|
48071
|
+
}
|
|
48072
|
+
}
|
|
48073
|
+
function appendRawToolEntry(ev) {
|
|
48074
|
+
if (ev.kind === "tool_use") {
|
|
48075
|
+
setOutputEntries((entries) => [
|
|
48076
|
+
...entries,
|
|
48077
|
+
{
|
|
48078
|
+
id: ev.id,
|
|
48079
|
+
timestamp: ev.timestamp,
|
|
48080
|
+
kind: "tool_use",
|
|
48081
|
+
toolUse: ev.toolUse,
|
|
48082
|
+
state: "translated"
|
|
48083
|
+
}
|
|
48084
|
+
]);
|
|
48085
|
+
} else {
|
|
48086
|
+
setOutputEntries((entries) => [
|
|
48087
|
+
...entries,
|
|
48088
|
+
{
|
|
48089
|
+
id: ev.id,
|
|
48090
|
+
timestamp: ev.timestamp,
|
|
48091
|
+
kind: "tool_result",
|
|
48092
|
+
toolResult: ev.toolResult,
|
|
48093
|
+
state: "translated"
|
|
48094
|
+
}
|
|
48095
|
+
]);
|
|
46396
48096
|
}
|
|
46397
48097
|
}
|
|
48098
|
+
function handleSessionChange(next) {
|
|
48099
|
+
try {
|
|
48100
|
+
writeState(projectDir, {
|
|
48101
|
+
selected_session_id: next.id,
|
|
48102
|
+
selected_at: new Date().toISOString(),
|
|
48103
|
+
transcript_path: next.path
|
|
48104
|
+
});
|
|
48105
|
+
} catch (err) {
|
|
48106
|
+
setStatusLine(`✗ failed to persist session pick: ${err.message}`);
|
|
48107
|
+
}
|
|
48108
|
+
seenIdsRef.current.clear();
|
|
48109
|
+
setLastAssistantText("");
|
|
48110
|
+
bellRef.current?.cancel();
|
|
48111
|
+
const shortPrev = session.id.slice(0, 8);
|
|
48112
|
+
const shortNext = next.id.slice(0, 8);
|
|
48113
|
+
setOutputEntries((entries) => [
|
|
48114
|
+
...entries,
|
|
48115
|
+
{
|
|
48116
|
+
id: `__divider_${Date.now()}`,
|
|
48117
|
+
timestamp: new Date().toISOString(),
|
|
48118
|
+
kind: "text",
|
|
48119
|
+
english: "",
|
|
48120
|
+
chinese: `已切换 session: ${shortPrev}… → ${shortNext}…`,
|
|
48121
|
+
state: "translated"
|
|
48122
|
+
}
|
|
48123
|
+
]);
|
|
48124
|
+
setSession(next);
|
|
48125
|
+
setStatusLine(`✓ switched to session ${shortNext}…`);
|
|
48126
|
+
}
|
|
46398
48127
|
async function saveSettingsFromModal(s) {
|
|
46399
48128
|
const path = userConfigPath();
|
|
46400
48129
|
let existing = {};
|
|
@@ -46410,7 +48139,13 @@ function App2({
|
|
|
46410
48139
|
api_key: s.api_key,
|
|
46411
48140
|
base_url: s.base_url,
|
|
46412
48141
|
model: s.model,
|
|
46413
|
-
timeout_seconds: Number(s.timeout_seconds)
|
|
48142
|
+
timeout_seconds: Number(s.timeout_seconds),
|
|
48143
|
+
notify_on_complete: s.notify_on_complete,
|
|
48144
|
+
translate_thinking: s.translate_thinking,
|
|
48145
|
+
translate_todos: s.translate_todos,
|
|
48146
|
+
translate_agent: s.translate_agent,
|
|
48147
|
+
context_enabled: s.context_enabled,
|
|
48148
|
+
show_tool_calls: s.show_tool_calls
|
|
46414
48149
|
};
|
|
46415
48150
|
const promptsClean = stripEmpty(s.prompts);
|
|
46416
48151
|
if (promptsClean)
|
|
@@ -46418,9 +48153,8 @@ function App2({
|
|
|
46418
48153
|
else
|
|
46419
48154
|
delete merged["prompts"];
|
|
46420
48155
|
try {
|
|
46421
|
-
|
|
46422
|
-
|
|
46423
|
-
`, "utf-8");
|
|
48156
|
+
writeAtomic(path, JSON.stringify(merged, null, 2) + `
|
|
48157
|
+
`, { mode: 384 });
|
|
46424
48158
|
} catch (err) {
|
|
46425
48159
|
setStatusLine(`✗ save failed: ${err.message}`);
|
|
46426
48160
|
setShowSettings(false);
|
|
@@ -46487,63 +48221,89 @@ function App2({
|
|
|
46487
48221
|
setError(null);
|
|
46488
48222
|
setStatusLine(undefined);
|
|
46489
48223
|
setAppState("idle");
|
|
46490
|
-
|
|
48224
|
+
setPendingConcern(null);
|
|
48225
|
+
translatedInputRef.current = "";
|
|
46491
48226
|
return;
|
|
46492
48227
|
}
|
|
46493
|
-
|
|
48228
|
+
});
|
|
48229
|
+
function commitTranslation(trimmedInput, en) {
|
|
48230
|
+
try {
|
|
48231
|
+
const path = writeUserInput(projectDir, en);
|
|
48232
|
+
appendHistory(projectDir, {
|
|
48233
|
+
timestamp: new Date().toISOString(),
|
|
48234
|
+
direction: "zh-to-en",
|
|
48235
|
+
input: trimmedInput,
|
|
48236
|
+
output: en
|
|
48237
|
+
});
|
|
48238
|
+
setStreamingEnglish("");
|
|
48239
|
+
setLastEnglish(en);
|
|
48240
|
+
setStatusLine(`✓ wrote ${path} — switch to Claude Code, type /i ↵`);
|
|
46494
48241
|
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;
|
|
48242
|
+
setPendingConcern(null);
|
|
48243
|
+
translatedInputRef.current = "";
|
|
48244
|
+
setAppState("idle");
|
|
48245
|
+
} catch (err) {
|
|
48246
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
48247
|
+
setError(`failed to write user-input.md: ${msg}`);
|
|
48248
|
+
setAppState("error");
|
|
46529
48249
|
}
|
|
46530
|
-
}
|
|
48250
|
+
}
|
|
46531
48251
|
async function handleSubmit() {
|
|
46532
48252
|
const trimmed = chineseInput.trim();
|
|
46533
48253
|
if (!trimmed)
|
|
46534
48254
|
return;
|
|
46535
48255
|
if (appState !== "idle")
|
|
46536
48256
|
return;
|
|
48257
|
+
if (pendingConcern && trimmed === translatedInputRef.current) {
|
|
48258
|
+
commitTranslation(trimmed, lastEnglish);
|
|
48259
|
+
return;
|
|
48260
|
+
}
|
|
48261
|
+
if (pendingConcern) {
|
|
48262
|
+
setPendingConcern(null);
|
|
48263
|
+
translatedInputRef.current = "";
|
|
48264
|
+
}
|
|
46537
48265
|
setAppState("translating");
|
|
46538
48266
|
setError(null);
|
|
46539
48267
|
setStatusLine(undefined);
|
|
46540
48268
|
setLastEnglish("");
|
|
48269
|
+
setStreamingEnglish("");
|
|
48270
|
+
const ctx = config.context_enabled && lastAssistantText.trim() ? lastAssistantText : undefined;
|
|
48271
|
+
const useContext7 = ctx !== undefined;
|
|
48272
|
+
let buf = "";
|
|
48273
|
+
let translationStart = -1;
|
|
46541
48274
|
let en;
|
|
48275
|
+
let concern = null;
|
|
46542
48276
|
markRequestStarted("zh-to-en");
|
|
46543
48277
|
try {
|
|
46544
|
-
const
|
|
46545
|
-
|
|
48278
|
+
const result2 = await translate(trimmed, "zh-to-en", config, {
|
|
48279
|
+
contextText: ctx,
|
|
48280
|
+
parseConcernHeader: useContext7,
|
|
48281
|
+
onDelta: (delta) => {
|
|
48282
|
+
buf += delta;
|
|
48283
|
+
if (!useContext7) {
|
|
48284
|
+
setStreamingEnglish(buf);
|
|
48285
|
+
return;
|
|
48286
|
+
}
|
|
48287
|
+
if (translationStart === -1) {
|
|
48288
|
+
const nl = buf.indexOf(`
|
|
48289
|
+
`);
|
|
48290
|
+
if (nl === -1)
|
|
48291
|
+
return;
|
|
48292
|
+
const firstLine = buf.slice(0, nl).trim();
|
|
48293
|
+
if (firstLine === "OK" || firstLine.startsWith("WARN:")) {
|
|
48294
|
+
let i = nl + 1;
|
|
48295
|
+
while (i < buf.length && /\s/.test(buf[i]))
|
|
48296
|
+
i += 1;
|
|
48297
|
+
translationStart = i;
|
|
48298
|
+
} else {
|
|
48299
|
+
translationStart = 0;
|
|
48300
|
+
}
|
|
48301
|
+
}
|
|
48302
|
+
setStreamingEnglish(buf.slice(translationStart));
|
|
48303
|
+
}
|
|
48304
|
+
});
|
|
46546
48305
|
en = result2.content;
|
|
48306
|
+
concern = result2.concern ?? null;
|
|
46547
48307
|
markRequestSuccess("zh-to-en", result2.elapsed_ms, result2.usage?.total_tokens ?? null);
|
|
46548
48308
|
} catch (err) {
|
|
46549
48309
|
const te = err instanceof TranslateError ? err : null;
|
|
@@ -46551,34 +48311,20 @@ function App2({
|
|
|
46551
48311
|
markRequestError("zh-to-en", te?.elapsed_ms ?? 0, te?.code ?? "error", msg);
|
|
46552
48312
|
setError(msg);
|
|
46553
48313
|
setAppState("error");
|
|
48314
|
+
setStreamingEnglish("");
|
|
46554
48315
|
return;
|
|
46555
48316
|
}
|
|
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;
|
|
48317
|
+
if (concern) {
|
|
48318
|
+
setPendingConcern(concern);
|
|
46571
48319
|
setLastEnglish(en);
|
|
46572
|
-
|
|
46573
|
-
|
|
48320
|
+
setStreamingEnglish("");
|
|
48321
|
+
translatedInputRef.current = trimmed;
|
|
46574
48322
|
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");
|
|
48323
|
+
return;
|
|
46579
48324
|
}
|
|
48325
|
+
commitTranslation(trimmed, en);
|
|
46580
48326
|
}
|
|
46581
|
-
|
|
48327
|
+
import_react30.useEffect(() => {
|
|
46582
48328
|
if (statusLine && appState === "idle") {
|
|
46583
48329
|
const t = setTimeout(() => setStatusLine(undefined), 8000);
|
|
46584
48330
|
return () => clearTimeout(t);
|
|
@@ -46588,35 +48334,40 @@ function App2({
|
|
|
46588
48334
|
const inputDisabled = appState === "translating";
|
|
46589
48335
|
const combinedStatus = statusLine ?? (tailError ? `tail error: ${tailError}` : undefined);
|
|
46590
48336
|
if (showSettings) {
|
|
46591
|
-
return /* @__PURE__ */
|
|
48337
|
+
return /* @__PURE__ */ import_react30.default.createElement(SettingsModal, {
|
|
46592
48338
|
initialConfig: config,
|
|
46593
|
-
onSave: saveSettingsFromModal
|
|
48339
|
+
onSave: saveSettingsFromModal,
|
|
48340
|
+
currentSession: session,
|
|
48341
|
+
projectDir,
|
|
48342
|
+
onSessionChange: handleSessionChange
|
|
46594
48343
|
});
|
|
46595
48344
|
}
|
|
46596
|
-
return /* @__PURE__ */
|
|
48345
|
+
return /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement(OutputPane, {
|
|
46597
48346
|
entries: outputEntries,
|
|
46598
|
-
|
|
46599
|
-
}), /* @__PURE__ */
|
|
48347
|
+
streaming: streamingOutputs
|
|
48348
|
+
}), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
|
|
46600
48349
|
flexDirection: "column"
|
|
46601
|
-
}, /* @__PURE__ */
|
|
48350
|
+
}, /* @__PURE__ */ import_react30.default.createElement(StatusBar, {
|
|
46602
48351
|
session,
|
|
46603
48352
|
appState,
|
|
46604
48353
|
model: config.model,
|
|
46605
48354
|
statusLine: combinedStatus,
|
|
46606
|
-
contextEnabled,
|
|
48355
|
+
contextEnabled: config.context_enabled,
|
|
46607
48356
|
contextSize: lastAssistantText.length,
|
|
46608
48357
|
lastRequest,
|
|
46609
48358
|
sessionMetrics
|
|
46610
|
-
}), /* @__PURE__ */
|
|
48359
|
+
}), /* @__PURE__ */ import_react30.default.createElement(InputPane, {
|
|
46611
48360
|
value: chineseInput,
|
|
46612
48361
|
onChange: setChineseInput,
|
|
46613
48362
|
onSubmit: () => void handleSubmit(),
|
|
46614
48363
|
disabled: inputDisabled,
|
|
46615
48364
|
placeholder: inputDisabled ? "" : "输入中文,回车直接翻译并发送…"
|
|
46616
|
-
}), /* @__PURE__ */
|
|
48365
|
+
}), /* @__PURE__ */ import_react30.default.createElement(PreviewPane, {
|
|
46617
48366
|
text: appState === "idle" ? lastEnglish : "",
|
|
46618
48367
|
translating: appState === "translating",
|
|
46619
|
-
|
|
48368
|
+
streamingText: streamingEnglish,
|
|
48369
|
+
error: appState === "error" ? error : null,
|
|
48370
|
+
concern: pendingConcern
|
|
46620
48371
|
})));
|
|
46621
48372
|
}
|
|
46622
48373
|
function stripEmpty(map2) {
|
|
@@ -46627,128 +48378,144 @@ function stripEmpty(map2) {
|
|
|
46627
48378
|
}
|
|
46628
48379
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
46629
48380
|
}
|
|
46630
|
-
|
|
46631
|
-
|
|
46632
|
-
|
|
46633
|
-
|
|
46634
|
-
|
|
46635
|
-
|
|
46636
|
-
|
|
48381
|
+
function seedTranslatedPayload(raw) {
|
|
48382
|
+
const seedOption = (o) => {
|
|
48383
|
+
const out = {
|
|
48384
|
+
label: seedString(o.label),
|
|
48385
|
+
description: seedString(o.description)
|
|
48386
|
+
};
|
|
48387
|
+
if (o.preview !== undefined)
|
|
48388
|
+
out.preview = o.preview;
|
|
48389
|
+
return out;
|
|
48390
|
+
};
|
|
48391
|
+
return {
|
|
48392
|
+
questions: raw.questions.map((q) => ({
|
|
48393
|
+
question: seedString(q.question),
|
|
48394
|
+
header: q.header,
|
|
48395
|
+
multiSelect: q.multiSelect,
|
|
48396
|
+
options: q.options.map(seedOption)
|
|
48397
|
+
}))
|
|
48398
|
+
};
|
|
46637
48399
|
}
|
|
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);
|
|
48400
|
+
function seedTranslatedTodos(raw) {
|
|
48401
|
+
return {
|
|
48402
|
+
todos: raw.todos.map((t) => ({
|
|
48403
|
+
content: seedString(t.content),
|
|
48404
|
+
activeForm: seedString(t.activeForm),
|
|
48405
|
+
status: t.status
|
|
48406
|
+
}))
|
|
48407
|
+
};
|
|
48408
|
+
}
|
|
48409
|
+
function seedTranslatedAgent(raw) {
|
|
48410
|
+
return {
|
|
48411
|
+
description: seedString(raw.description),
|
|
48412
|
+
prompt: seedString(raw.prompt),
|
|
48413
|
+
subagent_type: raw.subagent_type
|
|
48414
|
+
};
|
|
48415
|
+
}
|
|
48416
|
+
function patchField(payload, qi, which, oi, zh) {
|
|
48417
|
+
return {
|
|
48418
|
+
questions: payload.questions.map((q, i) => {
|
|
48419
|
+
if (i !== qi)
|
|
48420
|
+
return q;
|
|
48421
|
+
if (which === "question") {
|
|
48422
|
+
return { ...q, question: { ...q.question, zh } };
|
|
46668
48423
|
}
|
|
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"))));
|
|
48424
|
+
return {
|
|
48425
|
+
...q,
|
|
48426
|
+
options: q.options.map((o, j) => {
|
|
48427
|
+
if (j !== oi)
|
|
48428
|
+
return o;
|
|
48429
|
+
if (which === "label")
|
|
48430
|
+
return { ...o, label: { ...o.label, zh } };
|
|
48431
|
+
return { ...o, description: { ...o.description, zh } };
|
|
48432
|
+
})
|
|
48433
|
+
};
|
|
48434
|
+
})
|
|
48435
|
+
};
|
|
48436
|
+
}
|
|
48437
|
+
function patchTodoField(payload, i, which, zh) {
|
|
48438
|
+
return {
|
|
48439
|
+
todos: payload.todos.map((t, j) => {
|
|
48440
|
+
if (j !== i)
|
|
48441
|
+
return t;
|
|
48442
|
+
if (which === "content")
|
|
48443
|
+
return { ...t, content: { ...t.content, zh } };
|
|
48444
|
+
return { ...t, activeForm: { ...t.activeForm, zh } };
|
|
48445
|
+
})
|
|
48446
|
+
};
|
|
46748
48447
|
}
|
|
48448
|
+
// package.json
|
|
48449
|
+
var package_default = {
|
|
48450
|
+
name: "cc-zh-watcher",
|
|
48451
|
+
version: "0.2.0",
|
|
48452
|
+
description: "Side-channel Chinese ↔ English bridge for Claude Code (single-watcher TUI). Type Chinese, Claude sees English; Claude replies in English, you see Chinese.",
|
|
48453
|
+
type: "module",
|
|
48454
|
+
bin: {
|
|
48455
|
+
"cc-zh-watcher": "dist/cli.js"
|
|
48456
|
+
},
|
|
48457
|
+
main: "./dist/cli.js",
|
|
48458
|
+
files: [
|
|
48459
|
+
"dist/cli.js",
|
|
48460
|
+
"README.md",
|
|
48461
|
+
"LICENSE"
|
|
48462
|
+
],
|
|
48463
|
+
scripts: {
|
|
48464
|
+
dev: "bun run src/cli.ts",
|
|
48465
|
+
build: "bun build src/cli.ts --outfile dist/cli.js --target node",
|
|
48466
|
+
compile: "bun build src/cli.ts --compile --outfile dist/cc-zh-watcher",
|
|
48467
|
+
"compile:linux-x64": "bun build src/cli.ts --compile --target=bun-linux-x64 --outfile dist/cc-zh-watcher-linux-x64",
|
|
48468
|
+
"compile:linux-arm64": "bun build src/cli.ts --compile --target=bun-linux-arm64 --outfile dist/cc-zh-watcher-linux-arm64",
|
|
48469
|
+
"compile:darwin-x64": "bun build src/cli.ts --compile --target=bun-darwin-x64 --outfile dist/cc-zh-watcher-darwin-x64",
|
|
48470
|
+
"compile:darwin-arm64": "bun build src/cli.ts --compile --target=bun-darwin-arm64 --outfile dist/cc-zh-watcher-darwin-arm64",
|
|
48471
|
+
"compile:windows-x64": "bun build src/cli.ts --compile --target=bun-windows-x64 --outfile dist/cc-zh-watcher-windows-x64.exe",
|
|
48472
|
+
"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",
|
|
48473
|
+
test: "bun test",
|
|
48474
|
+
typecheck: "tsc --noEmit",
|
|
48475
|
+
prepublishOnly: "bun run typecheck && bun test && bun run build",
|
|
48476
|
+
"install:local": "bash scripts/install-local.sh",
|
|
48477
|
+
"uninstall:local": "npm uninstall -g cc-zh-watcher"
|
|
48478
|
+
},
|
|
48479
|
+
keywords: [
|
|
48480
|
+
"claude-code",
|
|
48481
|
+
"claude",
|
|
48482
|
+
"translation",
|
|
48483
|
+
"chinese",
|
|
48484
|
+
"i18n",
|
|
48485
|
+
"tui",
|
|
48486
|
+
"ink",
|
|
48487
|
+
"cli",
|
|
48488
|
+
"openai",
|
|
48489
|
+
"deepseek"
|
|
48490
|
+
],
|
|
48491
|
+
author: "storm <xm4763@gmail.com>",
|
|
48492
|
+
license: "MIT",
|
|
48493
|
+
repository: {
|
|
48494
|
+
type: "git",
|
|
48495
|
+
url: "git+https://github.com/CodingForMoney/cc-zh-watcher.git"
|
|
48496
|
+
},
|
|
48497
|
+
homepage: "https://github.com/CodingForMoney/cc-zh-watcher/#readme",
|
|
48498
|
+
bugs: {
|
|
48499
|
+
url: "https://github.com/CodingForMoney/cc-zh-watcher/issues"
|
|
48500
|
+
},
|
|
48501
|
+
engines: {
|
|
48502
|
+
node: ">=20.0.0"
|
|
48503
|
+
},
|
|
48504
|
+
devDependencies: {
|
|
48505
|
+
"@types/node": "^20.14.0",
|
|
48506
|
+
"@types/react": "^18.3.0",
|
|
48507
|
+
"bun-types": "^1.3.13",
|
|
48508
|
+
commander: "^12.1.0",
|
|
48509
|
+
ink: "^5.0.1",
|
|
48510
|
+
"ink-text-input": "^6.0.0",
|
|
48511
|
+
react: "^18.3.1",
|
|
48512
|
+
"react-devtools-core": "^7.0.1",
|
|
48513
|
+
typescript: "^5.5.0"
|
|
48514
|
+
}
|
|
48515
|
+
};
|
|
46749
48516
|
|
|
46750
48517
|
// src/cli.ts
|
|
46751
|
-
var VERSION =
|
|
48518
|
+
var VERSION = package_default.version;
|
|
46752
48519
|
function fail(msg, code = 1) {
|
|
46753
48520
|
process.stderr.write(`error: ${msg}
|
|
46754
48521
|
`);
|
|
@@ -46817,37 +48584,42 @@ async function resolveSession(opts) {
|
|
|
46817
48584
|
return picked;
|
|
46818
48585
|
}
|
|
46819
48586
|
async function runWatch(opts) {
|
|
46820
|
-
const
|
|
48587
|
+
const cwd2 = process.cwd();
|
|
46821
48588
|
let config;
|
|
46822
48589
|
try {
|
|
46823
|
-
config = loadConfig({ projectDir, explicitPath: opts.config });
|
|
48590
|
+
config = loadConfig({ projectDir: cwd2, explicitPath: opts.config });
|
|
46824
48591
|
} catch (err) {
|
|
46825
|
-
|
|
48592
|
+
const msg = err.message;
|
|
48593
|
+
if (msg.includes("no api_key configured")) {
|
|
48594
|
+
await runInlineConfigure();
|
|
48595
|
+
try {
|
|
48596
|
+
config = loadConfig({ projectDir: cwd2, explicitPath: opts.config });
|
|
48597
|
+
} catch (err2) {
|
|
48598
|
+
fail(err2.message);
|
|
48599
|
+
}
|
|
48600
|
+
} else {
|
|
48601
|
+
fail(msg);
|
|
48602
|
+
}
|
|
46826
48603
|
}
|
|
46827
48604
|
const session = await resolveSession({
|
|
46828
48605
|
pick: opts.pick,
|
|
46829
48606
|
sessionPrefix: opts.session,
|
|
46830
|
-
projectDir
|
|
48607
|
+
projectDir: cwd2
|
|
46831
48608
|
});
|
|
46832
48609
|
if (!session) {
|
|
46833
48610
|
fail("no session selected", 0);
|
|
46834
48611
|
}
|
|
48612
|
+
const projectDir = session.cwd ?? cwd2;
|
|
46835
48613
|
if (!existsSync8(slashCommandPath(projectDir))) {
|
|
46836
|
-
|
|
48614
|
+
const accepted = await promptInstallSlashCommand(projectDir);
|
|
48615
|
+
if (!accepted) {
|
|
48616
|
+
process.stderr.write(`note: /i not installed — you'll need to manually paste the
|
|
48617
|
+
` + ` translated English into Claude Code each turn.
|
|
46837
48618
|
`);
|
|
48619
|
+
}
|
|
46838
48620
|
}
|
|
46839
48621
|
render_default(import_react31.default.createElement(App2, { session, config, projectDir }));
|
|
46840
48622
|
}
|
|
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
48623
|
async function runUninstall(opts) {
|
|
46852
48624
|
const projectDir = process.cwd();
|
|
46853
48625
|
let wipe = opts.wipeCache;
|
|
@@ -46863,18 +48635,18 @@ async function runUninstall(opts) {
|
|
|
46863
48635
|
for (const s of skipped)
|
|
46864
48636
|
ok(` (not present) ${s}`);
|
|
46865
48637
|
}
|
|
46866
|
-
async function
|
|
48638
|
+
async function runInlineConfigure() {
|
|
46867
48639
|
const path = userConfigPath();
|
|
46868
48640
|
let existing = {};
|
|
46869
48641
|
if (existsSync8(path)) {
|
|
46870
48642
|
try {
|
|
46871
48643
|
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
48644
|
} catch {}
|
|
46877
48645
|
}
|
|
48646
|
+
ok("");
|
|
48647
|
+
ok("─ First-time setup ─────────────────────────────────────────");
|
|
48648
|
+
ok("No API endpoint configured yet. Set one up now (Ctrl+C to abort).");
|
|
48649
|
+
ok("");
|
|
46878
48650
|
const rl = createInterface({ input, output });
|
|
46879
48651
|
const ask = async (prompt, fallback) => {
|
|
46880
48652
|
const ans = (await rl.question(prompt)).trim();
|
|
@@ -46894,10 +48666,31 @@ async function runConfigure() {
|
|
|
46894
48666
|
model,
|
|
46895
48667
|
timeout_seconds: Number(timeoutStr) || 30
|
|
46896
48668
|
};
|
|
46897
|
-
|
|
46898
|
-
|
|
46899
|
-
`, "utf-8");
|
|
48669
|
+
writeAtomic(path, JSON.stringify(next, null, 2) + `
|
|
48670
|
+
`, { mode: 384 });
|
|
46900
48671
|
ok(`✓ wrote ${path}`);
|
|
48672
|
+
ok("");
|
|
48673
|
+
}
|
|
48674
|
+
async function promptInstallSlashCommand(projectDir) {
|
|
48675
|
+
const target = slashCommandPath(projectDir);
|
|
48676
|
+
const rl = createInterface({ input, output });
|
|
48677
|
+
const ans = (await rl.question(`
|
|
48678
|
+
/i slash command not yet installed in this project.
|
|
48679
|
+
` + ` project: ${projectDir}
|
|
48680
|
+
` + ` will write: ${target}
|
|
48681
|
+
` + `Install now? [Y/n] `)).trim().toLowerCase();
|
|
48682
|
+
rl.close();
|
|
48683
|
+
if (ans === "n" || ans === "no")
|
|
48684
|
+
return false;
|
|
48685
|
+
try {
|
|
48686
|
+
installSlashCommand(projectDir);
|
|
48687
|
+
ok(`✓ installed ${target}`);
|
|
48688
|
+
return true;
|
|
48689
|
+
} catch (err) {
|
|
48690
|
+
process.stderr.write(`✗ install failed: ${err.message}
|
|
48691
|
+
`);
|
|
48692
|
+
return false;
|
|
48693
|
+
}
|
|
46901
48694
|
}
|
|
46902
48695
|
function runPromptsShow(opts) {
|
|
46903
48696
|
let config = null;
|
|
@@ -47009,17 +48802,15 @@ function runSessionsList() {
|
|
|
47009
48802
|
state file: ${stateFilePath(projectDir)}`);
|
|
47010
48803
|
}
|
|
47011
48804
|
var program2 = new Command;
|
|
47012
|
-
program2.name("cc-zh-watcher").version(VERSION);
|
|
48805
|
+
program2.name("cc-zh-watcher").version(VERSION, "-v, --version", "output the version number");
|
|
47013
48806
|
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
48807
|
await runWatch(opts);
|
|
47015
48808
|
});
|
|
47016
|
-
program2.command("install").description("install /i slash command into <project>/.claude/commands/").action(runInstall);
|
|
47017
48809
|
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
48810
|
await runUninstall({
|
|
47019
48811
|
wipeCache: opts.wipeCache ? true : opts.keepCache ? false : undefined
|
|
47020
48812
|
});
|
|
47021
48813
|
});
|
|
47022
|
-
program2.command("configure").description("interactively create / edit user-global config").action(runConfigure);
|
|
47023
48814
|
program2.command("sessions").description("list all sessions in this project").action(runSessionsList);
|
|
47024
48815
|
var promptsCmd = program2.command("prompts").description("inspect / introspect the translation prompts");
|
|
47025
48816
|
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) => {
|