kimiflare 0.41.0 → 0.42.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/dist/index.js CHANGED
@@ -204,7 +204,8 @@ async function loadConfig() {
204
204
  plumbingModel: envPlumbingModel ?? parsed.plumbingModel,
205
205
  codeMode: envCodeMode ?? parsed.codeMode,
206
206
  costAttribution: envCostAttribution ?? parsed.costAttribution ?? false,
207
- filePicker: envFilePicker ?? parsed.filePicker ?? true
207
+ filePicker: envFilePicker ?? parsed.filePicker ?? true,
208
+ theme: parsed.theme
208
209
  };
209
210
  }
210
211
  if (parsed.accountId && parsed.apiToken) {
@@ -235,7 +236,8 @@ async function loadConfig() {
235
236
  codeMode: envCodeMode ?? parsed.codeMode ?? true,
236
237
  costAttribution: envCostAttribution ?? parsed.costAttribution ?? true,
237
238
  filePicker: envFilePicker ?? parsed.filePicker ?? true,
238
- cloudMode: envCloudMode ?? parsed.cloudMode
239
+ cloudMode: envCloudMode ?? parsed.cloudMode,
240
+ theme: parsed.theme
239
241
  };
240
242
  }
241
243
  } catch {
@@ -345,10 +347,11 @@ var init_lsp_config = __esm({
345
347
  });
346
348
 
347
349
  // src/util/sse.ts
348
- async function* readSSE(stream, signal) {
350
+ async function* readSSE(stream, signal, idleTimeoutMs) {
349
351
  const reader = stream.getReader();
350
352
  const decoder = new TextDecoder("utf-8");
351
353
  let buffer = "";
354
+ let lastDataAt = Date.now();
352
355
  const onAbort = () => {
353
356
  reader.cancel(new DOMException("aborted", "AbortError")).catch(() => {
354
357
  });
@@ -357,8 +360,15 @@ async function* readSSE(stream, signal) {
357
360
  try {
358
361
  while (true) {
359
362
  if (signal?.aborted) throw new DOMException("aborted", "AbortError");
363
+ if (idleTimeoutMs !== void 0 && Date.now() - lastDataAt > idleTimeoutMs) {
364
+ throw new DOMException(
365
+ `kimiflare: stream idle for ${idleTimeoutMs}ms \u2014 no data received from API`,
366
+ "TimeoutError"
367
+ );
368
+ }
360
369
  const { done, value } = await reader.read();
361
370
  if (done) break;
371
+ lastDataAt = Date.now();
362
372
  buffer += decoder.decode(value, { stream: true });
363
373
  buffer = buffer.replace(/\r\n/g, "\n");
364
374
  let sep3;
@@ -573,7 +583,7 @@ async function* runKimi(opts2) {
573
583
  const meta = readGatewayMeta(res.headers);
574
584
  if (meta) yield { type: "gateway_meta", meta };
575
585
  let lastUsage = null;
576
- for await (const ev of parseStream(res.body, opts2.signal)) {
586
+ for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs)) {
577
587
  if (ev.type === "usage") lastUsage = ev.usage;
578
588
  yield ev;
579
589
  }
@@ -654,11 +664,12 @@ function readGatewayMeta(headers) {
654
664
  if (model) meta.model = model;
655
665
  return Object.keys(meta).length > 0 ? meta : null;
656
666
  }
657
- async function* parseStream(body, signal) {
667
+ async function* parseStream(body, signal, idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS) {
658
668
  const toolCalls = /* @__PURE__ */ new Map();
659
669
  let lastUsage = null;
660
670
  let finishReason = null;
661
- for await (const dataStr of readSSE(body, signal)) {
671
+ let lastDataAt = Date.now();
672
+ for await (const dataStr of readSSE(body, signal, idleTimeoutMs)) {
662
673
  if (dataStr === "[DONE]") break;
663
674
  let chunk = null;
664
675
  try {
@@ -789,7 +800,7 @@ function sleep(ms, signal) {
789
800
  signal?.addEventListener("abort", onAbort, { once: true });
790
801
  });
791
802
  }
792
- var RETRYABLE_CODES, MAX_ATTEMPTS;
803
+ var RETRYABLE_CODES, MAX_ATTEMPTS, DEFAULT_IDLE_TIMEOUT_MS;
793
804
  var init_client = __esm({
794
805
  "src/agent/client.ts"() {
795
806
  "use strict";
@@ -799,6 +810,7 @@ var init_client = __esm({
799
810
  init_messages();
800
811
  RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
801
812
  MAX_ATTEMPTS = 5;
813
+ DEFAULT_IDLE_TIMEOUT_MS = 6e4;
802
814
  }
803
815
  });
804
816
 
@@ -6917,10 +6929,18 @@ var init_narrative = __esm({
6917
6929
  });
6918
6930
 
6919
6931
  // src/ui/tool-view.tsx
6920
- import React2 from "react";
6932
+ import React2, { useEffect, useState } from "react";
6921
6933
  import { Box as Box2, Text as Text2 } from "ink";
6922
6934
  import Spinner from "ink-spinner";
6923
6935
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
6936
+ function formatElapsed(ms) {
6937
+ const total = Math.floor(ms / 1e3);
6938
+ if (total < 1) return "<1s";
6939
+ const m = Math.floor(total / 60);
6940
+ const s = total % 60;
6941
+ if (m === 0) return `${s}s`;
6942
+ return `${m}m ${s}s`;
6943
+ }
6924
6944
  function firstLine(s) {
6925
6945
  const line = s.split("\n").find((l) => l.trim().length > 0) ?? "";
6926
6946
  return line.length <= 120 ? line : line.slice(0, 120) + "\u2026";
@@ -6933,10 +6953,20 @@ var init_tool_view = __esm({
6933
6953
  init_paths();
6934
6954
  init_theme_context();
6935
6955
  init_narrative();
6936
- ToolView = React2.memo(function ToolView2({ evt, verbose }) {
6956
+ ToolView = React2.memo(function ToolView2({ evt, verbose, isRepeated }) {
6937
6957
  const theme = useTheme();
6958
+ const [now2, setNow] = useState(Date.now());
6959
+ useEffect(() => {
6960
+ if (evt.startedAt === void 0) return;
6961
+ if (evt.status !== "running") {
6962
+ setNow(Date.now());
6963
+ return;
6964
+ }
6965
+ const id = setInterval(() => setNow(Date.now()), 1e3);
6966
+ return () => clearInterval(id);
6967
+ }, [evt.status, evt.startedAt]);
6938
6968
  const statusIcon = evt.status === "running" ? /* @__PURE__ */ jsx3(Text2, { color: theme.info.color, children: /* @__PURE__ */ jsx3(Spinner, { type: "dots" }) }) : evt.status === "error" ? /* @__PURE__ */ jsx3(Text2, { color: theme.palette.error, children: "\u2717" }) : /* @__PURE__ */ jsx3(Text2, { color: theme.palette.success, children: "\u2713" });
6939
- const title = evt.render?.title ?? (() => {
6969
+ let title = evt.render?.title ?? (() => {
6940
6970
  try {
6941
6971
  const args = evt.args ? JSON.parse(evt.args) : {};
6942
6972
  return humanizeToolTitle(evt.name, args);
@@ -6944,6 +6974,9 @@ var init_tool_view = __esm({
6944
6974
  return humanizeToolTitle(evt.name);
6945
6975
  }
6946
6976
  })();
6977
+ if (evt.startedAt !== void 0) {
6978
+ title += ` \xB7 ${formatElapsed(now2 - evt.startedAt)}`;
6979
+ }
6947
6980
  const expand = Boolean(evt.expanded || verbose);
6948
6981
  const lines = evt.result ? evt.result.split("\n") : [];
6949
6982
  const showLimit = verbose ? 200 : 20;
@@ -6951,7 +6984,8 @@ var init_tool_view = __esm({
6951
6984
  /* @__PURE__ */ jsxs2(Text2, { children: [
6952
6985
  statusIcon,
6953
6986
  " ",
6954
- /* @__PURE__ */ jsx3(Text2, { color: theme.info.color, children: title })
6987
+ /* @__PURE__ */ jsx3(Text2, { color: theme.info.color, children: title }),
6988
+ isRepeated ? /* @__PURE__ */ jsx3(Text2, { color: theme.warn, children: " \u26A0 repeated" }) : null
6955
6989
  ] }),
6956
6990
  evt.render?.diff ? /* @__PURE__ */ jsx3(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx3(DiffView, { ...evt.render.diff }) }) : null,
6957
6991
  evt.result && expand ? /* @__PURE__ */ jsxs2(
@@ -7027,6 +7061,15 @@ function parseBlocks(src) {
7027
7061
  out.push({ kind: "quote", text: quoteLines.join("\n") });
7028
7062
  continue;
7029
7063
  }
7064
+ if (/^\s*\d+\.\s+/.test(line)) {
7065
+ const items = [];
7066
+ while (i < lines.length && /^\s*\d+\.\s+/.test(lines[i])) {
7067
+ items.push(lines[i].replace(/^\s*\d+\.\s+/, ""));
7068
+ i++;
7069
+ }
7070
+ out.push({ kind: "numbered", items });
7071
+ continue;
7072
+ }
7030
7073
  if (/^\s*[-*]\s+/.test(line)) {
7031
7074
  const items = [];
7032
7075
  while (i < lines.length && /^\s*[-*]\s+/.test(lines[i])) {
@@ -7036,9 +7079,23 @@ function parseBlocks(src) {
7036
7079
  out.push({ kind: "bullet", items });
7037
7080
  continue;
7038
7081
  }
7082
+ if (i + 1 < lines.length && /^\|/.test(line) && /^\|[\s\-:|]+\|$/.test(lines[i + 1])) {
7083
+ const headerLine = line;
7084
+ const headers = headerLine.split("|").slice(1, -1).map((h) => h.trim());
7085
+ i += 2;
7086
+ const rows = [];
7087
+ while (i < lines.length && /^\|/.test(lines[i])) {
7088
+ rows.push(
7089
+ lines[i].split("|").slice(1, -1).map((c) => c.trim())
7090
+ );
7091
+ i++;
7092
+ }
7093
+ out.push({ kind: "table", headers, rows });
7094
+ continue;
7095
+ }
7039
7096
  const paraLines = [line];
7040
7097
  i++;
7041
- while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,3})\s+/.test(lines[i]) && !/^\s*[-*]\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) && !/^```/.test(lines[i])) {
7098
+ while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,3})\s+/.test(lines[i]) && !/^\s*[-*]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) && !/^```/.test(lines[i]) && !/^\|/.test(lines[i])) {
7042
7099
  paraLines.push(lines[i]);
7043
7100
  i++;
7044
7101
  }
@@ -7051,7 +7108,18 @@ function renderInline(src, theme) {
7051
7108
  return segments.map((seg, i) => {
7052
7109
  if (seg.kind === "bold") return /* @__PURE__ */ jsx4(Text3, { bold: true, children: seg.text }, i);
7053
7110
  if (seg.kind === "italic") return /* @__PURE__ */ jsx4(Text3, { italic: true, children: seg.text }, i);
7054
- if (seg.kind === "code") return /* @__PURE__ */ jsx4(Text3, { color: theme.tool, children: seg.text }, i);
7111
+ if (seg.kind === "code") {
7112
+ const color = theme.codeInline ?? theme.tool;
7113
+ return /* @__PURE__ */ jsx4(Text3, { color, children: seg.text }, i);
7114
+ }
7115
+ if (seg.kind === "strikethrough") {
7116
+ const color = theme.strikethrough ?? theme.muted?.color ?? theme.palette.secondary;
7117
+ return /* @__PURE__ */ jsx4(Text3, { strikethrough: true, color, children: seg.text }, i);
7118
+ }
7119
+ if (seg.kind === "link") {
7120
+ const color = theme.link ?? theme.accent;
7121
+ return /* @__PURE__ */ jsx4(Text3, { underline: true, color, children: seg.text }, i);
7122
+ }
7055
7123
  return /* @__PURE__ */ jsx4(Text3, { children: seg.text }, i);
7056
7124
  });
7057
7125
  }
@@ -7076,6 +7144,30 @@ function parseInline(src) {
7076
7144
  continue;
7077
7145
  }
7078
7146
  }
7147
+ if (ch === "~" && src[i + 1] === "~") {
7148
+ const end = src.indexOf("~~", i + 2);
7149
+ if (end > i + 1) {
7150
+ flush();
7151
+ out.push({ kind: "strikethrough", text: src.slice(i + 2, end) });
7152
+ i = end + 2;
7153
+ continue;
7154
+ }
7155
+ }
7156
+ if (ch === "[") {
7157
+ const closeBracket = src.indexOf("]", i + 1);
7158
+ const openParen = closeBracket >= 0 ? src.indexOf("(", closeBracket) : -1;
7159
+ const closeParen = openParen >= 0 ? src.indexOf(")", openParen) : -1;
7160
+ if (closeParen > openParen && openParen === closeBracket + 1) {
7161
+ flush();
7162
+ out.push({
7163
+ kind: "link",
7164
+ text: src.slice(i + 1, closeBracket),
7165
+ url: src.slice(openParen + 1, closeParen)
7166
+ });
7167
+ i = closeParen + 1;
7168
+ continue;
7169
+ }
7170
+ }
7079
7171
  if (ch === "*" && src[i + 1] === "*") {
7080
7172
  const end = src.indexOf("**", i + 2);
7081
7173
  if (end > i + 1) {
@@ -7135,11 +7227,53 @@ var init_markdown = __esm({
7135
7227
  /* @__PURE__ */ jsx4(Text3, { children: renderInline(item, theme) })
7136
7228
  ] }, i)) });
7137
7229
  }
7230
+ if (block.kind === "numbered") {
7231
+ return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", children: block.items.map((item, idx) => /* @__PURE__ */ jsxs3(Box3, { children: [
7232
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.accent, children: [
7233
+ " ",
7234
+ idx + 1,
7235
+ ". "
7236
+ ] }),
7237
+ /* @__PURE__ */ jsx4(Text3, { children: renderInline(item, theme) })
7238
+ ] }, idx)) });
7239
+ }
7138
7240
  if (block.kind === "quote") {
7139
- return /* @__PURE__ */ jsx4(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text3, { color: theme.info.color, italic: true, children: renderInline(block.text, theme) }) });
7241
+ const quoteColor = theme.blockquote?.color ?? theme.info.color;
7242
+ const dim = theme.blockquote?.dim ?? false;
7243
+ return /* @__PURE__ */ jsx4(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text3, { color: quoteColor, dimColor: dim, italic: true, children: renderInline(block.text, theme) }) });
7140
7244
  }
7141
7245
  if (block.kind === "code") {
7142
- return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", marginLeft: 2, children: block.text.split("\n").map((l, i) => /* @__PURE__ */ jsx4(Text3, { color: theme.tool, children: l }, i)) });
7246
+ const codeColor = theme.codeBlock ?? theme.tool;
7247
+ return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", marginLeft: 2, children: block.text.split("\n").map((l, i) => /* @__PURE__ */ jsx4(Text3, { color: codeColor, children: l }, i)) });
7248
+ }
7249
+ if (block.kind === "table") {
7250
+ const borderColor = theme.tableBorder ?? theme.accent;
7251
+ const headerColor = theme.tableHeader ?? theme.accent;
7252
+ const cellColor = theme.tableCell ?? theme.palette.foreground;
7253
+ const colCount = block.headers.length;
7254
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 2, children: [
7255
+ /* @__PURE__ */ jsxs3(Box3, { children: [
7256
+ /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: "\u2502 " }),
7257
+ block.headers.map((h, ci) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
7258
+ /* @__PURE__ */ jsx4(Text3, { bold: true, color: headerColor, children: h }),
7259
+ ci < colCount - 1 && /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: " \u2502 " })
7260
+ ] }, ci)),
7261
+ /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: " \u2502" })
7262
+ ] }),
7263
+ /* @__PURE__ */ jsx4(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: borderColor, children: [
7264
+ "\u251C",
7265
+ Array(colCount).fill("\u2500".repeat(8)).join("\u253C"),
7266
+ "\u2524"
7267
+ ] }) }),
7268
+ block.rows.map((row, ri) => /* @__PURE__ */ jsxs3(Box3, { children: [
7269
+ /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: "\u2502 " }),
7270
+ row.map((cell, ci) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
7271
+ /* @__PURE__ */ jsx4(Text3, { color: cellColor, children: cell }),
7272
+ ci < colCount - 1 && /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: " \u2502 " })
7273
+ ] }, ci)),
7274
+ /* @__PURE__ */ jsx4(Text3, { color: borderColor, children: " \u2502" })
7275
+ ] }, ri))
7276
+ ] });
7143
7277
  }
7144
7278
  return /* @__PURE__ */ jsx4(Text3, { children: renderInline(block.text, theme) });
7145
7279
  });
@@ -7151,6 +7285,9 @@ import React4 from "react";
7151
7285
  import { Box as Box4, Text as Text4, Static } from "ink";
7152
7286
  import Spinner2 from "ink-spinner";
7153
7287
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
7288
+ function toolSignature(name, args) {
7289
+ return `${name}:${args}`;
7290
+ }
7154
7291
  var ChatView, EventView;
7155
7292
  var init_chat = __esm({
7156
7293
  "src/ui/chat.tsx"() {
@@ -7162,6 +7299,17 @@ var init_chat = __esm({
7162
7299
  const theme = useTheme();
7163
7300
  const finalized = [];
7164
7301
  const active = [];
7302
+ const toolCounts = /* @__PURE__ */ new Map();
7303
+ for (const e of events) {
7304
+ if (e.kind === "tool") {
7305
+ const sig = toolSignature(e.name, e.args);
7306
+ toolCounts.set(sig, (toolCounts.get(sig) ?? 0) + 1);
7307
+ }
7308
+ }
7309
+ const repeatedSigs = /* @__PURE__ */ new Set();
7310
+ for (const [sig, count] of toolCounts) {
7311
+ if (count >= 3) repeatedSigs.add(sig);
7312
+ }
7165
7313
  for (let i = 0; i < events.length; i++) {
7166
7314
  const e = events[i];
7167
7315
  if (suppressTools && e.kind === "tool") continue;
@@ -7177,14 +7325,14 @@ var init_chat = __esm({
7177
7325
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
7178
7326
  /* @__PURE__ */ jsx5(Static, { items: finalized, children: (item) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
7179
7327
  item.showSeparator && /* @__PURE__ */ jsx5(Box4, { marginY: 1, children: /* @__PURE__ */ jsx5(Text4, { color: theme.info.color, children: "\u2500".repeat(40) }) }),
7180
- /* @__PURE__ */ jsx5(EventView, { evt: item.evt, showReasoning, verbose })
7328
+ /* @__PURE__ */ jsx5(EventView, { evt: item.evt, showReasoning, verbose, repeatedSigs })
7181
7329
  ] }, item.id) }),
7182
7330
  active.map((e, i) => {
7183
7331
  const prevEvt = i > 0 ? active[i - 1] : finalized[finalized.length - 1]?.evt;
7184
7332
  const showSeparator = e.kind === "user" && prevEvt && (prevEvt.kind === "assistant" || prevEvt.kind === "tool");
7185
7333
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
7186
7334
  showSeparator && /* @__PURE__ */ jsx5(Box4, { marginY: 1, children: /* @__PURE__ */ jsx5(Text4, { color: theme.info.color, children: "\u2500".repeat(40) }) }),
7187
- /* @__PURE__ */ jsx5(EventView, { evt: e, showReasoning, verbose })
7335
+ /* @__PURE__ */ jsx5(EventView, { evt: e, showReasoning, verbose, repeatedSigs })
7188
7336
  ] }, e.key);
7189
7337
  })
7190
7338
  ] });
@@ -7192,7 +7340,8 @@ var init_chat = __esm({
7192
7340
  EventView = React4.memo(function EventView2({
7193
7341
  evt,
7194
7342
  showReasoning,
7195
- verbose
7343
+ verbose,
7344
+ repeatedSigs
7196
7345
  }) {
7197
7346
  const theme = useTheme();
7198
7347
  if (evt.kind === "user") {
@@ -7222,7 +7371,8 @@ var init_chat = __esm({
7222
7371
  ] });
7223
7372
  }
7224
7373
  if (evt.kind === "tool") {
7225
- return /* @__PURE__ */ jsx5(ToolView, { evt, verbose });
7374
+ const isRepeated = repeatedSigs?.has(toolSignature(evt.name, evt.args)) ?? false;
7375
+ return /* @__PURE__ */ jsx5(ToolView, { evt, verbose, isRepeated });
7226
7376
  }
7227
7377
  if (evt.kind === "info") {
7228
7378
  return /* @__PURE__ */ jsxs4(Text4, { color: theme.info.color, children: [
@@ -7269,24 +7419,27 @@ var init_pricing = __esm({
7269
7419
  });
7270
7420
 
7271
7421
  // src/ui/status.tsx
7272
- import { useEffect, useState } from "react";
7422
+ import { useEffect as useEffect2, useState as useState2 } from "react";
7273
7423
  import { Box as Box5, Text as Text5 } from "ink";
7274
7424
  import Spinner3 from "ink-spinner";
7275
7425
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
7276
- function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode, cloudMode, cloudBudget, kimiMdStale }) {
7426
+ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode, cloudMode, cloudBudget, phase, currentTool, lastActivityAt, kimiMdStale }) {
7277
7427
  const theme = useTheme();
7278
- const [now2, setNow] = useState(Date.now());
7428
+ const [now2, setNow] = useState2(Date.now());
7279
7429
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
7280
7430
  const warn = usage && usage.prompt_tokens / contextLimit >= 0.8;
7281
- useEffect(() => {
7431
+ useEffect2(() => {
7282
7432
  if (!thinking || turnStartedAt === null) return;
7283
7433
  const id = setInterval(() => setNow(Date.now()), 1e3);
7284
7434
  return () => clearInterval(id);
7285
7435
  }, [thinking, turnStartedAt]);
7286
- const elapsed = turnStartedAt !== null ? formatElapsed(now2 - turnStartedAt) : null;
7436
+ const elapsed = turnStartedAt !== null ? formatElapsed2(now2 - turnStartedAt) : null;
7287
7437
  const leftParts = [`${shortModel(model)}`, effort];
7288
7438
  if (cloudMode) leftParts.push("CLOUD");
7289
7439
  if (codeMode) leftParts.push("CODE");
7440
+ const phaseLabel = phase === "generating" ? "generating" : phase === "executing" ? `executing ${currentTool ?? ""}` : phase === "waiting" ? "waiting" : "thinking";
7441
+ const idleMs = lastActivityAt && thinking ? now2 - lastActivityAt : 0;
7442
+ const idleLabel = idleMs > 3e4 ? ` (idle ${formatElapsed2(Math.floor(idleMs / 1e3))})` : "";
7290
7443
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
7291
7444
  /* @__PURE__ */ jsxs5(Box5, { children: [
7292
7445
  /* @__PURE__ */ jsxs5(Text5, { color: modeColor, bold: true, children: [
@@ -7298,8 +7451,9 @@ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode,
7298
7451
  thinking ? /* @__PURE__ */ jsxs5(Text5, { color: theme.spinner, children: [
7299
7452
  /* @__PURE__ */ jsx6(Spinner3, { type: "dots" }),
7300
7453
  " ",
7301
- "thinking",
7302
- elapsed ? ` \xB7 ${elapsed}` : ""
7454
+ phaseLabel,
7455
+ elapsed ? ` \xB7 ${elapsed}` : "",
7456
+ idleLabel
7303
7457
  ] }) : /* @__PURE__ */ jsxs5(Text5, { color: theme.info.color, children: [
7304
7458
  leftParts.join(" \xB7 "),
7305
7459
  " \xB7 ready"
@@ -7370,7 +7524,7 @@ function shortModel(m) {
7370
7524
  const last = m.split("/").at(-1) ?? m;
7371
7525
  return last;
7372
7526
  }
7373
- function formatElapsed(ms) {
7527
+ function formatElapsed2(ms) {
7374
7528
  const total = Math.floor(ms / 1e3);
7375
7529
  const m = Math.floor(total / 60);
7376
7530
  const s = total % 60;
@@ -7460,14 +7614,14 @@ var init_limit_modal = __esm({
7460
7614
  });
7461
7615
 
7462
7616
  // src/ui/resume-picker.tsx
7463
- import { useState as useState2 } from "react";
7617
+ import { useState as useState3 } from "react";
7464
7618
  import { Box as Box8, Text as Text8, useWindowSize } from "ink";
7465
7619
  import SelectInput3 from "ink-select-input";
7466
7620
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
7467
7621
  function ResumePicker({ sessions, onPick }) {
7468
7622
  const theme = useTheme();
7469
7623
  const { rows } = useWindowSize();
7470
- const [page, setPage] = useState2(0);
7624
+ const [page, setPage] = useState3(0);
7471
7625
  const pageSize = Math.max(MIN_PAGE_SIZE, rows - HEADER_ROWS - FOOTER_ROWS);
7472
7626
  const totalPages = Math.max(1, Math.ceil(sessions.length / pageSize));
7473
7627
  const safePage = Math.min(page, totalPages - 1);
@@ -7549,16 +7703,16 @@ var init_resume_picker = __esm({
7549
7703
  });
7550
7704
 
7551
7705
  // src/ui/task-list.tsx
7552
- import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
7706
+ import { useEffect as useEffect3, useRef, useState as useState4 } from "react";
7553
7707
  import { Box as Box9, Text as Text9 } from "ink";
7554
7708
  import Spinner4 from "ink-spinner";
7555
7709
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
7556
7710
  function TaskList({ tasks, startedAt, tokensDelta }) {
7557
7711
  const theme = useTheme();
7558
- const [now2, setNow] = useState3(Date.now());
7712
+ const [now2, setNow] = useState4(Date.now());
7559
7713
  const tasksRef = useRef(tasks);
7560
7714
  tasksRef.current = tasks;
7561
- useEffect2(() => {
7715
+ useEffect3(() => {
7562
7716
  if (startedAt === null) return;
7563
7717
  const id = setInterval(() => {
7564
7718
  setNow(Date.now());
@@ -7575,7 +7729,7 @@ function TaskList({ tasks, startedAt, tokensDelta }) {
7575
7729
  const total = tasks.length;
7576
7730
  const allDone = done === total;
7577
7731
  const header = active ? active.title : allDone ? `${total} tasks done` : `${done}/${total}`;
7578
- const elapsed = startedAt ? formatElapsed2(now2 - startedAt) : null;
7732
+ const elapsed = startedAt ? formatElapsed3(now2 - startedAt) : null;
7579
7733
  const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens2(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
7580
7734
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
7581
7735
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
@@ -7621,7 +7775,7 @@ function TaskRow({ task }) {
7621
7775
  task.title
7622
7776
  ] });
7623
7777
  }
7624
- function formatElapsed2(ms) {
7778
+ function formatElapsed3(ms) {
7625
7779
  const total = Math.floor(ms / 1e3);
7626
7780
  const m = Math.floor(total / 60);
7627
7781
  const s = total % 60;
@@ -8162,7 +8316,7 @@ var init_source = __esm({
8162
8316
  });
8163
8317
 
8164
8318
  // src/ui/text-input.tsx
8165
- import { useState as useState4, useEffect as useEffect3, useRef as useRef2 } from "react";
8319
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2 } from "react";
8166
8320
  import { Text as Text10, useInput } from "ink";
8167
8321
  import { jsx as jsx11 } from "react/jsx-runtime";
8168
8322
  function shouldTreatAsPaste(input) {
@@ -8204,14 +8358,14 @@ function CustomTextInput({
8204
8358
  onPickerSelect,
8205
8359
  onPickerCancel
8206
8360
  }) {
8207
- const [internalCursor, setInternalCursor] = useState4(value.length);
8361
+ const [internalCursor, setInternalCursor] = useState5(value.length);
8208
8362
  const cursorOffset = controlledCursor ?? internalCursor;
8209
8363
  const setCursorOffset = (offset) => {
8210
8364
  setInternalCursor(offset);
8211
8365
  onCursorChange?.(offset);
8212
8366
  };
8213
8367
  const pastesRef = useRef2(/* @__PURE__ */ new Map());
8214
- useEffect3(() => {
8368
+ useEffect4(() => {
8215
8369
  if (!focus) return;
8216
8370
  const next = cursorOffset > value.length ? value.length : cursorOffset;
8217
8371
  if (next !== cursorOffset) {
@@ -8396,7 +8550,7 @@ var init_text_input = __esm({
8396
8550
  });
8397
8551
 
8398
8552
  // src/ui/onboarding.tsx
8399
- import { useState as useState5, useEffect as useEffect4, useCallback } from "react";
8553
+ import { useState as useState6, useEffect as useEffect5, useCallback } from "react";
8400
8554
  import { Box as Box10, Text as Text11, useInput as useInput2 } from "ink";
8401
8555
  import SelectInput4 from "ink-select-input";
8402
8556
  import Spinner5 from "ink-spinner";
@@ -8419,15 +8573,15 @@ function formatRemaining(ms) {
8419
8573
  }
8420
8574
  function Onboarding({ onDone, onCancel }) {
8421
8575
  const theme = useTheme();
8422
- const [step, setStep] = useState5("mode");
8423
- const [mode, setMode] = useState5("byok");
8424
- const [accountId, setAccountId] = useState5("");
8425
- const [apiToken, setApiToken] = useState5("");
8426
- const [model, setModel] = useState5(DEFAULT_MODEL);
8427
- const [savedPath, setSavedPath] = useState5(null);
8428
- const [cloudAuth, setCloudAuth] = useState5(null);
8429
- const [pollTick, setPollTick] = useState5(0);
8430
- useEffect4(() => {
8576
+ const [step, setStep] = useState6("mode");
8577
+ const [mode, setMode] = useState6("byok");
8578
+ const [accountId, setAccountId] = useState6("");
8579
+ const [apiToken, setApiToken] = useState6("");
8580
+ const [model, setModel] = useState6(DEFAULT_MODEL);
8581
+ const [savedPath, setSavedPath] = useState6(null);
8582
+ const [cloudAuth, setCloudAuth] = useState6(null);
8583
+ const [pollTick, setPollTick] = useState6(0);
8584
+ useEffect5(() => {
8431
8585
  if (step !== "cloudAuth" || !cloudAuth) return;
8432
8586
  if (cloudAuth.phase !== "polling") return;
8433
8587
  let cancelled = false;
@@ -8830,13 +8984,13 @@ var init_welcome = __esm({
8830
8984
  });
8831
8985
 
8832
8986
  // src/ui/help-menu.tsx
8833
- import { useState as useState6 } from "react";
8987
+ import { useState as useState7 } from "react";
8834
8988
  import { Box as Box12, Text as Text13, useInput as useInput3 } from "ink";
8835
8989
  import SelectInput5 from "ink-select-input";
8836
8990
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
8837
8991
  function HelpMenu({ customCommands, costAttributionEnabled, cloudMode, onDone, onCommand }) {
8838
8992
  const theme = useTheme();
8839
- const [page, setPage] = useState6("main");
8993
+ const [page, setPage] = useState7("main");
8840
8994
  const customs = customCommands ?? [];
8841
8995
  useInput3((_input, key) => {
8842
8996
  if (key.escape) {
@@ -9164,17 +9318,17 @@ var init_tui_deploy = __esm({
9164
9318
  });
9165
9319
 
9166
9320
  // src/ui/remote-dashboard.tsx
9167
- import { useEffect as useEffect5, useState as useState7 } from "react";
9321
+ import { useEffect as useEffect6, useState as useState8 } from "react";
9168
9322
  import { Box as Box13, Text as Text14, useInput as useInput4 } from "ink";
9169
9323
  import SelectInput6 from "ink-select-input";
9170
9324
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
9171
9325
  function RemoteDashboard({ onSelect, onCancel }) {
9172
9326
  const theme = useTheme();
9173
- const [sessions, setSessions] = useState7([]);
9174
- const [loading, setLoading] = useState7(true);
9175
- const [error, setError] = useState7(null);
9176
- const [refreshing, setRefreshing] = useState7(false);
9177
- useEffect5(() => {
9327
+ const [sessions, setSessions] = useState8([]);
9328
+ const [loading, setLoading] = useState8(true);
9329
+ const [error, setError] = useState8(null);
9330
+ const [refreshing, setRefreshing] = useState8(false);
9331
+ useEffect6(() => {
9178
9332
  loadSessions();
9179
9333
  }, []);
9180
9334
  async function loadSessions() {
@@ -9288,7 +9442,7 @@ function RemoteSessionDetail({
9288
9442
  onCancel
9289
9443
  }) {
9290
9444
  const theme = useTheme();
9291
- const [cancelling, setCancelling] = useState7(false);
9445
+ const [cancelling, setCancelling] = useState8(false);
9292
9446
  useInput4((input, key) => {
9293
9447
  if (key.escape) {
9294
9448
  onBack();
@@ -11355,21 +11509,21 @@ var init_save = __esm({
11355
11509
  });
11356
11510
 
11357
11511
  // src/ui/command-wizard.tsx
11358
- import { useState as useState8 } from "react";
11512
+ import { useState as useState9 } from "react";
11359
11513
  import { Box as Box14, Text as Text15, useInput as useInput5, useWindowSize as useWindowSize2 } from "ink";
11360
11514
  import SelectInput7 from "ink-select-input";
11361
11515
  import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
11362
11516
  function CommandWizard({ mode, initial, existingNames, builtinNames, onDone, onSave }) {
11363
11517
  const theme = useTheme();
11364
- const [step, setStep] = useState8("name");
11365
- const [name, setName] = useState8(initial?.name ?? "");
11366
- const [description, setDescription] = useState8(initial?.description ?? "");
11367
- const [template, setTemplate] = useState8(initial?.template ?? "");
11368
- const [cmdMode, setCmdMode] = useState8(initial?.mode);
11369
- const [cmdEffort, setCmdEffort] = useState8(initial?.effort);
11370
- const [cmdModel, setCmdModel] = useState8(initial?.model);
11371
- const [source, setSource] = useState8(initial?.source ?? "project");
11372
- const [error, setError] = useState8(null);
11518
+ const [step, setStep] = useState9("name");
11519
+ const [name, setName] = useState9(initial?.name ?? "");
11520
+ const [description, setDescription] = useState9(initial?.description ?? "");
11521
+ const [template, setTemplate] = useState9(initial?.template ?? "");
11522
+ const [cmdMode, setCmdMode] = useState9(initial?.mode);
11523
+ const [cmdEffort, setCmdEffort] = useState9(initial?.effort);
11524
+ const [cmdModel, setCmdModel] = useState9(initial?.model);
11525
+ const [source, setSource] = useState9(initial?.source ?? "project");
11526
+ const [error, setError] = useState9(null);
11373
11527
  const { columns } = useWindowSize2();
11374
11528
  const totalSteps = 5;
11375
11529
  const stepIndex = step === "name" ? 1 : step === "description" ? 2 : step === "template" ? 3 : step === "advanced" || step === "mode" || step === "effort" || step === "model" ? 4 : step === "location" ? 4 : 5;
@@ -12311,20 +12465,20 @@ var init_command_list = __esm({
12311
12465
  });
12312
12466
 
12313
12467
  // src/ui/lsp-wizard.tsx
12314
- import { useState as useState9 } from "react";
12468
+ import { useState as useState10 } from "react";
12315
12469
  import { Box as Box17, Text as Text18 } from "ink";
12316
12470
  import SelectInput9 from "ink-select-input";
12317
12471
  import { spawn as spawn3 } from "child_process";
12318
12472
  import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
12319
12473
  function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
12320
12474
  const theme = useTheme();
12321
- const [page, setPage] = useState9("main");
12322
- const [selectedPreset, setSelectedPreset] = useState9(null);
12323
- const [customName, setCustomName] = useState9("");
12324
- const [customCommand, setCustomCommand] = useState9("");
12325
- const [installState, setInstallState] = useState9({ status: "idle", output: "" });
12326
- const [pendingServers, setPendingServers] = useState9(null);
12327
- const [pendingEnabled, setPendingEnabled] = useState9(true);
12475
+ const [page, setPage] = useState10("main");
12476
+ const [selectedPreset, setSelectedPreset] = useState10(null);
12477
+ const [customName, setCustomName] = useState10("");
12478
+ const [customCommand, setCustomCommand] = useState10("");
12479
+ const [installState, setInstallState] = useState10({ status: "idle", output: "" });
12480
+ const [pendingServers, setPendingServers] = useState10(null);
12481
+ const [pendingEnabled, setPendingEnabled] = useState10(true);
12328
12482
  const runInstall = (command) => {
12329
12483
  setInstallState({ status: "running", output: "Installing..." });
12330
12484
  const child = spawn3("bash", ["-lc", command], {
@@ -12885,39 +13039,628 @@ var init_theme_picker = __esm({
12885
13039
  }
12886
13040
  });
12887
13041
 
13042
+ // src/ui/themes/everforest-dark.json
13043
+ var everforest_dark_default;
13044
+ var init_everforest_dark = __esm({
13045
+ "src/ui/themes/everforest-dark.json"() {
13046
+ everforest_dark_default = {
13047
+ name: "everforest-dark",
13048
+ label: "Everforest Dark",
13049
+ palette: {
13050
+ background: "#2b3339",
13051
+ foreground: "#d3c6aa",
13052
+ primary: "#a7c080",
13053
+ secondary: "#7a8478",
13054
+ success: "#a7c080",
13055
+ error: "#e67e80"
13056
+ },
13057
+ user: "#a7c080",
13058
+ assistant: null,
13059
+ reasoning: { color: "#7a8478", dim: true },
13060
+ info: { color: "#d3c6aa", dim: false },
13061
+ error: "#e67e80",
13062
+ warn: "#dbbc7f",
13063
+ tool: "#7fbbb3",
13064
+ spinner: "#a7c080",
13065
+ permission: "#e67e80",
13066
+ queue: { color: "#7a8478", dim: true },
13067
+ accent: "#a7c080",
13068
+ modeBadge: {
13069
+ plan: "#a7c080",
13070
+ auto: "#a7c080",
13071
+ edit: "#e67e80"
13072
+ },
13073
+ blockquote: { color: "#7a8478", dim: true },
13074
+ codeInline: "#7fbbb3",
13075
+ codeBlock: "#7fbbb3",
13076
+ link: "#a7c080",
13077
+ strikethrough: "#7a8478",
13078
+ tableBorder: "#4b565c",
13079
+ tableHeader: "#d3c6aa",
13080
+ tableCell: "#d3c6aa",
13081
+ muted: { color: "#7a8478", dim: true }
13082
+ };
13083
+ }
13084
+ });
13085
+
13086
+ // src/ui/themes/everforest-light.json
13087
+ var everforest_light_default;
13088
+ var init_everforest_light = __esm({
13089
+ "src/ui/themes/everforest-light.json"() {
13090
+ everforest_light_default = {
13091
+ name: "everforest-light",
13092
+ label: "Everforest Light",
13093
+ palette: {
13094
+ background: "#fdf6e3",
13095
+ foreground: "#5c6a72",
13096
+ primary: "#8da101",
13097
+ secondary: "#939f91",
13098
+ success: "#8da101",
13099
+ error: "#f85552"
13100
+ },
13101
+ user: "#8da101",
13102
+ assistant: null,
13103
+ reasoning: { color: "#939f91", dim: true },
13104
+ info: { color: "#5c6a72", dim: false },
13105
+ error: "#f85552",
13106
+ warn: "#dfa000",
13107
+ tool: "#3a94c5",
13108
+ spinner: "#8da101",
13109
+ permission: "#f85552",
13110
+ queue: { color: "#939f91", dim: true },
13111
+ accent: "#8da101",
13112
+ modeBadge: {
13113
+ plan: "#8da101",
13114
+ auto: "#8da101",
13115
+ edit: "#f85552"
13116
+ },
13117
+ blockquote: { color: "#939f91", dim: true },
13118
+ codeInline: "#3a94c5",
13119
+ codeBlock: "#3a94c5",
13120
+ link: "#8da101",
13121
+ strikethrough: "#939f91",
13122
+ tableBorder: "#bdc3af",
13123
+ tableHeader: "#5c6a72",
13124
+ tableCell: "#5c6a72",
13125
+ muted: { color: "#939f91", dim: true }
13126
+ };
13127
+ }
13128
+ });
13129
+
13130
+ // src/ui/themes/kanagawa-dark.json
13131
+ var kanagawa_dark_default;
13132
+ var init_kanagawa_dark = __esm({
13133
+ "src/ui/themes/kanagawa-dark.json"() {
13134
+ kanagawa_dark_default = {
13135
+ name: "kanagawa-dark",
13136
+ label: "Kanagawa Dark",
13137
+ palette: {
13138
+ background: "#1f1f28",
13139
+ foreground: "#dcd7ba",
13140
+ primary: "#7e9cd8",
13141
+ secondary: "#727169",
13142
+ success: "#98bb6c",
13143
+ error: "#ff5d62"
13144
+ },
13145
+ user: "#7e9cd8",
13146
+ assistant: null,
13147
+ reasoning: { color: "#727169", dim: true },
13148
+ info: { color: "#dcd7ba", dim: false },
13149
+ error: "#ff5d62",
13150
+ warn: "#e6c384",
13151
+ tool: "#7fb4ca",
13152
+ spinner: "#7e9cd8",
13153
+ permission: "#ff5d62",
13154
+ queue: { color: "#727169", dim: true },
13155
+ accent: "#7e9cd8",
13156
+ modeBadge: {
13157
+ plan: "#7e9cd8",
13158
+ auto: "#98bb6c",
13159
+ edit: "#ff5d62"
13160
+ },
13161
+ blockquote: { color: "#727169", dim: true },
13162
+ codeInline: "#7fb4ca",
13163
+ codeBlock: "#7fb4ca",
13164
+ link: "#7e9cd8",
13165
+ strikethrough: "#727169",
13166
+ tableBorder: "#54546d",
13167
+ tableHeader: "#dcd7ba",
13168
+ tableCell: "#dcd7ba",
13169
+ muted: { color: "#727169", dim: true }
13170
+ };
13171
+ }
13172
+ });
13173
+
13174
+ // src/ui/themes/dracula-dark.json
13175
+ var dracula_dark_default;
13176
+ var init_dracula_dark = __esm({
13177
+ "src/ui/themes/dracula-dark.json"() {
13178
+ dracula_dark_default = {
13179
+ name: "dracula-dark",
13180
+ label: "Dracula Dark",
13181
+ palette: {
13182
+ background: "#282a36",
13183
+ foreground: "#f8f8f2",
13184
+ primary: "#ff79c6",
13185
+ secondary: "#6272a4",
13186
+ success: "#50fa7b",
13187
+ error: "#ff5555"
13188
+ },
13189
+ user: "#ff79c6",
13190
+ assistant: null,
13191
+ reasoning: { color: "#6272a4", dim: true },
13192
+ info: { color: "#f8f8f2", dim: false },
13193
+ error: "#ff5555",
13194
+ warn: "#f1fa8c",
13195
+ tool: "#8be9fd",
13196
+ spinner: "#ff79c6",
13197
+ permission: "#ff5555",
13198
+ queue: { color: "#6272a4", dim: true },
13199
+ accent: "#ff79c6",
13200
+ modeBadge: {
13201
+ plan: "#ff79c6",
13202
+ auto: "#50fa7b",
13203
+ edit: "#ff5555"
13204
+ },
13205
+ blockquote: { color: "#6272a4", dim: true },
13206
+ codeInline: "#8be9fd",
13207
+ codeBlock: "#8be9fd",
13208
+ link: "#ff79c6",
13209
+ strikethrough: "#6272a4",
13210
+ tableBorder: "#44475a",
13211
+ tableHeader: "#f8f8f2",
13212
+ tableCell: "#f8f8f2",
13213
+ muted: { color: "#6272a4", dim: true }
13214
+ };
13215
+ }
13216
+ });
13217
+
13218
+ // src/ui/themes/tokyo-night.json
13219
+ var tokyo_night_default;
13220
+ var init_tokyo_night = __esm({
13221
+ "src/ui/themes/tokyo-night.json"() {
13222
+ tokyo_night_default = {
13223
+ name: "tokyo-night",
13224
+ label: "Tokyo Night",
13225
+ palette: {
13226
+ background: "#1a1b26",
13227
+ foreground: "#a9b1d6",
13228
+ primary: "#7aa2f7",
13229
+ secondary: "#565f89",
13230
+ success: "#9ece6a",
13231
+ error: "#f7768e"
13232
+ },
13233
+ user: "#7aa2f7",
13234
+ assistant: null,
13235
+ reasoning: { color: "#565f89", dim: true },
13236
+ info: { color: "#a9b1d6", dim: false },
13237
+ error: "#f7768e",
13238
+ warn: "#e0af68",
13239
+ tool: "#bb9af7",
13240
+ spinner: "#7aa2f7",
13241
+ permission: "#f7768e",
13242
+ queue: { color: "#565f89", dim: true },
13243
+ accent: "#7aa2f7",
13244
+ modeBadge: {
13245
+ plan: "#7aa2f7",
13246
+ auto: "#9ece6a",
13247
+ edit: "#f7768e"
13248
+ },
13249
+ blockquote: { color: "#565f89", dim: true },
13250
+ codeInline: "#bb9af7",
13251
+ codeBlock: "#bb9af7",
13252
+ link: "#7aa2f7",
13253
+ strikethrough: "#565f89",
13254
+ tableBorder: "#414868",
13255
+ tableHeader: "#c0caf5",
13256
+ tableCell: "#a9b1d6",
13257
+ muted: { color: "#565f89", dim: true }
13258
+ };
13259
+ }
13260
+ });
13261
+
13262
+ // src/ui/themes/catppuccin-mocha.json
13263
+ var catppuccin_mocha_default;
13264
+ var init_catppuccin_mocha = __esm({
13265
+ "src/ui/themes/catppuccin-mocha.json"() {
13266
+ catppuccin_mocha_default = {
13267
+ name: "catppuccin-mocha",
13268
+ label: "Catppuccin Mocha",
13269
+ palette: {
13270
+ background: "#1e1e2e",
13271
+ foreground: "#cdd6f4",
13272
+ primary: "#89b4fa",
13273
+ secondary: "#6c7086",
13274
+ success: "#a6e3a1",
13275
+ error: "#f38ba8"
13276
+ },
13277
+ user: "#89b4fa",
13278
+ assistant: null,
13279
+ reasoning: { color: "#6c7086", dim: true },
13280
+ info: { color: "#cdd6f4", dim: false },
13281
+ error: "#f38ba8",
13282
+ warn: "#f9e2af",
13283
+ tool: "#cba6f7",
13284
+ spinner: "#89b4fa",
13285
+ permission: "#f38ba8",
13286
+ queue: { color: "#6c7086", dim: true },
13287
+ accent: "#89b4fa",
13288
+ modeBadge: {
13289
+ plan: "#89b4fa",
13290
+ auto: "#a6e3a1",
13291
+ edit: "#f38ba8"
13292
+ },
13293
+ blockquote: { color: "#6c7086", dim: true },
13294
+ codeInline: "#cba6f7",
13295
+ codeBlock: "#cba6f7",
13296
+ link: "#89b4fa",
13297
+ strikethrough: "#6c7086",
13298
+ tableBorder: "#45475a",
13299
+ tableHeader: "#cdd6f4",
13300
+ tableCell: "#cdd6f4",
13301
+ muted: { color: "#6c7086", dim: true }
13302
+ };
13303
+ }
13304
+ });
13305
+
13306
+ // src/ui/themes/catppuccin-latte.json
13307
+ var catppuccin_latte_default;
13308
+ var init_catppuccin_latte = __esm({
13309
+ "src/ui/themes/catppuccin-latte.json"() {
13310
+ catppuccin_latte_default = {
13311
+ name: "catppuccin-latte",
13312
+ label: "Catppuccin Latte",
13313
+ palette: {
13314
+ background: "#eff1f5",
13315
+ foreground: "#4c4f69",
13316
+ primary: "#1e66f5",
13317
+ secondary: "#8c8fa1",
13318
+ success: "#40a02b",
13319
+ error: "#d20f39"
13320
+ },
13321
+ user: "#1e66f5",
13322
+ assistant: null,
13323
+ reasoning: { color: "#8c8fa1", dim: true },
13324
+ info: { color: "#4c4f69", dim: false },
13325
+ error: "#d20f39",
13326
+ warn: "#df8e1d",
13327
+ tool: "#8839ef",
13328
+ spinner: "#1e66f5",
13329
+ permission: "#d20f39",
13330
+ queue: { color: "#8c8fa1", dim: true },
13331
+ accent: "#1e66f5",
13332
+ modeBadge: {
13333
+ plan: "#1e66f5",
13334
+ auto: "#40a02b",
13335
+ edit: "#d20f39"
13336
+ },
13337
+ blockquote: { color: "#8c8fa1", dim: true },
13338
+ codeInline: "#8839ef",
13339
+ codeBlock: "#8839ef",
13340
+ link: "#1e66f5",
13341
+ strikethrough: "#8c8fa1",
13342
+ tableBorder: "#bcc0cc",
13343
+ tableHeader: "#4c4f69",
13344
+ tableCell: "#4c4f69",
13345
+ muted: { color: "#8c8fa1", dim: true }
13346
+ };
13347
+ }
13348
+ });
13349
+
13350
+ // src/ui/themes/solarized-dark.json
13351
+ var solarized_dark_default;
13352
+ var init_solarized_dark = __esm({
13353
+ "src/ui/themes/solarized-dark.json"() {
13354
+ solarized_dark_default = {
13355
+ name: "solarized-dark",
13356
+ label: "Solarized Dark",
13357
+ palette: {
13358
+ background: "#002b36",
13359
+ foreground: "#839496",
13360
+ primary: "#268bd2",
13361
+ secondary: "#586e75",
13362
+ success: "#859900",
13363
+ error: "#dc322f"
13364
+ },
13365
+ user: "#268bd2",
13366
+ assistant: null,
13367
+ reasoning: { color: "#586e75", dim: true },
13368
+ info: { color: "#839496", dim: false },
13369
+ error: "#dc322f",
13370
+ warn: "#b58900",
13371
+ tool: "#2aa198",
13372
+ spinner: "#268bd2",
13373
+ permission: "#dc322f",
13374
+ queue: { color: "#586e75", dim: true },
13375
+ accent: "#268bd2",
13376
+ modeBadge: {
13377
+ plan: "#268bd2",
13378
+ auto: "#859900",
13379
+ edit: "#dc322f"
13380
+ },
13381
+ blockquote: { color: "#586e75", dim: true },
13382
+ codeInline: "#2aa198",
13383
+ codeBlock: "#2aa198",
13384
+ link: "#268bd2",
13385
+ strikethrough: "#586e75",
13386
+ tableBorder: "#073642",
13387
+ tableHeader: "#93a1a1",
13388
+ tableCell: "#839496",
13389
+ muted: { color: "#586e75", dim: true }
13390
+ };
13391
+ }
13392
+ });
13393
+
13394
+ // src/ui/themes/solarized-light.json
13395
+ var solarized_light_default;
13396
+ var init_solarized_light = __esm({
13397
+ "src/ui/themes/solarized-light.json"() {
13398
+ solarized_light_default = {
13399
+ name: "solarized-light",
13400
+ label: "Solarized Light",
13401
+ palette: {
13402
+ background: "#fdf6e3",
13403
+ foreground: "#657b83",
13404
+ primary: "#268bd2",
13405
+ secondary: "#93a1a1",
13406
+ success: "#859900",
13407
+ error: "#dc322f"
13408
+ },
13409
+ user: "#268bd2",
13410
+ assistant: null,
13411
+ reasoning: { color: "#93a1a1", dim: true },
13412
+ info: { color: "#657b83", dim: false },
13413
+ error: "#dc322f",
13414
+ warn: "#b58900",
13415
+ tool: "#2aa198",
13416
+ spinner: "#268bd2",
13417
+ permission: "#dc322f",
13418
+ queue: { color: "#93a1a1", dim: true },
13419
+ accent: "#268bd2",
13420
+ modeBadge: {
13421
+ plan: "#268bd2",
13422
+ auto: "#859900",
13423
+ edit: "#dc322f"
13424
+ },
13425
+ blockquote: { color: "#93a1a1", dim: true },
13426
+ codeInline: "#2aa198",
13427
+ codeBlock: "#2aa198",
13428
+ link: "#268bd2",
13429
+ strikethrough: "#93a1a1",
13430
+ tableBorder: "#eee8d5",
13431
+ tableHeader: "#586e75",
13432
+ tableCell: "#657b83",
13433
+ muted: { color: "#93a1a1", dim: true }
13434
+ };
13435
+ }
13436
+ });
13437
+
13438
+ // src/ui/themes/nord.json
13439
+ var nord_default;
13440
+ var init_nord = __esm({
13441
+ "src/ui/themes/nord.json"() {
13442
+ nord_default = {
13443
+ name: "nord",
13444
+ label: "Nord",
13445
+ palette: {
13446
+ background: "#2e3440",
13447
+ foreground: "#d8dee9",
13448
+ primary: "#88c0d0",
13449
+ secondary: "#4c566a",
13450
+ success: "#a3be8c",
13451
+ error: "#bf616a"
13452
+ },
13453
+ user: "#88c0d0",
13454
+ assistant: null,
13455
+ reasoning: { color: "#4c566a", dim: true },
13456
+ info: { color: "#d8dee9", dim: false },
13457
+ error: "#bf616a",
13458
+ warn: "#ebcb8b",
13459
+ tool: "#81a1c1",
13460
+ spinner: "#88c0d0",
13461
+ permission: "#bf616a",
13462
+ queue: { color: "#4c566a", dim: true },
13463
+ accent: "#88c0d0",
13464
+ modeBadge: {
13465
+ plan: "#88c0d0",
13466
+ auto: "#a3be8c",
13467
+ edit: "#bf616a"
13468
+ },
13469
+ blockquote: { color: "#4c566a", dim: true },
13470
+ codeInline: "#81a1c1",
13471
+ codeBlock: "#81a1c1",
13472
+ link: "#88c0d0",
13473
+ strikethrough: "#4c566a",
13474
+ tableBorder: "#434c5e",
13475
+ tableHeader: "#eceff4",
13476
+ tableCell: "#d8dee9",
13477
+ muted: { color: "#4c566a", dim: true }
13478
+ };
13479
+ }
13480
+ });
13481
+
13482
+ // src/ui/themes/gruvbox-dark.json
13483
+ var gruvbox_dark_default;
13484
+ var init_gruvbox_dark = __esm({
13485
+ "src/ui/themes/gruvbox-dark.json"() {
13486
+ gruvbox_dark_default = {
13487
+ name: "gruvbox-dark",
13488
+ label: "Gruvbox Dark",
13489
+ palette: {
13490
+ background: "#282828",
13491
+ foreground: "#ebdbb2",
13492
+ primary: "#b8bb26",
13493
+ secondary: "#928374",
13494
+ success: "#b8bb26",
13495
+ error: "#fb4934"
13496
+ },
13497
+ user: "#b8bb26",
13498
+ assistant: null,
13499
+ reasoning: { color: "#928374", dim: true },
13500
+ info: { color: "#ebdbb2", dim: false },
13501
+ error: "#fb4934",
13502
+ warn: "#fabd2f",
13503
+ tool: "#83a598",
13504
+ spinner: "#b8bb26",
13505
+ permission: "#fb4934",
13506
+ queue: { color: "#928374", dim: true },
13507
+ accent: "#b8bb26",
13508
+ modeBadge: {
13509
+ plan: "#b8bb26",
13510
+ auto: "#b8bb26",
13511
+ edit: "#fb4934"
13512
+ },
13513
+ blockquote: { color: "#928374", dim: true },
13514
+ codeInline: "#83a598",
13515
+ codeBlock: "#83a598",
13516
+ link: "#b8bb26",
13517
+ strikethrough: "#928374",
13518
+ tableBorder: "#504945",
13519
+ tableHeader: "#ebdbb2",
13520
+ tableCell: "#ebdbb2",
13521
+ muted: { color: "#928374", dim: true }
13522
+ };
13523
+ }
13524
+ });
13525
+
13526
+ // src/ui/themes/gruvbox-light.json
13527
+ var gruvbox_light_default;
13528
+ var init_gruvbox_light = __esm({
13529
+ "src/ui/themes/gruvbox-light.json"() {
13530
+ gruvbox_light_default = {
13531
+ name: "gruvbox-light",
13532
+ label: "Gruvbox Light",
13533
+ palette: {
13534
+ background: "#fbf1c7",
13535
+ foreground: "#3c3836",
13536
+ primary: "#79740e",
13537
+ secondary: "#928374",
13538
+ success: "#79740e",
13539
+ error: "#9d0006"
13540
+ },
13541
+ user: "#79740e",
13542
+ assistant: null,
13543
+ reasoning: { color: "#928374", dim: true },
13544
+ info: { color: "#3c3836", dim: false },
13545
+ error: "#9d0006",
13546
+ warn: "#b57614",
13547
+ tool: "#076678",
13548
+ spinner: "#79740e",
13549
+ permission: "#9d0006",
13550
+ queue: { color: "#928374", dim: true },
13551
+ accent: "#79740e",
13552
+ modeBadge: {
13553
+ plan: "#79740e",
13554
+ auto: "#79740e",
13555
+ edit: "#9d0006"
13556
+ },
13557
+ blockquote: { color: "#928374", dim: true },
13558
+ codeInline: "#076678",
13559
+ codeBlock: "#076678",
13560
+ link: "#79740e",
13561
+ strikethrough: "#928374",
13562
+ tableBorder: "#d5c4a1",
13563
+ tableHeader: "#3c3836",
13564
+ tableCell: "#3c3836",
13565
+ muted: { color: "#928374", dim: true }
13566
+ };
13567
+ }
13568
+ });
13569
+
13570
+ // src/ui/themes/one-dark.json
13571
+ var one_dark_default;
13572
+ var init_one_dark = __esm({
13573
+ "src/ui/themes/one-dark.json"() {
13574
+ one_dark_default = {
13575
+ name: "one-dark",
13576
+ label: "One Dark",
13577
+ palette: {
13578
+ background: "#282c34",
13579
+ foreground: "#abb2bf",
13580
+ primary: "#61afef",
13581
+ secondary: "#5c6370",
13582
+ success: "#98c379",
13583
+ error: "#e06c75"
13584
+ },
13585
+ user: "#61afef",
13586
+ assistant: null,
13587
+ reasoning: { color: "#5c6370", dim: true },
13588
+ info: { color: "#abb2bf", dim: false },
13589
+ error: "#e06c75",
13590
+ warn: "#e5c07b",
13591
+ tool: "#c678dd",
13592
+ spinner: "#61afef",
13593
+ permission: "#e06c75",
13594
+ queue: { color: "#5c6370", dim: true },
13595
+ accent: "#61afef",
13596
+ modeBadge: {
13597
+ plan: "#61afef",
13598
+ auto: "#98c379",
13599
+ edit: "#e06c75"
13600
+ },
13601
+ blockquote: { color: "#5c6370", dim: true },
13602
+ codeInline: "#c678dd",
13603
+ codeBlock: "#c678dd",
13604
+ link: "#61afef",
13605
+ strikethrough: "#5c6370",
13606
+ tableBorder: "#3e4451",
13607
+ tableHeader: "#abb2bf",
13608
+ tableCell: "#abb2bf",
13609
+ muted: { color: "#5c6370", dim: true }
13610
+ };
13611
+ }
13612
+ });
13613
+
12888
13614
  // src/ui/theme.ts
12889
- function buildTheme(name, label, palette, overrides) {
12890
- const base = {
12891
- name,
12892
- label,
13615
+ function normalizeTheme(json) {
13616
+ const obj = json;
13617
+ const palette = obj.palette;
13618
+ const normalizeDim = (v) => {
13619
+ if (v === void 0) return void 0;
13620
+ if (typeof v === "string") return { color: v, dim: false };
13621
+ const d = v;
13622
+ return { color: String(d.color), dim: d.dim === true };
13623
+ };
13624
+ const normalizeColor2 = (v) => {
13625
+ if (v === void 0) return void 0;
13626
+ if (typeof v === "string") return v;
13627
+ const d = v;
13628
+ return String(d.color);
13629
+ };
13630
+ return {
13631
+ name: String(obj.name),
13632
+ label: String(obj.label),
12893
13633
  palette,
12894
- user: palette.primary,
12895
- tool: palette.secondary,
12896
- spinner: palette.primary,
12897
- accent: palette.primary,
12898
- error: palette.error,
12899
- warn: palette.error,
12900
- info: { color: palette.secondary, dim: false },
12901
- reasoning: { color: palette.secondary, dim: false },
12902
- permission: palette.error,
12903
- queue: { color: palette.secondary, dim: false },
12904
- assistant: void 0,
12905
- modeBadge: {
13634
+ user: String(obj.user ?? palette.primary),
13635
+ assistant: obj.assistant === null ? void 0 : typeof obj.assistant === "string" ? obj.assistant : void 0,
13636
+ reasoning: normalizeDim(obj.reasoning) ?? { color: palette.secondary, dim: false },
13637
+ info: normalizeDim(obj.info) ?? { color: palette.secondary, dim: false },
13638
+ error: String(obj.error ?? palette.error),
13639
+ warn: String(obj.warn ?? palette.error),
13640
+ tool: String(obj.tool ?? palette.secondary),
13641
+ spinner: String(obj.spinner ?? palette.primary),
13642
+ permission: String(obj.permission ?? palette.error),
13643
+ queue: normalizeDim(obj.queue) ?? { color: palette.secondary, dim: false },
13644
+ accent: String(obj.accent ?? palette.primary),
13645
+ modeBadge: obj.modeBadge ?? {
12906
13646
  plan: palette.primary,
12907
13647
  auto: palette.success,
12908
13648
  edit: palette.error
12909
- }
12910
- };
12911
- if (!overrides) return base;
12912
- return {
12913
- ...base,
12914
- ...overrides,
12915
- modeBadge: overrides.modeBadge ?? base.modeBadge,
12916
- info: overrides.info ?? base.info,
12917
- reasoning: overrides.reasoning ?? base.reasoning,
12918
- queue: overrides.queue ?? base.queue
13649
+ },
13650
+ blockquote: normalizeDim(obj.blockquote),
13651
+ codeInline: normalizeColor2(obj.codeInline),
13652
+ codeBlock: normalizeColor2(obj.codeBlock),
13653
+ link: normalizeColor2(obj.link),
13654
+ strikethrough: normalizeColor2(obj.strikethrough),
13655
+ tableBorder: normalizeColor2(obj.tableBorder),
13656
+ tableHeader: normalizeColor2(obj.tableHeader),
13657
+ tableCell: normalizeColor2(obj.tableCell),
13658
+ muted: normalizeDim(obj.muted)
12919
13659
  };
12920
13660
  }
13661
+ function setThemes(themes) {
13662
+ THEMES = themes;
13663
+ }
12921
13664
  function resolveTheme(name) {
12922
13665
  if (!name) return THEMES[DEFAULT_THEME_NAME];
12923
13666
  return THEMES[name] ?? THEMES[DEFAULT_THEME_NAME];
@@ -12928,44 +13671,341 @@ function themeNames() {
12928
13671
  function themeList() {
12929
13672
  return Object.values(THEMES);
12930
13673
  }
12931
- var everforestDark, everforestLight, kanagawaDark, draculaDark, THEMES, DEFAULT_THEME_NAME;
13674
+ var BUILT_IN_THEMES, THEMES, DEFAULT_THEME_NAME;
12932
13675
  var init_theme = __esm({
12933
13676
  "src/ui/theme.ts"() {
12934
13677
  "use strict";
12935
- everforestDark = buildTheme("everforest-dark", "everforest-dark", {
12936
- primary: "#a7c080",
12937
- secondary: "#d3c6aa",
12938
- success: "#a7c080",
12939
- error: "#e67e80"
12940
- });
12941
- everforestLight = buildTheme("everforest-light", "everforest-light", {
12942
- primary: "#c5e49a",
12943
- secondary: "#f0e6c8",
12944
- success: "#c5e49a",
12945
- error: "#e07070"
12946
- });
12947
- kanagawaDark = buildTheme("kanagawa-dark", "kanagawa-dark", {
12948
- primary: "#8aadf4",
12949
- secondary: "#f0e6c8",
12950
- success: "#a6e3a1",
12951
- error: "#f38ba8"
12952
- });
12953
- draculaDark = buildTheme("dracula-dark", "dracula-dark", {
12954
- primary: "#ff79c6",
12955
- secondary: "#f8f8f2",
12956
- success: "#50fa7b",
12957
- error: "#ff5555"
12958
- });
12959
- THEMES = {
12960
- "everforest-dark": everforestDark,
12961
- "everforest-light": everforestLight,
12962
- "kanagawa-dark": kanagawaDark,
12963
- "dracula-dark": draculaDark
13678
+ init_everforest_dark();
13679
+ init_everforest_light();
13680
+ init_kanagawa_dark();
13681
+ init_dracula_dark();
13682
+ init_tokyo_night();
13683
+ init_catppuccin_mocha();
13684
+ init_catppuccin_latte();
13685
+ init_solarized_dark();
13686
+ init_solarized_light();
13687
+ init_nord();
13688
+ init_gruvbox_dark();
13689
+ init_gruvbox_light();
13690
+ init_one_dark();
13691
+ BUILT_IN_THEMES = {
13692
+ "everforest-dark": normalizeTheme(everforest_dark_default),
13693
+ "everforest-light": normalizeTheme(everforest_light_default),
13694
+ "kanagawa-dark": normalizeTheme(kanagawa_dark_default),
13695
+ "dracula-dark": normalizeTheme(dracula_dark_default),
13696
+ "tokyo-night": normalizeTheme(tokyo_night_default),
13697
+ "catppuccin-mocha": normalizeTheme(catppuccin_mocha_default),
13698
+ "catppuccin-latte": normalizeTheme(catppuccin_latte_default),
13699
+ "solarized-dark": normalizeTheme(solarized_dark_default),
13700
+ "solarized-light": normalizeTheme(solarized_light_default),
13701
+ "nord": normalizeTheme(nord_default),
13702
+ "gruvbox-dark": normalizeTheme(gruvbox_dark_default),
13703
+ "gruvbox-light": normalizeTheme(gruvbox_light_default),
13704
+ "one-dark": normalizeTheme(one_dark_default)
12964
13705
  };
13706
+ THEMES = { ...BUILT_IN_THEMES };
12965
13707
  DEFAULT_THEME_NAME = "everforest-dark";
12966
13708
  }
12967
13709
  });
12968
13710
 
13711
+ // src/ui/wcag.ts
13712
+ function hexToRgb(hex) {
13713
+ const m = hex.match(/^#([0-9a-fA-F]{6})$/);
13714
+ if (!m) return null;
13715
+ const v = parseInt(m[1], 16);
13716
+ return {
13717
+ r: v >> 16 & 255,
13718
+ g: v >> 8 & 255,
13719
+ b: v & 255
13720
+ };
13721
+ }
13722
+ function relativeLuminance({ r, g, b }) {
13723
+ const toLinear = (c) => {
13724
+ const s = c / 255;
13725
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
13726
+ };
13727
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
13728
+ }
13729
+ function contrastRatio(a, b) {
13730
+ const rgbA = hexToRgb(a);
13731
+ const rgbB = hexToRgb(b);
13732
+ if (!rgbA || !rgbB) return null;
13733
+ const l1 = relativeLuminance(rgbA);
13734
+ const l2 = relativeLuminance(rgbB);
13735
+ const lighter = Math.max(l1, l2);
13736
+ const darker = Math.min(l1, l2);
13737
+ return (lighter + 0.05) / (darker + 0.05);
13738
+ }
13739
+ function checkContrast(foreground, background, required = 4.5) {
13740
+ const ratio = contrastRatio(foreground, background);
13741
+ if (ratio === null) return null;
13742
+ if (ratio >= required) return null;
13743
+ return {
13744
+ pair: `${foreground} on ${background}`,
13745
+ foreground,
13746
+ background,
13747
+ ratio: Math.round(ratio * 100) / 100,
13748
+ required
13749
+ };
13750
+ }
13751
+ var init_wcag = __esm({
13752
+ "src/ui/wcag.ts"() {
13753
+ "use strict";
13754
+ }
13755
+ });
13756
+
13757
+ // src/ui/theme-loader.ts
13758
+ import { readFile as readFile16, readdir as readdir4 } from "fs/promises";
13759
+ import { join as join21 } from "path";
13760
+ import { homedir as homedir14 } from "os";
13761
+ function projectThemesDir(cwd = process.cwd()) {
13762
+ return join21(cwd, ".kimiflare", "themes");
13763
+ }
13764
+ function isHexColor(c) {
13765
+ return /^#[0-9a-fA-F]{6}$/.test(c);
13766
+ }
13767
+ function validateHex(field, value, errors) {
13768
+ if (value === void 0) return;
13769
+ if (!isHexColor(value)) {
13770
+ errors.push(`${field}: "${value}" is not a valid #RRGGBB hex color`);
13771
+ }
13772
+ }
13773
+ function validatePalette(p, errors) {
13774
+ if (!p || typeof p !== "object") {
13775
+ errors.push("palette must be an object");
13776
+ return null;
13777
+ }
13778
+ const palette = p;
13779
+ const required = ["background", "foreground", "primary", "secondary", "success", "error"];
13780
+ for (const key of required) {
13781
+ if (typeof palette[key] !== "string") {
13782
+ errors.push(`palette.${key} is required and must be a string`);
13783
+ } else {
13784
+ validateHex(`palette.${key}`, palette[key], errors);
13785
+ }
13786
+ }
13787
+ if (errors.length > 0) return null;
13788
+ return {
13789
+ background: palette.background,
13790
+ foreground: palette.foreground,
13791
+ primary: palette.primary,
13792
+ secondary: palette.secondary,
13793
+ success: palette.success,
13794
+ error: palette.error
13795
+ };
13796
+ }
13797
+ function validateDimColor(field, value, errors) {
13798
+ if (value === void 0) return void 0;
13799
+ if (typeof value === "string") {
13800
+ validateHex(field, value, errors);
13801
+ return { color: value, dim: false };
13802
+ }
13803
+ if (!value || typeof value !== "object") {
13804
+ errors.push(`${field} must be a string or { color, dim } object`);
13805
+ return void 0;
13806
+ }
13807
+ const obj = value;
13808
+ if (typeof obj.color !== "string") {
13809
+ errors.push(`${field}.color is required`);
13810
+ return void 0;
13811
+ }
13812
+ validateHex(`${field}.color`, obj.color, errors);
13813
+ return {
13814
+ color: obj.color,
13815
+ dim: obj.dim === true
13816
+ };
13817
+ }
13818
+ function validateModeBadge(value, errors) {
13819
+ if (value === void 0) return void 0;
13820
+ if (!value || typeof value !== "object") {
13821
+ errors.push("modeBadge must be an object");
13822
+ return void 0;
13823
+ }
13824
+ const obj = value;
13825
+ const result = {};
13826
+ for (const key of ["plan", "auto", "edit"]) {
13827
+ if (typeof obj[key] !== "string") {
13828
+ errors.push(`modeBadge.${key} is required`);
13829
+ } else {
13830
+ validateHex(`modeBadge.${key}`, obj[key], errors);
13831
+ result[key] = obj[key];
13832
+ }
13833
+ }
13834
+ return result;
13835
+ }
13836
+ function normalizeColor(v) {
13837
+ if (v === void 0) return void 0;
13838
+ if (typeof v === "string") return v;
13839
+ const d = v;
13840
+ return typeof d.color === "string" ? d.color : void 0;
13841
+ }
13842
+ async function loadThemesFromDir(dir, source) {
13843
+ const themes = [];
13844
+ const errors = [];
13845
+ let files;
13846
+ try {
13847
+ files = await readdir4(dir);
13848
+ } catch {
13849
+ return { themes, errors };
13850
+ }
13851
+ for (const file of files.filter((f) => f.endsWith(".json"))) {
13852
+ const path = join21(dir, file);
13853
+ let raw;
13854
+ try {
13855
+ raw = await readFile16(path, "utf-8");
13856
+ } catch (e) {
13857
+ errors.push(`${path}: ${e instanceof Error ? e.message : String(e)}`);
13858
+ continue;
13859
+ }
13860
+ let json;
13861
+ try {
13862
+ json = JSON.parse(raw);
13863
+ } catch (e) {
13864
+ errors.push(`${path}: invalid JSON \u2014 ${e instanceof Error ? e.message : String(e)}`);
13865
+ continue;
13866
+ }
13867
+ if (!json || typeof json !== "object") {
13868
+ errors.push(`${path}: root must be an object`);
13869
+ continue;
13870
+ }
13871
+ const obj = json;
13872
+ const fileErrors = [];
13873
+ if (typeof obj.name !== "string" || obj.name.length === 0) {
13874
+ fileErrors.push("name is required");
13875
+ }
13876
+ if (typeof obj.label !== "string" || obj.label.length === 0) {
13877
+ fileErrors.push("label is required");
13878
+ }
13879
+ const palette = validatePalette(obj.palette, fileErrors);
13880
+ if (fileErrors.length > 0) {
13881
+ errors.push(...fileErrors.map((e) => `${path}: ${e}`));
13882
+ continue;
13883
+ }
13884
+ if (!palette) continue;
13885
+ const theme = {
13886
+ name: obj.name,
13887
+ label: obj.label,
13888
+ palette,
13889
+ user: typeof obj.user === "string" ? obj.user : palette.primary,
13890
+ assistant: obj.assistant === null ? void 0 : typeof obj.assistant === "string" ? obj.assistant : void 0,
13891
+ reasoning: validateDimColor("reasoning", obj.reasoning, fileErrors) ?? { color: palette.secondary, dim: false },
13892
+ info: validateDimColor("info", obj.info, fileErrors) ?? { color: palette.secondary, dim: false },
13893
+ error: typeof obj.error === "string" ? obj.error : palette.error,
13894
+ warn: typeof obj.warn === "string" ? obj.warn : palette.error,
13895
+ tool: typeof obj.tool === "string" ? obj.tool : palette.secondary,
13896
+ spinner: typeof obj.spinner === "string" ? obj.spinner : palette.primary,
13897
+ permission: typeof obj.permission === "string" ? obj.permission : palette.error,
13898
+ queue: validateDimColor("queue", obj.queue, fileErrors) ?? { color: palette.secondary, dim: false },
13899
+ accent: typeof obj.accent === "string" ? obj.accent : palette.primary,
13900
+ modeBadge: validateModeBadge(obj.modeBadge, fileErrors) ?? {
13901
+ plan: palette.primary,
13902
+ auto: palette.success,
13903
+ edit: palette.error
13904
+ },
13905
+ blockquote: validateDimColor("blockquote", obj.blockquote, fileErrors),
13906
+ codeInline: normalizeColor(obj.codeInline),
13907
+ codeBlock: normalizeColor(obj.codeBlock),
13908
+ link: normalizeColor(obj.link),
13909
+ strikethrough: normalizeColor(obj.strikethrough),
13910
+ tableBorder: normalizeColor(obj.tableBorder),
13911
+ tableHeader: normalizeColor(obj.tableHeader),
13912
+ tableCell: normalizeColor(obj.tableCell),
13913
+ muted: validateDimColor("muted", obj.muted, fileErrors)
13914
+ };
13915
+ if (fileErrors.length > 0) {
13916
+ errors.push(...fileErrors.map((e) => `${path}: ${e}`));
13917
+ continue;
13918
+ }
13919
+ const wcagIssues = [];
13920
+ const bg = palette.background;
13921
+ const check = (label, color) => {
13922
+ if (!color) return;
13923
+ const issue = checkContrast(color, bg);
13924
+ if (issue) wcagIssues.push({ ...issue, pair: `${label} (${issue.pair})` });
13925
+ };
13926
+ check("foreground", palette.foreground);
13927
+ check("user", theme.user);
13928
+ check("assistant", theme.assistant);
13929
+ check("reasoning", theme.reasoning.color);
13930
+ check("info", theme.info.color);
13931
+ check("error", theme.error);
13932
+ check("warn", theme.warn);
13933
+ check("tool", theme.tool);
13934
+ check("accent", theme.accent);
13935
+ check("link", theme.link);
13936
+ check("codeInline", theme.codeInline);
13937
+ check("codeBlock", theme.codeBlock);
13938
+ check("tableHeader", theme.tableHeader);
13939
+ check("tableCell", theme.tableCell);
13940
+ themes.push({ theme, source, path, wcagIssues });
13941
+ }
13942
+ return { themes, errors };
13943
+ }
13944
+ async function loadAllThemes(cwd = process.cwd()) {
13945
+ if (cachedResult) return cachedResult;
13946
+ const themes = {};
13947
+ const errors = [];
13948
+ for (const [name, theme] of Object.entries(THEMES)) {
13949
+ themes[name] = {
13950
+ theme,
13951
+ source: "built-in",
13952
+ path: "<built-in>",
13953
+ wcagIssues: []
13954
+ };
13955
+ }
13956
+ const user = await loadThemesFromDir(USER_THEMES_DIR, "user");
13957
+ for (const t of user.themes) {
13958
+ themes[t.theme.name] = t;
13959
+ }
13960
+ errors.push(...user.errors);
13961
+ const project = await loadThemesFromDir(projectThemesDir(cwd), "project");
13962
+ for (const t of project.themes) {
13963
+ themes[t.theme.name] = t;
13964
+ }
13965
+ errors.push(...project.errors);
13966
+ cachedResult = { themes, errors };
13967
+ return cachedResult;
13968
+ }
13969
+ function clearThemeCache() {
13970
+ cachedResult = null;
13971
+ }
13972
+ async function loadAndMergeThemes(cwd = process.cwd()) {
13973
+ clearThemeCache();
13974
+ const { themes, errors } = await loadAllThemes(cwd);
13975
+ const merged = {};
13976
+ for (const [name, t] of Object.entries(THEMES)) {
13977
+ merged[name] = t;
13978
+ }
13979
+ for (const t of Object.values(themes)) {
13980
+ merged[t.theme.name] = t.theme;
13981
+ }
13982
+ setThemes(merged);
13983
+ const wcagWarnings = [];
13984
+ for (const t of Object.values(themes)) {
13985
+ if (t.wcagIssues.length > 0 && t.source !== "built-in") {
13986
+ wcagWarnings.push(
13987
+ `Theme "${t.theme.label}" has WCAG contrast issues:
13988
+ ` + t.wcagIssues.map((i) => ` ${i.pair}: ${i.ratio}:1 (needs ${i.required}:1)`).join("\n")
13989
+ );
13990
+ }
13991
+ }
13992
+ return { errors, wcagWarnings };
13993
+ }
13994
+ var USER_THEMES_DIR, cachedResult;
13995
+ var init_theme_loader = __esm({
13996
+ "src/ui/theme-loader.ts"() {
13997
+ "use strict";
13998
+ init_wcag();
13999
+ init_theme();
14000
+ USER_THEMES_DIR = join21(
14001
+ process.env.XDG_CONFIG_HOME || join21(homedir14(), ".config"),
14002
+ "kimiflare",
14003
+ "themes"
14004
+ );
14005
+ cachedResult = null;
14006
+ }
14007
+ });
14008
+
12969
14009
  // src/util/lsp-nudge.ts
12970
14010
  function maybeLspNudge(userText, lspEnabled, lspServers) {
12971
14011
  if (lspEnabled && Object.keys(lspServers).length > 0) {
@@ -13188,15 +14228,15 @@ var tui_report_exports = {};
13188
14228
  __export(tui_report_exports, {
13189
14229
  getCategoryReportText: () => getCategoryReportText
13190
14230
  });
13191
- import { readFile as readFile16 } from "fs/promises";
13192
- import { join as join21 } from "path";
13193
- import { homedir as homedir14 } from "os";
14231
+ import { readFile as readFile17 } from "fs/promises";
14232
+ import { join as join22 } from "path";
14233
+ import { homedir as homedir15 } from "os";
13194
14234
  function usageDir3() {
13195
- const xdg = process.env.XDG_DATA_HOME || join21(homedir14(), ".local", "share");
13196
- return join21(xdg, "kimiflare");
14235
+ const xdg = process.env.XDG_DATA_HOME || join22(homedir15(), ".local", "share");
14236
+ return join22(xdg, "kimiflare");
13197
14237
  }
13198
14238
  function usagePath3() {
13199
- return join21(usageDir3(), "usage.json");
14239
+ return join22(usageDir3(), "usage.json");
13200
14240
  }
13201
14241
  function today3() {
13202
14242
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -13208,7 +14248,7 @@ function daysAgo2(n) {
13208
14248
  }
13209
14249
  async function loadLog3() {
13210
14250
  try {
13211
- const raw = await readFile16(usagePath3(), "utf8");
14251
+ const raw = await readFile17(usagePath3(), "utf8");
13212
14252
  return JSON.parse(raw);
13213
14253
  } catch {
13214
14254
  return { version: 1, days: [], sessions: [] };
@@ -13274,11 +14314,11 @@ __export(app_exports, {
13274
14314
  shouldOpenMentionPicker: () => shouldOpenMentionPicker,
13275
14315
  shouldOpenSlashPicker: () => shouldOpenSlashPicker
13276
14316
  });
13277
- import React14, { useState as useState10, useRef as useRef3, useEffect as useEffect6, useCallback as useCallback2 } from "react";
14317
+ import React14, { useState as useState11, useRef as useRef3, useEffect as useEffect7, useCallback as useCallback2 } from "react";
13278
14318
  import { Box as Box21, Text as Text22, useApp, useInput as useInput7, render } from "ink";
13279
14319
  import SelectInput11 from "ink-select-input";
13280
14320
  import { existsSync as existsSync3, statSync as statSync4 } from "fs";
13281
- import { join as join22 } from "path";
14321
+ import { join as join23 } from "path";
13282
14322
  import { unlink as unlink3 } from "fs/promises";
13283
14323
  import { execSync as execSync2 } from "child_process";
13284
14324
  import { spawn as spawn4 } from "child_process";
@@ -13356,7 +14396,7 @@ function buildFilePickerIgnoreList(cwd) {
13356
14396
  ];
13357
14397
  const gitignorePatterns = [];
13358
14398
  try {
13359
- const gitignorePath = join22(cwd, ".gitignore");
14399
+ const gitignorePath = join23(cwd, ".gitignore");
13360
14400
  const stats = statSync4(gitignorePath);
13361
14401
  if (stats.size > MAX_GITIGNORE_SIZE) {
13362
14402
  return hardcoded;
@@ -13514,12 +14554,12 @@ function App({
13514
14554
  initialCloudDeviceId
13515
14555
  }) {
13516
14556
  const { exit } = useApp();
13517
- const [cfg, setCfg] = useState10(initialCfg);
13518
- const [lspScope, setLspScope] = useState10(initialLspScope);
13519
- const [lspProjectPath, setLspProjectPath] = useState10(initialLspProjectPath);
13520
- const [cloudToken, setCloudToken] = useState10(initialCloudToken);
13521
- const [cloudDeviceId, setCloudDeviceId] = useState10(initialCloudDeviceId);
13522
- const [events, setRawEvents] = useState10([]);
14557
+ const [cfg, setCfg] = useState11(initialCfg);
14558
+ const [lspScope, setLspScope] = useState11(initialLspScope);
14559
+ const [lspProjectPath, setLspProjectPath] = useState11(initialLspProjectPath);
14560
+ const [cloudToken, setCloudToken] = useState11(initialCloudToken);
14561
+ const [cloudDeviceId, setCloudDeviceId] = useState11(initialCloudDeviceId);
14562
+ const [events, setRawEvents] = useState11([]);
13523
14563
  const setEvents = useCallback2(
13524
14564
  (updater) => {
13525
14565
  setRawEvents((prev) => {
@@ -13529,45 +14569,72 @@ function App({
13529
14569
  },
13530
14570
  []
13531
14571
  );
13532
- const [input, setInput] = useState10("");
13533
- const [busy, setBusy] = useState10(false);
13534
- const [usage, setUsage] = useState10(null);
13535
- const [sessionUsage, setSessionUsage] = useState10(null);
13536
- const [gatewayMeta, setGatewayMeta] = useState10(null);
13537
- const [cloudBudget, setCloudBudget] = useState10(null);
13538
- const [showReasoning, setShowReasoning] = useState10(false);
13539
- const [perm, setPerm] = useState10(null);
13540
- const [limitModal, setLimitModal] = useState10(null);
13541
- const [queue, setQueue] = useState10([]);
13542
- const [history, setHistory] = useState10([]);
13543
- const [historyIndex, setHistoryIndex] = useState10(-1);
13544
- const [draftInput, setDraftInput] = useState10("");
13545
- const [mode, setMode] = useState10("edit");
13546
- const [codeMode, setCodeMode] = useState10(false);
14572
+ const [input, setInput] = useState11("");
14573
+ const [busy, setBusy] = useState11(false);
14574
+ const [usage, setUsage] = useState11(null);
14575
+ const [sessionUsage, setSessionUsage] = useState11(null);
14576
+ const [gatewayMeta, setGatewayMeta] = useState11(null);
14577
+ const [cloudBudget, setCloudBudget] = useState11(null);
14578
+ const [showReasoning, setShowReasoning] = useState11(false);
14579
+ const [perm, setPerm] = useState11(null);
14580
+ const [limitModal, setLimitModal] = useState11(null);
14581
+ const [queue, setQueue] = useState11([]);
14582
+ const [history, setHistory] = useState11([]);
14583
+ const [historyIndex, setHistoryIndex] = useState11(-1);
14584
+ const [draftInput, setDraftInput] = useState11("");
14585
+ const [mode, setMode] = useState11("edit");
14586
+ const [codeMode, setCodeMode] = useState11(false);
13547
14587
  const filePickerEnabled = initialCfg?.filePicker ?? true;
13548
- const [effort, setEffort] = useState10(
14588
+ const [effort, setEffort] = useState11(
13549
14589
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
13550
14590
  );
13551
- const [resumeSessions, setResumeSessions] = useState10(null);
13552
- const [showHelpMenu, setShowHelpMenu] = useState10(false);
13553
- const [commandWizard, setCommandWizard] = useState10(null);
13554
- const [commandPicker, setCommandPicker] = useState10(null);
13555
- const [commandToDelete, setCommandToDelete] = useState10(null);
13556
- const [showCommandList, setShowCommandList] = useState10(false);
13557
- const [showLspWizard, setShowLspWizard] = useState10(false);
13558
- const [showRemoteDashboard, setShowRemoteDashboard] = useState10(false);
13559
- const [selectedRemoteSession, setSelectedRemoteSession] = useState10(null);
13560
- const [tasks, setTasks] = useState10([]);
13561
- const [tasksStartedAt, setTasksStartedAt] = useState10(null);
13562
- const [tasksStartTokens, setTasksStartTokens] = useState10(0);
13563
- const [turnStartedAt, setTurnStartedAt] = useState10(null);
13564
- const [verbose, setVerbose] = useState10(false);
13565
- const [hasUpdate, setHasUpdate] = useState10(initialUpdateResult?.hasUpdate ?? false);
13566
- const [latestVersion, setLatestVersion] = useState10(initialUpdateResult?.latestVersion ?? null);
13567
- const [theme, setTheme] = useState10(resolveTheme(initialCfg?.theme));
13568
- const [showThemePicker, setShowThemePicker] = useState10(false);
13569
- const [kimiMdStale, setKimiMdStale] = useState10(false);
13570
- useEffect6(() => {
14591
+ const [resumeSessions, setResumeSessions] = useState11(null);
14592
+ const [showHelpMenu, setShowHelpMenu] = useState11(false);
14593
+ const [commandWizard, setCommandWizard] = useState11(null);
14594
+ const [commandPicker, setCommandPicker] = useState11(null);
14595
+ const [commandToDelete, setCommandToDelete] = useState11(null);
14596
+ const [showCommandList, setShowCommandList] = useState11(false);
14597
+ const [showLspWizard, setShowLspWizard] = useState11(false);
14598
+ const [showRemoteDashboard, setShowRemoteDashboard] = useState11(false);
14599
+ const [selectedRemoteSession, setSelectedRemoteSession] = useState11(null);
14600
+ const [tasks, setTasks] = useState11([]);
14601
+ const [tasksStartedAt, setTasksStartedAt] = useState11(null);
14602
+ const [tasksStartTokens, setTasksStartTokens] = useState11(0);
14603
+ const [turnStartedAt, setTurnStartedAt] = useState11(null);
14604
+ const [turnPhase, setTurnPhase] = useState11("waiting");
14605
+ const [currentToolName, setCurrentToolName] = useState11(null);
14606
+ const [lastActivityAt, setLastActivityAt] = useState11(null);
14607
+ const [verbose, setVerbose] = useState11(false);
14608
+ const [hasUpdate, setHasUpdate] = useState11(initialUpdateResult?.hasUpdate ?? false);
14609
+ const [latestVersion, setLatestVersion] = useState11(initialUpdateResult?.latestVersion ?? null);
14610
+ const [theme, setTheme] = useState11(resolveTheme(initialCfg?.theme));
14611
+ const [showThemePicker, setShowThemePicker] = useState11(false);
14612
+ const [kimiMdStale, setKimiMdStale] = useState11(false);
14613
+ useEffect7(() => {
14614
+ let cancelled = false;
14615
+ loadAndMergeThemes().then(({ errors, wcagWarnings }) => {
14616
+ if (cancelled) return;
14617
+ if (errors.length > 0) {
14618
+ setEvents((e) => [
14619
+ ...e,
14620
+ { kind: "error", key: mkKey(), text: `theme load errors:
14621
+ ${errors.join("\n")}` }
14622
+ ]);
14623
+ }
14624
+ if (wcagWarnings.length > 0) {
14625
+ setEvents((e) => [
14626
+ ...e,
14627
+ { kind: "info", key: mkKey(), text: `theme WCAG warnings:
14628
+ ${wcagWarnings.join("\n")}` }
14629
+ ]);
14630
+ }
14631
+ setTheme(resolveTheme(initialCfg?.theme));
14632
+ });
14633
+ return () => {
14634
+ cancelled = true;
14635
+ };
14636
+ }, []);
14637
+ useEffect7(() => {
13571
14638
  if (!cfg?.cloudMode || !initialCloudToken) return;
13572
14639
  let cancelled = false;
13573
14640
  const fetchBudget = async () => {
@@ -13582,11 +14649,11 @@ function App({
13582
14649
  cancelled = true;
13583
14650
  };
13584
14651
  }, [cfg?.cloudMode, initialCloudToken]);
13585
- const [cursorOffset, setCursorOffset] = useState10(0);
13586
- const [activePicker, setActivePicker] = useState10(null);
13587
- const [filePickerItems, setFilePickerItems] = useState10([]);
14652
+ const [cursorOffset, setCursorOffset] = useState11(0);
14653
+ const [activePicker, setActivePicker] = useState11(null);
14654
+ const [filePickerItems, setFilePickerItems] = useState11([]);
13588
14655
  const filePickerLoadedRef = useRef3(false);
13589
- const [customCommandsVersion, setCustomCommandsVersion] = useState10(0);
14656
+ const [customCommandsVersion, setCustomCommandsVersion] = useState11(0);
13590
14657
  const cacheStableRef = useRef3(initialCfg?.cacheStablePrompts !== false);
13591
14658
  const messagesRef = useRef3(
13592
14659
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
@@ -13628,7 +14695,7 @@ function App({
13628
14695
  const flushTimeoutRef = useRef3(null);
13629
14696
  const customCommandsRef = useRef3([]);
13630
14697
  const pickerCancelRef = useRef3(null);
13631
- useEffect6(() => {
14698
+ useEffect7(() => {
13632
14699
  busyRef.current = busy;
13633
14700
  }, [busy]);
13634
14701
  const pickerAnchor = activePicker?.anchor ?? null;
@@ -13653,7 +14720,7 @@ function App({
13653
14720
  if (pickerKind !== "slash" || pickerQuery === null) return [];
13654
14721
  return fuzzyFilter(allSlashCommands, pickerQuery, (c) => c.name).slice(0, 50);
13655
14722
  }, [pickerKind, allSlashCommands, pickerQuery]);
13656
- useEffect6(() => {
14723
+ useEffect7(() => {
13657
14724
  if (activePicker !== null) {
13658
14725
  const trigger = activePicker.kind === "file" ? "@" : "/";
13659
14726
  if (cursorOffset < activePicker.anchor) {
@@ -13710,14 +14777,14 @@ function App({
13710
14777
  return;
13711
14778
  }
13712
14779
  }, [input, cursorOffset, activePicker, filePickerEnabled]);
13713
- useEffect6(() => {
14780
+ useEffect7(() => {
13714
14781
  if (activePicker?.kind !== "file") return;
13715
14782
  const max = Math.max(0, filteredFileItems.length - 1);
13716
14783
  if (activePicker.selected > max) {
13717
14784
  setActivePicker({ ...activePicker, selected: max });
13718
14785
  }
13719
14786
  }, [filteredFileItems.length, activePicker]);
13720
- useEffect6(() => {
14787
+ useEffect7(() => {
13721
14788
  if (activePicker?.kind !== "slash") return;
13722
14789
  const max = Math.max(0, filteredSlashItems.length - 1);
13723
14790
  if (activePicker.selected > max) {
@@ -13761,7 +14828,7 @@ function App({
13761
14828
  pickerCancelRef.current = cursorOffset;
13762
14829
  setActivePicker(null);
13763
14830
  }, [cursorOffset]);
13764
- useEffect6(() => {
14831
+ useEffect7(() => {
13765
14832
  const modalActive = showHelpMenu || commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || perm !== null || limitModal !== null;
13766
14833
  if (modalActive && activePicker !== null) {
13767
14834
  setActivePicker(null);
@@ -13778,7 +14845,7 @@ function App({
13778
14845
  limitModal,
13779
14846
  activePicker
13780
14847
  ]);
13781
- useEffect6(() => {
14848
+ useEffect7(() => {
13782
14849
  if (!cfg) return;
13783
14850
  void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
13784
14851
  ({ pruneSessions: pruneSessions2 }) => pruneSessions2().then((removed) => {
@@ -13804,7 +14871,7 @@ function App({
13804
14871
  }
13805
14872
  });
13806
14873
  if (cfg.memoryEnabled) {
13807
- const dbPath = cfg.memoryDbPath ?? join22(process.cwd(), ".kimiflare", "memory.db");
14874
+ const dbPath = cfg.memoryDbPath ?? join23(process.cwd(), ".kimiflare", "memory.db");
13808
14875
  const manager = new MemoryManager({
13809
14876
  dbPath,
13810
14877
  accountId: cfg.accountId,
@@ -13853,7 +14920,7 @@ function App({
13853
14920
  } catch {
13854
14921
  }
13855
14922
  })();
13856
- if (existsSync3(join22(cwd, "KIMI.md"))) {
14923
+ if (existsSync3(join23(cwd, "KIMI.md"))) {
13857
14924
  const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
13858
14925
  const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
13859
14926
  if (driftCount >= 5) {
@@ -13879,7 +14946,7 @@ function App({
13879
14946
  }
13880
14947
  });
13881
14948
  }, [cfg, setEvents]);
13882
- useEffect6(() => {
14949
+ useEffect7(() => {
13883
14950
  const id = setInterval(() => {
13884
14951
  try {
13885
14952
  performance.clearMarks();
@@ -13904,7 +14971,7 @@ function App({
13904
14971
  ]);
13905
14972
  }
13906
14973
  }, [setEvents]);
13907
- useEffect6(() => {
14974
+ useEffect7(() => {
13908
14975
  if (!cfg || updateCheckedRef.current) return;
13909
14976
  updateCheckedRef.current = true;
13910
14977
  if (initialUpdateResult) {
@@ -13955,7 +15022,7 @@ function App({
13955
15022
  }
13956
15023
  });
13957
15024
  }, [cfg, initialUpdateResult]);
13958
- useEffect6(() => {
15025
+ useEffect7(() => {
13959
15026
  modeRef.current = mode;
13960
15027
  if (cacheStableRef.current) {
13961
15028
  messagesRef.current[1] = {
@@ -13982,10 +15049,10 @@ function App({
13982
15049
  executorRef.current.clearSessionPermissions();
13983
15050
  }
13984
15051
  }, [mode, cfg?.model]);
13985
- useEffect6(() => {
15052
+ useEffect7(() => {
13986
15053
  effortRef.current = effort;
13987
15054
  }, [effort]);
13988
- useEffect6(() => {
15055
+ useEffect7(() => {
13989
15056
  if (!cfg) return;
13990
15057
  const id = setInterval(() => {
13991
15058
  void checkForUpdate().then((result) => {
@@ -14140,7 +15207,7 @@ function App({
14140
15207
  ]);
14141
15208
  }
14142
15209
  }, [cfg]);
14143
- useEffect6(() => {
15210
+ useEffect7(() => {
14144
15211
  if (cfg && !mcpInitRef.current) {
14145
15212
  void initMcp();
14146
15213
  }
@@ -14465,6 +15532,9 @@ function App({
14465
15532
  } finally {
14466
15533
  setBusy(false);
14467
15534
  setTurnStartedAt(null);
15535
+ setTurnPhase("waiting");
15536
+ setCurrentToolName(null);
15537
+ setLastActivityAt(null);
14468
15538
  activeControllerRef.current = null;
14469
15539
  permResolveRef.current = null;
14470
15540
  limitResolveRef.current = null;
@@ -14524,7 +15594,7 @@ function App({
14524
15594
  lspManagerRef.current.notifyChange(path, content);
14525
15595
  } else {
14526
15596
  void import("fs/promises").then(
14527
- ({ readFile: readFile17 }) => readFile17(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
15597
+ ({ readFile: readFile18 }) => readFile18(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
14528
15598
  })
14529
15599
  );
14530
15600
  }
@@ -14644,7 +15714,7 @@ function App({
14644
15714
  }
14645
15715
  }
14646
15716
  });
14647
- if (existsSync3(join22(cwd, "KIMI.md"))) {
15717
+ if (existsSync3(join23(cwd, "KIMI.md"))) {
14648
15718
  if (cacheStableRef.current) {
14649
15719
  messagesRef.current[1] = {
14650
15720
  role: "system",
@@ -14699,6 +15769,9 @@ function App({
14699
15769
  if (asstId !== null) updateAssistant(asstId, () => ({ streaming: false }));
14700
15770
  setBusy(false);
14701
15771
  setTurnStartedAt(null);
15772
+ setTurnPhase("waiting");
15773
+ setCurrentToolName(null);
15774
+ setLastActivityAt(null);
14702
15775
  activeAsstIdRef.current = null;
14703
15776
  activeControllerRef.current = null;
14704
15777
  permResolveRef.current = null;
@@ -14717,15 +15790,19 @@ function App({
14717
15790
  (picked) => {
14718
15791
  setShowThemePicker(false);
14719
15792
  if (!picked) return;
14720
- setCfg((c) => c ? { ...c, theme: picked.name } : c);
14721
- if (cfg) void saveConfig({ ...cfg, theme: picked.name }).catch(() => {
15793
+ setCfg((c) => {
15794
+ if (!c) return c;
15795
+ const updated = { ...c, theme: picked.name };
15796
+ void saveConfig(updated).catch(() => {
15797
+ });
15798
+ return updated;
14722
15799
  });
14723
15800
  setEvents((e) => [
14724
15801
  ...e,
14725
15802
  { kind: "info", key: mkKey(), text: `theme: ${picked.label} \u2014 restart to apply` }
14726
15803
  ]);
14727
15804
  },
14728
- [cfg]
15805
+ []
14729
15806
  );
14730
15807
  const handleResumePick = useCallback2(
14731
15808
  async (picked) => {
@@ -15049,8 +16126,12 @@ use: /thinking low | medium | high`
15049
16126
  ]);
15050
16127
  return true;
15051
16128
  }
15052
- setCfg((c2) => c2 ? { ...c2, theme: next.name } : c2);
15053
- if (cfg) void saveConfig({ ...cfg, theme: next.name }).catch(() => {
16129
+ setCfg((prev) => {
16130
+ if (!prev) return prev;
16131
+ const updated = { ...prev, theme: next.name };
16132
+ void saveConfig(updated).catch(() => {
16133
+ });
16134
+ return updated;
15054
16135
  });
15055
16136
  setEvents((e) => [
15056
16137
  ...e,
@@ -15620,7 +16701,7 @@ ${lines.join("\n")}` }]);
15620
16701
  }
15621
16702
  }
15622
16703
  turnCounterRef.current += 1;
15623
- if (turnCounterRef.current % 15 === 0 && existsSync3(join22(process.cwd(), "KIMI.md")) && !kimiMdStale) {
16704
+ if (turnCounterRef.current % 15 === 0 && existsSync3(join23(process.cwd(), "KIMI.md")) && !kimiMdStale) {
15624
16705
  setEvents((e) => [
15625
16706
  ...e,
15626
16707
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
@@ -15649,6 +16730,8 @@ ${lines.join("\n")}` }]);
15649
16730
  onAssistantStart: () => {
15650
16731
  const id = nextAssistantId++;
15651
16732
  activeAsstIdRef.current = id;
16733
+ setTurnPhase("generating");
16734
+ setLastActivityAt(Date.now());
15652
16735
  setEvents((e) => [
15653
16736
  ...e,
15654
16737
  { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
@@ -15657,17 +16740,23 @@ ${lines.join("\n")}` }]);
15657
16740
  onReasoningDelta: (d) => {
15658
16741
  const id = activeAsstIdRef.current;
15659
16742
  if (id !== null) updateAssistant(id, (e) => ({ reasoning: e.reasoning + d }));
16743
+ setLastActivityAt(Date.now());
15660
16744
  },
15661
16745
  onTextDelta: (d) => {
15662
16746
  const id = activeAsstIdRef.current;
15663
16747
  if (id !== null) updateAssistant(id, (e) => ({ text: e.text + d }));
16748
+ setLastActivityAt(Date.now());
15664
16749
  },
15665
16750
  onAssistantFinal: () => {
15666
16751
  const id = activeAsstIdRef.current;
15667
16752
  if (id !== null) updateAssistant(id, () => ({ streaming: false }));
16753
+ setTurnPhase("waiting");
15668
16754
  },
15669
16755
  onToolCallFinalized: (call) => {
15670
16756
  pendingToolCallsRef.current.set(call.id, call.function.name);
16757
+ setTurnPhase("executing");
16758
+ setCurrentToolName(call.function.name);
16759
+ setLastActivityAt(Date.now());
15671
16760
  const spec = executorRef.current.list().find((t) => t.name === call.function.name);
15672
16761
  let renderMeta;
15673
16762
  let argsParsed = {};
@@ -15690,12 +16779,18 @@ ${lines.join("\n")}` }]);
15690
16779
  args: call.function.arguments,
15691
16780
  status: "running",
15692
16781
  render: renderMeta,
15693
- expanded: false
16782
+ expanded: false,
16783
+ startedAt: Date.now()
15694
16784
  }
15695
16785
  ]);
15696
16786
  },
15697
16787
  onToolResult: (r) => {
15698
16788
  pendingToolCallsRef.current.delete(r.tool_call_id);
16789
+ setLastActivityAt(Date.now());
16790
+ if (pendingToolCallsRef.current.size === 0) {
16791
+ setTurnPhase("waiting");
16792
+ setCurrentToolName(null);
16793
+ }
15699
16794
  updateTool(r.tool_call_id, {
15700
16795
  status: r.ok ? "done" : "error",
15701
16796
  result: r.content
@@ -15802,7 +16897,7 @@ ${lines.join("\n")}` }]);
15802
16897
  lspManagerRef.current.notifyChange(path, content2);
15803
16898
  } else {
15804
16899
  void import("fs/promises").then(
15805
- ({ readFile: readFile17 }) => readFile17(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
16900
+ ({ readFile: readFile18 }) => readFile18(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
15806
16901
  })
15807
16902
  );
15808
16903
  }
@@ -15934,6 +17029,9 @@ ${lines.join("\n")}` }]);
15934
17029
  if (asstId !== null) updateAssistant(asstId, () => ({ streaming: false }));
15935
17030
  setBusy(false);
15936
17031
  setTurnStartedAt(null);
17032
+ setTurnPhase("waiting");
17033
+ setCurrentToolName(null);
17034
+ setLastActivityAt(null);
15937
17035
  activeAsstIdRef.current = null;
15938
17036
  activeControllerRef.current = null;
15939
17037
  permResolveRef.current = null;
@@ -15950,7 +17048,7 @@ ${lines.join("\n")}` }]);
15950
17048
  },
15951
17049
  [cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateGatewayMeta, flushToolBatch]
15952
17050
  );
15953
- useEffect6(() => {
17051
+ useEffect7(() => {
15954
17052
  if (!busy && queue.length > 0) {
15955
17053
  const next = queue[0];
15956
17054
  setQueue((q) => q.slice(1));
@@ -15978,7 +17076,7 @@ ${lines.join("\n")}` }]);
15978
17076
  [busy, processMessage]
15979
17077
  );
15980
17078
  submitRef.current = submit;
15981
- useEffect6(() => {
17079
+ useEffect7(() => {
15982
17080
  if (compactSuggestedRef.current) return;
15983
17081
  if (usage && usage.prompt_tokens / CONTEXT_LIMIT >= AUTO_COMPACT_SUGGEST_PCT) {
15984
17082
  compactSuggestedRef.current = true;
@@ -16077,7 +17175,7 @@ ${lines.join("\n")}` }]);
16077
17175
  {
16078
17176
  servers: cfg?.lspServers ?? {},
16079
17177
  currentScope: lspScope,
16080
- hasProjectDir: existsSync3(join22(process.cwd(), ".kimiflare")),
17178
+ hasProjectDir: existsSync3(join23(process.cwd(), ".kimiflare")),
16081
17179
  onDone: () => setShowLspWizard(false),
16082
17180
  onSave: (servers, enabled, scope) => {
16083
17181
  setCfg((c) => c ? { ...c, lspEnabled: enabled, lspServers: servers } : c);
@@ -16231,6 +17329,9 @@ ${lines.join("\n")}` }]);
16231
17329
  codeMode,
16232
17330
  cloudMode: cfg.cloudMode,
16233
17331
  cloudBudget,
17332
+ phase: turnPhase,
17333
+ currentTool: currentToolName,
17334
+ lastActivityAt,
16234
17335
  kimiMdStale
16235
17336
  }
16236
17337
  ),
@@ -16380,6 +17481,7 @@ var init_app = __esm({
16380
17481
  init_theme_context();
16381
17482
  init_theme_picker();
16382
17483
  init_theme();
17484
+ init_theme_loader();
16383
17485
  init_lsp_config();
16384
17486
  init_lsp_nudge();
16385
17487
  init_file_picker();