@toddzheng024/dscode-bundle 0.5.0 → 0.7.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.
@@ -1,3 +1,70 @@
1
+ // dscode-language-v2
2
+ // dscode-language-v1
3
+ const DSCODE_LANGUAGES = [{"code":"en","name":"English"},{"code":"zh-CN","name":"简体中文"},{"code":"zh-TW","name":"繁體中文"},{"code":"ja","name":"日本語"},{"code":"ko","name":"한국어"},{"code":"es","name":"Español"}];
4
+ const DSCODE_MESSAGES = {"en":{"activity.replying":"Replying","activity.thinking":"Thinking","activity.running":"Running","activity.turn":"this turn","activity.interrupt":"Esc to interrupt","agents.running":"running","agents.idle":"idle","agents.done":"done","agents.total":"total","welcome.model":"model","welcome.effort":"effort","welcome.project":"project","verbose.on":"verbose on: thinking and tool calls are shown in the chat","verbose.off":"verbose off","mouse.on":"mouse on: the wheel scrolls the chat · hold Option (iTerm2) or Fn (Terminal) while dragging to select text","mouse.off":"mouse off: select and copy freely · PageUp/PageDown scroll the chat · /mouse turns wheel scrolling on","language.current":"Language: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"language → {name}","language.title":"/language — interface language","language.currentMark":"current","language.unknown":"Unknown language \"{value}\". Choose en, zh-CN, zh-TW, ja, ko or es.","language.saveFailed":"language save failed: {error}","doctor.logs.new":"Only warnings/errors since this TUI version started are recorded.","footer.current":"current","footer.average":"average","footer.context":"context","footer.cache":"cache","composer.placeholder":"type a message","doctor.nearTimeout":"Assessment: {count} Bash calls ended at about 300 seconds, which matches the tool timeout; traces alone cannot prove the root cause. Next, check those calls' shell completion markers and terminal errors.","doctor.logs.none":"No log file yet; earlier console logs were not persisted and cannot be recovered.","doctor.evidence":"Diagnostic evidence: {traces} recent sessions, {logs} warnings/errors.","doctor.noFindings":"No clear timeouts, unfinished tool calls or error events in recent traces.","doctor.noRoute":"No model route is available, so the model analysis could not run.","doctor.scope":"Evidence scope: {traces} recent sessions, {logs} warnings/errors; conversation text, tool arguments and tool output were not read.","doctor.failed":"Model analysis did not finish: {error}."},"zh-CN":{"activity.replying":"正在回复","activity.thinking":"正在思考","activity.running":"正在执行","activity.turn":"本轮","activity.interrupt":"Esc 中断","agents.running":"运行中","agents.idle":"空闲","agents.done":"已完成","agents.total":"总计","welcome.model":"模型","welcome.effort":"推理","welcome.project":"项目","verbose.on":"详细模式已开启:对话中显示思考与工具调用","verbose.off":"详细模式已关闭","mouse.on":"鼠标捕获已开启:滚轮滚动对话 · 按住 Option(iTerm2)或 Fn(Terminal)拖选文本","mouse.off":"鼠标捕获已关闭:可自由选择复制 · PageUp/PageDown 滚动对话 · /mouse 重新开启","language.current":"语言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"语言 → {name}","language.title":"/language — 界面语言","language.currentMark":"当前","language.unknown":"未知语言 \"{value}\"。可选 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"语言设置保存失败:{error}","doctor.logs.new":"仅记录新版 TUI 启动后的 warning/error。","footer.current":"当前","footer.average":"平均","footer.context":"上下文","footer.cache":"缓存","composer.placeholder":"输入消息","doctor.nearTimeout":"判断:{count} 次 Bash 调用在约 300 秒结束,符合工具超时特征;trace 不能单独证明触发超时的根因。下一步检查这些调用的 shell 完成标记和终端错误。","doctor.logs.none":"日志文件尚未建立;旧版控制台日志未持久化,无法回溯。","doctor.evidence":"诊断证据:{traces} 个近期会话、{logs} 条 warning/error。","doctor.noFindings":"近期 trace 中未发现明确的超时、未完成工具调用或错误事件。","doctor.noRoute":"没有可用的模型路由,无法运行模型分析。","doctor.scope":"证据范围:{traces} 个近期会话、{logs} 条 warning/error;未读取对话正文、工具参数或工具输出。","doctor.failed":"模型分析未完成:{error}。"},"zh-TW":{"activity.replying":"正在回覆","activity.thinking":"正在思考","activity.running":"正在執行","activity.turn":"本輪","activity.interrupt":"Esc 中斷","agents.running":"執行中","agents.idle":"閒置","agents.done":"已完成","agents.total":"總計","welcome.model":"模型","welcome.effort":"推理","welcome.project":"專案","verbose.on":"詳細模式已開啟:對話中顯示思考與工具呼叫","verbose.off":"詳細模式已關閉","mouse.on":"滑鼠擷取已開啟:滾輪捲動對話 · 按住 Option(iTerm2)或 Fn(Terminal)拖選文字","mouse.off":"滑鼠擷取已關閉:可自由選取複製 · PageUp/PageDown 捲動對話 · /mouse 重新開啟","language.current":"語言:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"語言 → {name}","language.title":"/language — 介面語言","language.currentMark":"目前","language.unknown":"未知語言 \"{value}\"。可選 en、zh-CN、zh-TW、ja、ko、es。","language.saveFailed":"語言設定儲存失敗:{error}","doctor.logs.new":"僅記錄新版 TUI 啟動後的 warning/error。","footer.current":"目前","footer.average":"平均","footer.context":"上下文","footer.cache":"快取","composer.placeholder":"輸入訊息","doctor.nearTimeout":"判斷:{count} 次 Bash 呼叫在約 300 秒結束,符合工具逾時特徵;trace 無法單獨證明觸發逾時的根因。下一步檢查這些呼叫的 shell 完成標記和終端錯誤。","doctor.logs.none":"日誌檔尚未建立;舊版主控台日誌未持久化,無法回溯。","doctor.evidence":"診斷證據:{traces} 個近期工作階段、{logs} 筆 warning/error。","doctor.noFindings":"近期 trace 中未發現明確的逾時、未完成工具呼叫或錯誤事件。","doctor.noRoute":"沒有可用的模型路由,無法執行模型分析。","doctor.scope":"證據範圍:{traces} 個近期工作階段、{logs} 筆 warning/error;未讀取對話內容、工具參數或工具輸出。","doctor.failed":"模型分析未完成:{error}。"},"ja":{"activity.replying":"応答中","activity.thinking":"思考中","activity.running":"実行中","activity.turn":"今回のターン","activity.interrupt":"Esc で中断","agents.running":"実行中","agents.idle":"待機中","agents.done":"完了","agents.total":"合計","welcome.model":"モデル","welcome.effort":"推論","welcome.project":"プロジェクト","verbose.on":"詳細モード オン:思考とツール呼び出しをチャットに表示します","verbose.off":"詳細モード オフ","mouse.on":"マウス キャプチャ オン:ホイールでチャットをスクロール · Option(iTerm2)または Fn(Terminal)を押しながらドラッグでテキストを選択","mouse.off":"マウス キャプチャ オフ:自由に選択・コピーできます · PageUp/PageDown でスクロール · /mouse で再びオン","language.current":"言語:{name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"言語 → {name}","language.title":"/language — 表示言語","language.currentMark":"現在","language.unknown":"不明な言語 \"{value}\"。en、zh-CN、zh-TW、ja、ko、es から選んでください。","language.saveFailed":"言語設定の保存に失敗しました:{error}","doctor.logs.new":"この TUI バージョンの起動以降の warning/error のみ記録されています。","footer.current":"現在","footer.average":"平均","footer.context":"コンテキスト","footer.cache":"キャッシュ","composer.placeholder":"メッセージを入力","doctor.nearTimeout":"判断:Bash 呼び出し {count} 件が約 300 秒で終了しており、ツールのタイムアウトの特徴に一致します。トレースだけでは根本原因を証明できません。次はこれらの呼び出しのシェル完了マーカーと端末エラーを確認してください。","doctor.logs.none":"ログファイルはまだありません。以前のコンソールログは保存されておらず、遡れません。","doctor.evidence":"診断の根拠:直近のセッション {traces} 件、warning/error {logs} 件。","doctor.noFindings":"直近のトレースに明確なタイムアウト、未完了ツール、停滞は見つかりませんでした。","doctor.noRoute":"利用できるモデルルートがないため、モデル分析を実行できません。","doctor.scope":"根拠の範囲:直近のセッション {traces} 件、warning/error {logs} 件。会話本文、ツール引数、ツール出力は読んでいません。","doctor.failed":"モデル分析が完了しませんでした:{error}。"},"ko":{"activity.replying":"응답 중","activity.thinking":"생각 중","activity.running":"실행 중","activity.turn":"이번 턴","activity.interrupt":"Esc 중단","agents.running":"실행 중","agents.idle":"대기","agents.done":"완료","agents.total":"전체","welcome.model":"모델","welcome.effort":"추론","welcome.project":"프로젝트","verbose.on":"상세 모드 켜짐: 생각과 도구 호출을 채팅에 표시합니다","verbose.off":"상세 모드 꺼짐","mouse.on":"마우스 캡처 켜짐: 휠로 채팅 스크롤 · Option(iTerm2) 또는 Fn(Terminal)을 누른 채 드래그하여 텍스트 선택","mouse.off":"마우스 캡처 꺼짐: 자유롭게 선택·복사 · PageUp/PageDown으로 채팅 스크롤 · /mouse로 다시 켜기","language.current":"언어: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"언어 → {name}","language.title":"/language — 인터페이스 언어","language.currentMark":"현재","language.unknown":"알 수 없는 언어 \"{value}\". en, zh-CN, zh-TW, ja, ko, es 중에서 선택하세요.","language.saveFailed":"언어 설정 저장 실패: {error}","doctor.logs.new":"이 TUI 버전 시작 이후의 warning/error만 기록됩니다.","footer.current":"현재","footer.average":"평균","footer.context":"컨텍스트","footer.cache":"캐시","composer.placeholder":"메시지를 입력하세요","doctor.nearTimeout":"판단: Bash 호출 {count}건이 약 300초에 종료되어 도구 시간 초과 특징과 일치합니다. 트레이스만으로는 근본 원인을 증명할 수 없습니다. 다음으로 해당 호출의 셸 완료 표시와 터미널 오류를 확인하세요.","doctor.logs.none":"아직 로그 파일이 없습니다. 이전 콘솔 로그는 저장되지 않아 복구할 수 없습니다.","doctor.evidence":"진단 근거: 최근 세션 {traces}개, warning/error {logs}건.","doctor.noFindings":"최근 트레이스에서 명확한 시간 초과, 미완료 도구, 정지는 발견되지 않았습니다.","doctor.noRoute":"사용 가능한 모델 경로가 없어 모델 분석을 실행할 수 없습니다.","doctor.scope":"근거 범위: 최근 세션 {traces}개, warning/error {logs}건. 대화 본문, 도구 인수, 도구 출력은 읽지 않았습니다.","doctor.failed":"모델 분석이 완료되지 않았습니다: {error}."},"es":{"activity.replying":"Respondiendo","activity.thinking":"Pensando","activity.running":"Ejecutando","activity.turn":"este turno","activity.interrupt":"Esc para interrumpir","agents.running":"en ejecución","agents.idle":"inactivo","agents.done":"terminado","agents.total":"en total","welcome.model":"modelo","welcome.effort":"esfuerzo","welcome.project":"proyecto","verbose.on":"modo detallado activado: el razonamiento y las llamadas a herramientas se muestran en el chat","verbose.off":"modo detallado desactivado","mouse.on":"ratón activado: la rueda desplaza el chat · mantén Option (iTerm2) o Fn (Terminal) al arrastrar para seleccionar texto","mouse.off":"ratón desactivado: selecciona y copia libremente · PageUp/PageDown desplazan el chat · /mouse vuelve a activar la captura","language.current":"Idioma: {name} · /language en | zh-CN | zh-TW | ja | ko | es","language.set":"idioma → {name}","language.title":"/language — idioma de la interfaz","language.currentMark":"actual","language.unknown":"Idioma desconocido \"{value}\". Elige en, zh-CN, zh-TW, ja, ko o es.","language.saveFailed":"no se pudo guardar el idioma: {error}","doctor.logs.new":"Solo se registran los warnings/errores desde que arrancó esta versión del TUI.","footer.current":"actual","footer.average":"promedio","footer.context":"contexto","footer.cache":"caché","composer.placeholder":"escribe un mensaje","doctor.nearTimeout":"Valoración: {count} llamadas a Bash terminaron a unos 300 segundos, lo que coincide con el tiempo de espera de la herramienta; las trazas por sí solas no prueban la causa raíz. A continuación, revisa los marcadores de finalización del shell y los errores de terminal de esas llamadas.","doctor.logs.none":"Aún no hay archivo de registro; los registros de consola anteriores no se conservaron y no se pueden recuperar.","doctor.evidence":"Evidencia del diagnóstico: {traces} sesiones recientes, {logs} warnings/errores.","doctor.noFindings":"No hay tiempos de espera, herramientas sin terminar ni bloqueos claros en las trazas recientes.","doctor.noRoute":"No hay una ruta de modelo disponible, así que el análisis con modelo no se pudo ejecutar.","doctor.scope":"Alcance de la evidencia: {traces} sesiones recientes, {logs} warnings/errores; no se leyó el texto de la conversación, los argumentos ni la salida de las herramientas.","doctor.failed":"El análisis con modelo no terminó: {error}."}};
5
+ const DSCODE_LANGUAGE_ALIASES = {"en":["en","english","eng","英文","英语","英語"],"zh-CN":["zh-cn","zh","zh-hans","zhhans","cn","chinese","simplified","中文","简体","简体中文","简中","中文简体"],"zh-TW":["zh-tw","zh-hant","zhhant","tw","hk","zh-hk","traditional","繁体","繁體","繁體中文","繁体中文","繁中"],"ja":["ja","jp","japanese","日本語","日语","日語"],"ko":["ko","kr","korean","한국어","韩语","韓語","韓文"],"es":["es","spanish","español","espanol","西班牙语","西班牙語"]};
6
+ function dscodeNormalizeLanguage(value) {
7
+ const aliasesByCode = DSCODE_LANGUAGE_ALIASES;
8
+ const wanted = String(value ?? '').trim().toLowerCase().replace(/[_\s]+/g, '-');
9
+ if (!wanted) return null;
10
+ for (const [code, aliases] of Object.entries(aliasesByCode)) if (aliases.includes(wanted) || code.toLowerCase() === wanted) return code;
11
+ return null;
12
+ }
13
+ function dscodeLanguageName(code) {
14
+ return DSCODE_LANGUAGES.find(language => language.code === code)?.name ?? code;
15
+ }
16
+ function dscodeTranslate(locale, key, params) {
17
+ const table = DSCODE_MESSAGES[locale] ?? DSCODE_MESSAGES.en;
18
+ let text = table[key] ?? DSCODE_MESSAGES.en[key] ?? key;
19
+ if (params) for (const [name, value] of Object.entries(params)) text = text.split(`{${name}}`).join(String(value));
20
+ return text;
21
+ }
22
+ function dscodeLanguageFile() { return join(homedir(), ".dsh", "dsh-code", "language.json"); }
23
+ function dscodeLoadLanguage() {
24
+ const override = dscodeNormalizeLanguage(process.env.DSCODE_LANGUAGE);
25
+ if (override) return override;
26
+ try { return dscodeNormalizeLanguage(JSON.parse(readFileSync(dscodeLanguageFile(), "utf8")).language) ?? "en"; } catch { return "en"; }
27
+ }
28
+ let dscodeLocale = dscodeLoadLanguage();
29
+ function dscodeT(key, params) { return dscodeTranslate(dscodeLocale, key, params); }
30
+ function dscodeSaveLanguage(code) {
31
+ fs.mkdirSync(join(homedir(), ".dsh", "dsh-code"), { recursive: true });
32
+ fs.writeFileSync(dscodeLanguageFile(), JSON.stringify({ language: code }, null, 2) + "\n");
33
+ }
34
+ function dscodePadEnd(text, width) { return text + " ".repeat(Math.max(1, width - visibleColumns(text))); }
35
+ function dscodeFlagFile(name) { return join(homedir(), ".dsh", "dsh-code", name + ".json"); }
36
+ function dscodeLoadFlag(name, fallback = false) {
37
+ try { const value = JSON.parse(readFileSync(dscodeFlagFile(name), "utf8"))[name]; return typeof value === "boolean" ? value : fallback; } catch { return fallback; }
38
+ }
39
+ function dscodeSaveFlag(name, value) {
40
+ try { fs.mkdirSync(join(homedir(), ".dsh", "dsh-code"), { recursive: true }); fs.writeFileSync(dscodeFlagFile(name), JSON.stringify({ [name]: value }, null, 2) + "\n"); } catch {}
41
+ }
42
+ // dscode-scroll-v2
43
+ // dscode-interaction-v2
44
+ // dscode-welcome-scroll-v2
45
+ // dscode-welcome-v2
46
+ const WELCOME_ART = ["...........1.1..........","............1...........","............1...........",".........1..1..1........",".......1..22222..1......","...1...1....2....1...1..","..11...2....2....2...11.","....11.2...222...2.11...","......22.22.3.22.22.....",".....22.22.333.22.22....","....1...2.33332.2...1...","........2.33322.2.......","....1...2.33222.2...1...",".....22.22.222.22.22....","......22.22.3.22.22.....","....11.2...222...2.11...","..11...2....2....2...11.","...1...1....2....1...1..",".......1..22222..1......",".........1..1..1........","............1...........","............1...........","...........1.1..........","........................"];
47
+ const WELCOME_ART_SMALL = ["..........1.1.........","...........1..........","........1..1..1.......",".........1.2.1........","...1..1...222...1..1..","...1..1....2....1..1..","..1.1.2...222...2.1.1.",".....22.22.3.22.22....","....12.22.333.22.21...","...1...2.33332.2...1..",".......2.33322.2......","...1...2.33222.2...1..","....12.22.222.22.21...",".....22.22.3.22.22....","..1.1.2...222...2.1.1.","...1..1....2....1..1..","...1..1...222...1..1..",".........1.2.1........","........1..1..1.......","...........1..........","..........1.1.........","......................"];
48
+ function welcomeArtRows(grid, tones) {
49
+ const rows = [];
50
+ for (let y = 0; y < grid.length; y += 2) {
51
+ const top = grid[y] || '';
52
+ const bottom = grid[y + 1] || '';
53
+ const segments = [];
54
+ for (let x = 0; x < Math.max(top.length, bottom.length); x++) {
55
+ const upper = tones[top[x]] || '';
56
+ const lower = tones[bottom[x]] || '';
57
+ const glyph = upper && lower ? upper === lower ? '\u2588' : '\u2580' : upper ? '\u2580' : lower ? '\u2584' : ' ';
58
+ const color = upper || lower;
59
+ const background = upper && lower && upper !== lower ? lower : '';
60
+ const last = segments[segments.length - 1];
61
+ if (last && last.glyph === glyph && last.color === color && last.background === background) last.text += glyph;
62
+ else segments.push({ glyph, text: glyph, color, background });
63
+ }
64
+ rows.push(segments);
65
+ }
66
+ return rows;
67
+ }
1
68
  import { readClipboardImage as dscodeReadClipboardImage } from "./dscode-clipboard-image/index.mjs";
2
69
  // dscode-clipboard-image-v1
3
70
  // dscode-large-paste-v1
@@ -83,8 +150,8 @@ function userBackgroundRows(rows, columns, measure) {
83
150
  // dscode-turn-divider-v1
84
151
  function turnDividedLines(entry, columns, rendered, remaining) {
85
152
  if (entry?.turnEnded !== true || (rendered.length > 0 && rendered.length >= remaining)) return rendered;
86
- const width = Math.max(1, Math.min(72, columns - 6));
87
- return [...rendered, { segments: [{ text: ` ${'─'.repeat(width)}`, style: 'dim' }] }];
153
+ // Full terminal width: the row renders with truncate-end, so it can never wrap.
154
+ return [...rendered, { segments: [{ text: '─'.repeat(Math.max(1, columns)), style: 'dim' }] }];
88
155
  }
89
156
  // dscode-scroll-v1
90
157
  function transcriptWindow(lines, rows, offset) {
@@ -102,6 +169,12 @@ function mouseWheelDirection(unit) {
102
169
  const DSCODE_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
103
170
  const DSCODE_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1000l";
104
171
  const dscodeWheelListeners = new Set();
172
+ let dscodeMouseEnabled = dscodeLoadFlag("mouse", false);
173
+ function dscodeSetMouse(enabled) {
174
+ dscodeMouseEnabled = enabled;
175
+ if (process.stdout.isTTY === true) process.stdout.write(enabled ? DSCODE_MOUSE_ENABLE : DSCODE_MOUSE_DISABLE);
176
+ return enabled;
177
+ }
105
178
  function dscodeHandleMouseUnit(unit) {
106
179
  const direction = mouseWheelDirection(unit);
107
180
  if (direction === null) return false;
@@ -319,7 +392,7 @@ function dscodeTelemetryNodes(value, key) {
319
392
  function dscodeActivity(entries, streaming) {
320
393
  const running = entries.filter(entry => entry.kind === "tool" && entry.state === "running");
321
394
  const tool = running.at(-1);
322
- if (!tool) return streaming ? "正在回复" : "正在思考";
395
+ if (!tool) return streaming ? dscodeT("activity.replying") : dscodeT("activity.thinking");
323
396
  let description = "";
324
397
  try {
325
398
  if (typeof tool.arguments === "string" && tool.arguments.length <= 4096) {
@@ -329,22 +402,46 @@ function dscodeActivity(entries, streaming) {
329
402
  } catch {}
330
403
  // No raw argument/command dump in the chat chrome. A supplied description
331
404
  // is a task label, not a claim that the command succeeded.
332
- return "正在执行 · " + singleLineText(tool.name) + (running.length > 1 ? " +" + (running.length - 1) : "") +
405
+ return dscodeT("activity.running") + " · " + singleLineText(tool.name) + (running.length > 1 ? " +" + (running.length - 1) : "") +
333
406
  (description ? " · " + truncateColumns(singleLineText(description), 56) : "");
334
407
  }
408
+
409
+
410
+ // A comet orbiting the snowflake clockwise. Each side cell uses only its inner braille
411
+ // dot column (bit masks), so every position is one column from the flake and the orbit
412
+ // stays centred: down the right side, then up the left side. The comet is two dots long;
413
+ // when it crosses under or over the flake the previous dot lingers dimly in the old cell.
414
+ const DSCODE_ORBIT = [["right", 1], ["right", 2], ["right", 4], ["right", 64], ["left", 128], ["left", 32], ["left", 16], ["left", 8]];
415
+ function dscodeMixTone(from, to, amount) { return from.map((value, index) => Math.round(value + (to[index] - value) * amount)); }
416
+ function dscodeSpinnerCells(tick, palette) {
417
+ const index = ((tick % DSCODE_ORBIT.length) + DSCODE_ORBIT.length) % DSCODE_ORBIT.length;
418
+ const [side, bit] = DSCODE_ORBIT[index];
419
+ const [previousSide, previousBit] = DSCODE_ORBIT[(index + DSCODE_ORBIT.length - 1) % DSCODE_ORBIT.length];
420
+ const cells = { left: { text: " ", color: palette.brandMid }, right: { text: " ", color: palette.brandMid } };
421
+ if (previousSide === side) cells[side] = { text: String.fromCharCode(0x2800 | bit | previousBit), color: palette.brandMid };
422
+ else { cells[side] = { text: String.fromCharCode(0x2800 | bit), color: palette.brandMid }; cells[previousSide] = { text: String.fromCharCode(0x2800 | previousBit), color: palette.dim }; }
423
+ // The flake breathes with the orbit: brightest as the comet passes the top, deepest at the bottom.
424
+ const flake = dscodeMixTone(palette.brandDeep, palette.brandBright, (Math.cos(2 * Math.PI * index / DSCODE_ORBIT.length) + 1) / 2);
425
+ return { left: cells.left, flake, right: cells.right };
426
+ }
335
427
  function DscodeActivityLine({ entries, streaming, since, animated = true }) {
336
428
  const columns = useStdout().stdout?.columns ?? 80;
337
- const tick = useFrames(animated ? 160 : 1000);
429
+ const tick = useFrames(animated ? 100 : 1000);
338
430
  const elapsed = since > 0 ? Math.max(0, Date.now() - since) : 0;
339
- const suffix = columns >= 48 ? " · 本轮 " + runClock(elapsed) + " · Esc 中断" : " · 本轮 " + runClock(elapsed);
340
- const glyph = animated ? ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][tick % 10] : "●";
341
- const label = truncateColumns(dscodeActivity(entries, streaming), Math.max(1, columns - 6 - visibleColumns(suffix)));
431
+ const suffix = columns >= 64 ? " · " + dscodeT("activity.turn") + " " + runClock(elapsed) + " · " + dscodeT("activity.interrupt") : " · " + dscodeT("activity.turn") + " " + runClock(elapsed);
432
+ const palette = getPalette();
433
+ const spinner = animated ? dscodeSpinnerCells(tick, palette) : { left: { text: " ", color: palette.brandMid }, flake: palette.brandBright, right: { text: " ", color: palette.brandMid } };
434
+ const label = truncateColumns(dscodeActivity(entries, streaming), Math.max(1, columns - 9 - visibleColumns(suffix)));
342
435
  return (0, import_react.createElement)(Box, { paddingX: 2 },
343
436
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
344
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, glyph + " " + label),
437
+ (0, import_react.createElement)(Text, { color: inkColor(spinner.left.color) }, spinner.left.text),
438
+ (0, import_react.createElement)(Text, { color: inkColor(spinner.flake) }, "❄"),
439
+ (0, import_react.createElement)(Text, { color: inkColor(spinner.right.color) }, spinner.right.text),
440
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " " + label),
345
441
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, suffix)));
346
442
  }
347
443
 
444
+
348
445
  // dscode-footer-v1
349
446
  import { footerFor as dscodeFooterFor } from "../../plugins/session-metrics/view.mjs";
350
447
  // dscode-ime-v1
@@ -391,15 +488,29 @@ function dscodeVisibleRelay(message) {
391
488
  return message.source.kind === "plugin" && message.source.plugin === "dscode-session-bridge" && message.source.form === "relay";
392
489
  }
393
490
  // dscode-interaction-v1
394
- function dscodeChatLines(entry, columns) {
395
- if (entry.kind === "tool") return [];
491
+ function dscodeChatLines(entry, columns, verbose = false) {
492
+ const width = Math.max(1, Math.floor(columns));
493
+ if (entry.kind === "tool") {
494
+ if (!verbose) return [];
495
+ const state = entry.state === "running" ? " · running" : entry.state === "error" ? " · error" : "";
496
+ const lines = hangingStyledLines([lineSegment("Tool Call: " + entry.name, "dim"), lineSegment(entry.preview ? " " + entry.preview : "", "dim"), lineSegment(state, entry.state === "error" ? "error" : "dim")], width, "· ", "dim", " ", "dim");
497
+ if (entry.summary) lines.push(...hangingTextLines("Output: " + entry.summary, width, " ", entry.state === "error" ? "error" : "dim", " "));
498
+ lines.push({ segments: [] });
499
+ return lines;
500
+ }
396
501
  if (entry.kind === "assistant") {
397
- if (!entry.text && !entry.interrupted) return [];
398
- entry = { ...entry, reasoning: "" };
502
+ const thinking = verbose && entry.reasoning ? [...dscodeThinkingLines(entry.reasoning, width), { segments: [] }] : [];
503
+ if (!entry.text && !entry.interrupted) return thinking;
504
+ return [...thinking, ...transcriptEntryLines({ ...entry, reasoning: "" }, columns, false, false, false)];
399
505
  }
400
506
  const rows = transcriptEntryLines(entry, columns, false, false, false);
401
507
  return entry.kind === "user" && !entry.notice ? userBackgroundRows(rows, columns, visibleColumns) : rows;
402
508
  }
509
+ function dscodeThinkingLines(reasoning, width) {
510
+ const lines = hangingTextLines("Thinking: " + reasoning.replace(/\s+/g, " ").trim(), width, "· ", "dimItalic", " ");
511
+ const cap = 8;
512
+ return lines.length <= cap ? lines : [...lines.slice(0, cap), ...textLines(" … " + (lines.length - cap) + " more lines · Ctrl+O opens the full history", width, "dim")];
513
+ }
403
514
  import { n as __require, r as __toESM, t as __commonJSMin } from "./rolldown-runtime-CMFfr-1z.mjs";
404
515
  import { a as inkColor, c as chalk, i as getTheme, n as dim, o as parseThemeName, r as getPalette, s as setTheme } from "./theme-DCT8Y2xf.mjs";
405
516
  import { randomUUID } from "node:crypto";
@@ -25097,6 +25208,31 @@ const THEME_ROWS = [
25097
25208
  * it), Esc/q closes without changing anything. Colors read the ACTIVE
25098
25209
  * palette, so the panel itself adapts to a light theme once applied.
25099
25210
  */
25211
+ function LanguagePanel({ current, select, close }) {
25212
+ const rows = DSCODE_LANGUAGES;
25213
+ const [cursor, setCursor] = (0, import_react.useState)(() => Math.max(0, rows.findIndex((language) => language.code === current)));
25214
+ const stdout = useStdout().stdout;
25215
+ const viewport = panelViewport(stdout?.columns ?? 80, stdout?.rows ?? 30);
25216
+ useInput((input, key) => {
25217
+ if (key.escape || input === "q") return close();
25218
+ if (key.upArrow) return setCursor((value) => (value + rows.length - 1) % rows.length);
25219
+ if (key.downArrow) return setCursor((value) => (value + 1) % rows.length);
25220
+ if (key.return) return select(rows[cursor].code);
25221
+ });
25222
+ if (viewport.maxHeight === 0 || viewport.compact) return (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("/language · esc close", viewport.contentColumns));
25223
+ const rowBudget = Math.max(1, viewport.bodyRows);
25224
+ const first = clampScroll(cursor, rows.length, rowBudget);
25225
+ const visible = rows.slice(first, first + rowBudget);
25226
+ return (0, import_react.createElement)(Box, { width: viewport.outerColumns, borderStyle: "round", borderColor: inkColor(getPalette().dim), flexDirection: "column", paddingX: 1 },
25227
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), wrap: "truncate-end" }, truncateColumns(dscodeT("language.title"), viewport.contentColumns)),
25228
+ ...visible.map((language, index) => {
25229
+ const selected = first + index === cursor;
25230
+ const active = language.code === current;
25231
+ return (0, import_react.createElement)(Text, { key: language.code, color: selected ? inkColor(getPalette().brandBright) : void 0, wrap: "truncate-end" },
25232
+ truncateColumns(`${selected ? "› " : " "}${active ? "● " : "○ "}${language.name}${active ? " · " + dscodeT("language.currentMark") : ""} · ${language.code}`, viewport.contentColumns));
25233
+ }),
25234
+ (0, import_react.createElement)(Text, { dimColor: true, wrap: "truncate-end" }, truncateColumns("↑↓ choose · enter apply · esc/q close", viewport.contentColumns)));
25235
+ }
25100
25236
  function ThemePanel({ current, select, close }) {
25101
25237
  const [cursor, setCursor] = (0, import_react.useState)(() => {
25102
25238
  const index = THEME_ROWS.findIndex((theme) => theme.id === current);
@@ -29297,7 +29433,7 @@ function transcriptEntryLines(entry, columns, showReasoning = true, reasoningTog
29297
29433
  }
29298
29434
  /** Settled-history variant carrying the Ctrl+R reasoning fold. */
29299
29435
  function settledEntryLines(entry, columns, showReasoning) {
29300
- return dscodeChatLines(entry, columns);
29436
+ return dscodeChatLines(entry, columns, showReasoning);
29301
29437
  }
29302
29438
  /**
29303
29439
  * Clamp the live-region allocation so the flexible dynamic rows never exceed
@@ -31891,6 +32027,18 @@ const LOCAL_COMMANDS = [
31891
32027
  label: "/todos",
31892
32028
  description: "inspect the full todo list"
31893
32029
  },
32030
+ {
32031
+ label: "/verbose",
32032
+ description: "toggle thinking and tool call details in the chat"
32033
+ },
32034
+ {
32035
+ label: "/mouse",
32036
+ description: "toggle mouse capture: off to select and copy text, on for wheel scrolling"
32037
+ },
32038
+ {
32039
+ label: "/language",
32040
+ description: "show or set the interface language: en, zh-CN, zh-TW, ja, ko, es"
32041
+ },
31894
32042
  {
31895
32043
  label: "/subagent",
31896
32044
  description: "choose the model delegated subagents run on"
@@ -32232,8 +32380,10 @@ function PanelGap({ visible }) {
32232
32380
  * keeps its historical three lines. Short or narrow terminals keep a one-line
32233
32381
  * form without the kernel line.
32234
32382
  */
32235
- function Header({ cwd = "", model = "", effort = "" }) {
32383
+ function Header({ cwd = "", model = "", effort = "", animated = false }) {
32236
32384
  const stdout = useStdout().stdout;
32385
+ const rippleTick = useFrames(animated ? 450 : 3600000);
32386
+ const ripplePhase = animated ? rippleTick % 4 : 3;
32237
32387
  const columns = stdout?.columns ?? 80;
32238
32388
  const full = (stdout?.rows ?? 30) >= 24 && columns >= 64;
32239
32389
  const width = Math.max(1, full ? Math.min(columns - 2, 84) : columns - 4);
@@ -32242,33 +32392,32 @@ function Header({ cwd = "", model = "", effort = "" }) {
32242
32392
  const modelName = singleLineText(model).split("/").at(-1) || "unknown";
32243
32393
  const effortName = singleLineText(effort) || "default";
32244
32394
  const project = welcomePath(cwd, detailsWidth);
32395
+ const palette = getPalette();
32396
+ const art = (stdout?.rows ?? 30) >= 26 ? WELCOME_ART : WELCOME_ART_SMALL;
32397
+ const luminance = ([red, green, blue]) => red * 299 + green * 587 + blue * 114;
32398
+ const tones = [palette.brandDeep, palette.brand, palette.brandBright].sort((left, right) => luminance(left) - luminance(right));
32399
+ // Ripple: the bright band moves core → ring → tips, then one resting frame in the base tones.
32400
+ const rippleBand = { 1: 2, 2: 1, 3: 0 };
32401
+ const tone = level => ripplePhase < 3 ? (rippleBand[level] === ripplePhase ? tones[2] : level === 3 ? tones[1] : tones[0]) : tones[level - 1];
32245
32402
  if (!full) return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1 },
32246
32403
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
32247
32404
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "❄ DSCODE"),
32248
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.5.0")),
32405
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.7.0")),
32249
32406
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(modelName + " · " + effortName, width)),
32250
32407
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" }, welcomePath(cwd, width)));
32251
- return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1, marginBottom: 1 },
32408
+ return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1 },
32252
32409
  (0, import_react.createElement)(Box, { flexDirection: "row" },
32253
32410
  (0, import_react.createElement)(Box, { flexDirection: "column", width: 28 },
32254
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄"),
32255
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " █▄▄█ █▄█▄█ █▄▄█"),
32256
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄███▄ ▀█▀ ▄███▄"),
32257
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀██▄ █ ▄██▀"),
32258
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄███▄"),
32259
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀███▀"),
32260
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄██▀ █ ▀██▄"),
32261
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀███▀ ▄█▄ ▀███▀"),
32262
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " █▀▀█ █▀█▀█ █▀▀█"),
32263
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀")),
32411
+ ...welcomeArtRows(art, { "1": inkColor(tone(1)), "2": inkColor(tone(2)), "3": inkColor(tone(3)) }).map((segments, row) => (0, import_react.createElement)(Text, { key: row }, " ",
32412
+ ...segments.map((segment, index) => (0, import_react.createElement)(Text, { key: index, color: segment.color || void 0, backgroundColor: segment.background || void 0 }, segment.text))))),
32264
32413
  (0, import_react.createElement)(Box, { flexDirection: "column", width: detailsWidth, marginTop: 2 },
32265
32414
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().text), bold: true }, "DSCODE"),
32266
32415
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandDeep) }, "────────────"),
32267
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.5.0"),
32268
- (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("model " + modelName, detailsWidth)),
32269
- (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("effort " + effortName, detailsWidth)),
32416
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.7.0"),
32417
+ (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.model"), 9) + modelName, detailsWidth)),
32418
+ (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.effort"), 9) + effortName, detailsWidth)),
32270
32419
  (0, import_react.createElement)(Text, null, " "),
32271
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "project"),
32420
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, dscodeT("welcome.project")),
32272
32421
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, project))));
32273
32422
  }
32274
32423
  /**
@@ -32285,8 +32434,8 @@ function AgentsLine({ rows, total }) {
32285
32434
  const idle = rows.filter(row => row.state === "idle").length;
32286
32435
  const done = rows.filter(row => row.state === "done").length;
32287
32436
  const active = [...running].sort((a, b) => b.updatedAt - a.updatedAt)[0];
32288
- const counts = [running.length + " running", idle ? idle + " idle" : "", done ? done + " done" : "", total > rows.length ? total + " total" : ""].filter(Boolean).join(" · ");
32289
- const summary = "agents " + (columns < 60 ? running.length + " running" : counts) + " · /agents";
32437
+ const counts = [running.length + " " + dscodeT("agents.running"), idle ? idle + " " + dscodeT("agents.idle") : "", done ? done + " " + dscodeT("agents.done") : "", total > rows.length ? total + " " + dscodeT("agents.total") : ""].filter(Boolean).join(" · ");
32438
+ const summary = "agents " + (columns < 60 ? running.length + " " + dscodeT("agents.running") : counts) + " · /agents";
32290
32439
  const detail = active && columns >= 80 ? " — " + singleLineText(active.label) + " · " + singleLineText(active.activity) : "";
32291
32440
  return (0, import_react.createElement)(Box, { paddingX: 2 },
32292
32441
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" },
@@ -32475,7 +32624,7 @@ function deepseekWaveHues(tier) {
32475
32624
  function StatusLine({ facts, stats, busy, columns, items }) {
32476
32625
  const [, refreshMetrics] = (0, import_react.useState)(0);
32477
32626
  (0, import_react.useEffect)(() => { const timer = setInterval(() => refreshMetrics(n => n + 1), 1000); return () => clearInterval(timer); }, []);
32478
- facts = { ...facts, telemetry: columns >= 48 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.min(columns - 8, Math.max(40, Math.floor(columns * 0.8) - 4)))) : "" };
32627
+ facts = { ...facts, telemetry: columns >= 48 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.min(columns - 8, Math.max(40, Math.floor(columns * 0.8) - 4))), dscodeLocale) : "" };
32479
32628
  const layout = (0, import_react.useMemo)(() => layoutStatusBar(facts, stats, Math.max(8, columns - 2), {
32480
32629
  busy,
32481
32630
  items,
@@ -34510,7 +34659,7 @@ function CompletionMenu({ active, mention, index, rows, error }) {
34510
34659
  * While a modal (approval / question / model panel) owns the keys, the
34511
34660
  * box passes every key through untouched.
34512
34661
  */
34513
- function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, readClipboardImage, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, onTranscriptScroll, sessionKey }) {
34662
+ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openLanguage, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, readClipboardImage, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, onTranscriptScroll, sessionKey }) {
34514
34663
  const columns = useStdout().stdout?.columns ?? 80;
34515
34664
  const inputTerminalRows = useStdout().stdout?.rows ?? 30;
34516
34665
  const dscodeImeStdout = useStdout().stdout;
@@ -35242,6 +35391,27 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35242
35391
  openAgents();
35243
35392
  return;
35244
35393
  }
35394
+ if (text === "/verbose") {
35395
+ toggleReasoning();
35396
+ return;
35397
+ }
35398
+ if (text === "/mouse") {
35399
+ const enabled = dscodeSetMouse(!dscodeMouseEnabled);
35400
+ dscodeSaveFlag("mouse", enabled);
35401
+ notify(dscodeT(enabled ? "mouse.on" : "mouse.off"));
35402
+ return;
35403
+ }
35404
+ if (text === "/language" || text.startsWith("/language ")) {
35405
+ const wanted = text.slice(9).trim();
35406
+ if (!wanted) { openLanguage(); return; }
35407
+ const code = dscodeNormalizeLanguage(wanted);
35408
+ if (!code) { notify(dscodeT("language.unknown", { value: wanted }), "warning"); return; }
35409
+ dscodeLocale = code;
35410
+ try { dscodeSaveLanguage(code); } catch (error) { notify(dscodeT("language.saveFailed", { error: error instanceof Error ? error.message : String(error) }), "error"); }
35411
+ notify(dscodeT("language.set", { name: dscodeLanguageName(code) }));
35412
+ refresh();
35413
+ return;
35414
+ }
35245
35415
  if (text === "/todos") {
35246
35416
  openTodos();
35247
35417
  return;
@@ -35439,7 +35609,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35439
35609
  bold: true
35440
35610
  }, warning), bandFill(2 + visibleColumns(warning))));
35441
35611
  }
35442
- const frozenLine = value === "" ? "type a message" : verboseLine(value, Math.max(1, columns - 6));
35612
+ const frozenLine = value === "" ? dscodeT("composer.placeholder") : verboseLine(value, Math.max(1, columns - 6));
35443
35613
  return band((0, import_react.createElement)(Text, {
35444
35614
  backgroundColor: bandBg,
35445
35615
  wrap: "truncate-end"
@@ -35823,7 +35993,7 @@ function App(props) {
35823
35993
  }
35824
35994
  }, [modelOpen, props.subscribeProviderAuthorizations]);
35825
35995
  const busy = view.busy;
35826
- const [showReasoning, setShowReasoning] = (0, import_react.useState)(false);
35996
+ const [showReasoning, setShowReasoning] = (0, import_react.useState)(() => dscodeLoadFlag("verbose"));
35827
35997
  const budgetWarnRef = (0, import_react.useRef)(void 0);
35828
35998
  const [verboseOpen, setVerboseOpen] = (0, import_react.useState)(false);
35829
35999
  const [diffView, setDiffView] = (0, import_react.useState)(void 0);
@@ -35837,6 +36007,7 @@ function App(props) {
35837
36007
  const [statuslineOpen, setStatuslineOpen] = (0, import_react.useState)(false);
35838
36008
  const [statuslineItems, setStatuslineItems] = (0, import_react.useState)(() => parseStatuslineItems(props.statusline));
35839
36009
  const [themeOpen, setThemeOpen] = (0, import_react.useState)(false);
36010
+ const [languageOpen, setLanguageOpen] = (0, import_react.useState)(false);
35840
36011
  const [historyOpen, setHistoryOpen] = (0, import_react.useState)(false);
35841
36012
  const [agentsOpen, setAgentsOpen] = (0, import_react.useState)(false);
35842
36013
  const [subagentOpen, setSubagentOpen] = (0, import_react.useState)(false);
@@ -35905,7 +36076,7 @@ function App(props) {
35905
36076
  const agentRows = (0, import_react.useSyncExternalStore)(props.subagents.subscribe, props.subagents.getSnapshot);
35906
36077
  const approvalPending = approvalSnapshot.pending !== void 0;
35907
36078
  const questionPending = questionSnapshot.pending !== void 0;
35908
- const inputActive = deleteConfirmId !== void 0 ? !approvalPending && !questionPending : !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
36079
+ const inputActive = deleteConfirmId !== void 0 ? !approvalPending && !questionPending : !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !languageOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35909
36080
  (0, import_react.useEffect)(() => {
35910
36081
  if (!approvalPending && !questionPending) return;
35911
36082
  setEmailOpen(false);
@@ -35920,6 +36091,7 @@ function App(props) {
35920
36091
  setPluginOpen(false);
35921
36092
  setStatuslineOpen(false);
35922
36093
  setThemeOpen(false);
36094
+ setLanguageOpen(false);
35923
36095
  setHistoryOpen(false);
35924
36096
  setAgentsOpen(false);
35925
36097
  setSubagentOpen(false);
@@ -35971,14 +36143,14 @@ function App(props) {
35971
36143
  setMenuRows((current) => current === rows ? current : rows);
35972
36144
  }, []);
35973
36145
  const composerEditorCap = composerMaxRows(terminalRows);
35974
- const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
36146
+ const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !languageOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35975
36147
  const welcomeFull = terminalRows >= 24 && terminalColumns >= 64;
35976
- const welcomeMaxRows = welcomeFull ? 13 : terminalRows >= 10 ? 4 : 1;
36148
+ const welcomeMaxRows = welcomeFull ? terminalRows >= 26 ? 14 : 13 : terminalRows >= 10 ? 4 : 1;
35977
36149
  // The nine fixed rows belong to the composer, footer and their gutters.
35978
36150
  const transcriptCapacity = transcriptVisible ? Math.max(0, terminalRows - 9 - composerGutterRows - (composerRows - 1) - menuRows) : 0;
35979
36151
  const streamingActive = view.streaming !== "";
35980
36152
  const deepDivingVisible = busy;
35981
- const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2))), [
36153
+ const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2), showReasoning)), [
35982
36154
  view.entries,
35983
36155
  settled,
35984
36156
  terminalColumns,
@@ -35990,7 +36162,10 @@ function App(props) {
35990
36162
  const demand = settledTail.length + allLiveLines.length + streamingDemand + (busy ? 1 : 0) + (agentRows.length > 0 ? 1 : 0);
35991
36163
  const welcomeRows = welcomeVisibleRows(transcriptCapacity, welcomeMaxRows, demand, transcriptVisible);
35992
36164
  const settledBudget = transcriptVisible ? Math.max(0, transcriptCapacity - welcomeRows) : 0;
35993
- const settledViewportRows = busy || streamingActive ? Math.floor(settledBudget / 3) : settledBudget;
36165
+ const liveDemand = allLiveLines.length + streamingDemand;
36166
+ const liveCap = busy || streamingActive ? Math.max(1, Math.floor(settledBudget * 2 / 3)) : 0;
36167
+ const liveRows = Math.min(liveDemand, liveCap);
36168
+ const settledViewportRows = Math.max(0, settledBudget - liveRows);
35994
36169
  const [scrollOffset, setScrollOffset] = (0, import_react.useState)(0);
35995
36170
  const previousHistory = (0, import_react.useRef)({ sessionKey: props.sessionKey, count: allSettledLines.length });
35996
36171
  const sameHistory = previousHistory.current.sessionKey === props.sessionKey;
@@ -36013,7 +36188,7 @@ function App(props) {
36013
36188
  });
36014
36189
  const renderedSettled = transcriptWindow(allSettledLines, settledViewportRows, effectiveScrollOffset);
36015
36190
  const dynamicRows = Math.max(0, settledBudget - settledViewportRows);
36016
- const liveBudget = dynamicRows === 0 ? 0 : busy || streamingActive ? Math.max(1, Math.floor(dynamicRows / 3)) : Math.max(0, dynamicRows - (deepDivingVisible ? 1 : 0));
36191
+ const liveBudget = dynamicRows === 0 ? 0 : streamingActive ? Math.max(0, dynamicRows - Math.min(streamingDemand, dynamicRows)) : dynamicRows;
36017
36192
  const visibleLiveLines = liveBudget === 0 ? [] : allLiveLines.slice(-liveBudget);
36018
36193
  const streamRows = Math.max(1, dynamicRows - visibleLiveLines.length);
36019
36194
  const reasoningRows = 0;
@@ -36031,7 +36206,7 @@ function App(props) {
36031
36206
  const auditedReasoningRows = liveAudit.allocation.reasoning;
36032
36207
  const auditedAnswerRows = liveAudit.allocation.answer;
36033
36208
 
36034
- const modalVisible = emailOpen || modelOpen || helpOpen || modeOpen || permissionOpen || resumeOpen || pluginOpen || jobsOpen || statuslineOpen || themeOpen || historyOpen || agentsOpen || subagentOpen || todosOpen || verboseOpen && !approvalPending && !questionPending || diffView !== void 0 || approvalPending || questionPending;
36209
+ const modalVisible = emailOpen || modelOpen || helpOpen || modeOpen || permissionOpen || resumeOpen || pluginOpen || jobsOpen || statuslineOpen || themeOpen || languageOpen || historyOpen || agentsOpen || subagentOpen || todosOpen || verboseOpen && !approvalPending && !questionPending || diffView !== void 0 || approvalPending || questionPending;
36035
36210
  const closeInspector = (0, import_react.useCallback)(() => {
36036
36211
  setVerboseOpen(false);
36037
36212
  }, []);
@@ -36281,7 +36456,7 @@ function App(props) {
36281
36456
  }
36282
36457
  return (0, import_react.createElement)(Box, { flexDirection: "column", height: Math.max(1, terminalRows - 1) },
36283
36458
  welcomeRows > 0 ? (0, import_react.createElement)(Box, { height: welcomeRows, overflowY: "hidden", flexDirection: "column", justifyContent: "flex-end", flexShrink: 0 },
36284
- (0, import_react.createElement)(Box, { flexShrink: 0 }, !emailOpen && terminalRows >= 10 ? (0, import_react.createElement)(Header, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel }) : (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "DSCODE"))) : void 0,
36459
+ (0, import_react.createElement)(Box, { flexShrink: 0 }, !emailOpen && terminalRows >= 10 ? (0, import_react.createElement)(Header, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel, animated: animations }) : (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "DSCODE"))) : void 0,
36285
36460
  emailOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(DscodeEmailPanel, {
36286
36461
  gmail, imap, columns: terminalColumns, rows: Math.max(3, terminalRows - 8 - composerGutterRows - composerRows),
36287
36462
  close: () => setEmailOpen(false),
@@ -36399,6 +36574,16 @@ function App(props) {
36399
36574
  setThemeOpen(false);
36400
36575
  },
36401
36576
  close: () => setThemeOpen(false)
36577
+ }) : void 0, languageOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(LanguagePanel, {
36578
+ current: dscodeLocale,
36579
+ select: (code) => {
36580
+ dscodeLocale = code;
36581
+ try { dscodeSaveLanguage(code); } catch (error) { notify(dscodeT("language.saveFailed", { error: error instanceof Error ? error.message : String(error) }), "error"); }
36582
+ notify(dscodeT("language.set", { name: dscodeLanguageName(code) }));
36583
+ setLanguageOpen(false);
36584
+ refreshScreen();
36585
+ },
36586
+ close: () => setLanguageOpen(false)
36402
36587
  }) : void 0, historyOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(HistoryPanel, {
36403
36588
  entries: recallSpace,
36404
36589
  fill: (text, index) => {
@@ -36510,6 +36695,7 @@ function App(props) {
36510
36695
  openJobs: () => setJobsOpen(true),
36511
36696
  openStatusline: () => setStatuslineOpen(true),
36512
36697
  openTheme: () => setThemeOpen(true),
36698
+ openLanguage: () => setLanguageOpen(true),
36513
36699
  openHistory: () => setHistoryOpen(true),
36514
36700
  openAgents: () => setAgentsOpen(true),
36515
36701
  openSubagent: () => setSubagentOpen(true),
@@ -36548,7 +36734,10 @@ function App(props) {
36548
36734
  },
36549
36735
  refresh: refreshScreen,
36550
36736
  toggleReasoning: () => {
36551
- setShowReasoning((current) => !current);
36737
+ const next = !showReasoning;
36738
+ setShowReasoning(next);
36739
+ dscodeSaveFlag("verbose", next);
36740
+ notify(dscodeT(next ? "verbose.on" : "verbose.off"));
36552
36741
  refreshScreen();
36553
36742
  },
36554
36743
  loadMentions: props.loadMentions,
@@ -36853,7 +37042,7 @@ const internals = {
36853
37042
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(true);
36854
37043
  try {
36855
37044
  if (process.stdout.isTTY === true) process.stdout.write("\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H");
36856
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_ENABLE : ""));
37045
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : "") + (process.stdout.isTTY === true && dscodeMouseEnabled ? DSCODE_MOUSE_ENABLE : ""));
36857
37046
  const tuiStdin = createSplitStdin(process.stdin);
36858
37047
  const instance = render(element, {
36859
37048
  exitOnCtrlC: false,