neko-harness 0.2.8 → 0.2.9

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.
@@ -9,7 +9,7 @@
9
9
  href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='26' fill='%23ffb7c5'/%3E%3Ctext x='50' y='70' font-size='58' text-anchor='middle' fill='%23ffffff' font-family='sans-serif' font-weight='bold'%3En%3C/text%3E%3C/svg%3E"
10
10
  />
11
11
  <title>neko harness</title>
12
- <script type="module" crossorigin src="/assets/index-BJ4XDkf5.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-BKYqZ61y.js"></script>
13
13
  <link rel="stylesheet" crossorigin href="/assets/index-BwqDUlvt.css">
14
14
  </head>
15
15
  <body>
package/dist/gateway.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
 
4
4
  // packages/gateway/src/cli-args.ts
5
- var GATEWAY_USAGE = `\u7528\u6CD5: main.ts [\u9009\u9879]
5
+ var GATEWAY_USAGE = `\u7528\u6CD5: neko serve [\u9009\u9879]
6
6
 
7
7
  \u9009\u9879:
8
8
  --host <ip> WebSocket \u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u4EC5\u672C\u673A\uFF09
@@ -2707,7 +2707,7 @@ import { dirname as dirname5, join as join7 } from "path";
2707
2707
  import { fileURLToPath as fileURLToPath2 } from "url";
2708
2708
 
2709
2709
  // packages/core/src/application-version.ts
2710
- var RUNNING_VERSION = "0.2.8";
2710
+ var RUNNING_VERSION = "0.2.9";
2711
2711
  var APPLICATION_PROTOCOL = 1;
2712
2712
  function stableVersion(value) {
2713
2713
  return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
@@ -4955,7 +4955,7 @@ if (gateway.socketPath !== undefined)
4955
4955
  console.log(` Unix socket: ${gateway.socketPath}`);
4956
4956
  if (gateway.wsUrl !== undefined)
4957
4957
  console.log(` WebSocket: ${gateway.wsUrl}`);
4958
- console.log("Ctrl-C \u505C\u6B62\u3002");
4958
+ console.log("\u6309 Ctrl+C \u505C\u6B62\u3002");
4959
4959
  var stopping = false;
4960
4960
  var stop = () => {
4961
4961
  if (stopping)
package/dist/neko.js CHANGED
@@ -79,7 +79,7 @@ function updateAction(input) {
79
79
  return "install";
80
80
  return "none";
81
81
  }
82
- var RUNNING_VERSION = "0.2.8", APPLICATION_PROTOCOL = 1, ApplicationProtocolError;
82
+ var RUNNING_VERSION = "0.2.9", APPLICATION_PROTOCOL = 1, ApplicationProtocolError;
83
83
  var init_application_version = __esm(() => {
84
84
  ApplicationProtocolError = class ApplicationProtocolError extends Error {
85
85
  constructor() {
@@ -3115,39 +3115,39 @@ var init_palette = __esm(() => {
3115
3115
  "syntaxPunctuation"
3116
3116
  ];
3117
3117
  DARK_PALETTE = {
3118
- panel: { bg: "#38313C" },
3119
- selection: { bg: "#523349", fg: "#FFFFFF", bold: true },
3120
- userBg: { bg: tianyi.s900 },
3121
- toolPendingBg: { bg: "#2C2733" },
3122
- toolSuccessBg: { bg: "#223128" },
3123
- toolErrorBg: { bg: "#382631" },
3124
- codeBg: { bg: "#222A38" },
3125
- accent: { fg: sakura.s300 },
3126
- model: { fg: tianyi.s300 },
3127
- muted: { fg: ink.s400 },
3128
- dim: { fg: ink.s500 },
3129
- thinking: { fg: "#93A1B5", italic: true },
3130
- ok: { fg: "#9DCB9A" },
3131
- warning: { fg: "#FFC79B" },
3132
- error: { fg: "#FF8FA3" },
3133
- userText: { fg: ink.s50 },
3134
- toolTitle: { fg: sakura.s200 },
3135
- toolOutput: { fg: ink.s400 },
3136
- codeFg: { fg: sakura.s200 },
3137
- border: { fg: ink.s600 },
3138
- borderMuted: { fg: ink.s700 },
3139
- diffAdded: { fg: "#9DCB9A" },
3140
- diffRemoved: { fg: "#FF8FA3" },
3141
- diffContext: { fg: ink.s500 },
3142
- syntaxComment: { fg: ink.s500, italic: true },
3143
- syntaxKeyword: { fg: sakura.s300, bold: true },
3144
- syntaxFunction: { fg: sakura.s200 },
3145
- syntaxVariable: { fg: ink.s100 },
3146
- syntaxString: { fg: tianyi.s200 },
3147
- syntaxNumber: { fg: tianyi.s300 },
3148
- syntaxType: { fg: tianyi.s300 },
3149
- syntaxOperator: { fg: ink.s300 },
3150
- syntaxPunctuation: { fg: ink.s400 }
3118
+ panel: { bg: "#24283B", fg: "#C0CAF5" },
3119
+ selection: { bg: "#343B58", fg: "#E1E7FF", bold: true },
3120
+ userBg: { bg: "#292E42", fg: "#C0CAF5" },
3121
+ toolPendingBg: { bg: "#222638", fg: "#C0CAF5" },
3122
+ toolSuccessBg: { bg: "#232C32", fg: "#C0CAF5" },
3123
+ toolErrorBg: { bg: "#302735", fg: "#C0CAF5" },
3124
+ codeBg: { bg: "#1A1B26", fg: "#C0CAF5" },
3125
+ accent: { fg: "#7AA2F7" },
3126
+ model: { fg: "#7DCFFF" },
3127
+ muted: { fg: "#A9B1D6" },
3128
+ dim: { fg: "#9AA5CE" },
3129
+ thinking: { fg: "#9AA5CE", italic: true },
3130
+ ok: { fg: "#9ECE6A" },
3131
+ warning: { fg: "#E0AF68" },
3132
+ error: { fg: "#F7768E" },
3133
+ userText: { fg: "#C0CAF5" },
3134
+ toolTitle: { fg: "#7AA2F7" },
3135
+ toolOutput: { fg: "#C0CAF5" },
3136
+ codeFg: { fg: "#C0CAF5" },
3137
+ border: { fg: "#737DA2" },
3138
+ borderMuted: { fg: "#545E7C" },
3139
+ diffAdded: { fg: "#9ECE6A" },
3140
+ diffRemoved: { fg: "#F7768E" },
3141
+ diffContext: { fg: "#9AA5CE" },
3142
+ syntaxComment: { fg: "#8993B3", italic: true },
3143
+ syntaxKeyword: { fg: "#BB9AF7", bold: true },
3144
+ syntaxFunction: { fg: "#7AA2F7" },
3145
+ syntaxVariable: { fg: "#C0CAF5" },
3146
+ syntaxString: { fg: "#9ECE6A" },
3147
+ syntaxNumber: { fg: "#FF9E64" },
3148
+ syntaxType: { fg: "#7DCFFF" },
3149
+ syntaxOperator: { fg: "#89DDFF" },
3150
+ syntaxPunctuation: { fg: "#A9B1D6" }
3151
3151
  };
3152
3152
  LIGHT_PALETTE = {
3153
3153
  panel: { bg: ink.s50 },
@@ -3187,7 +3187,42 @@ var init_palette = __esm(() => {
3187
3187
  });
3188
3188
 
3189
3189
  // packages/tui/src/theme/slots.ts
3190
- import { getTerminalColorMode, parseColor, styleText } from "@earendil-works/pi-tui";
3190
+ import {
3191
+ backgroundAnsi,
3192
+ foregroundAnsi,
3193
+ getTerminalColorMode,
3194
+ parseColor,
3195
+ styleText
3196
+ } from "@earendil-works/pi-tui";
3197
+ function surfaceText(text, fg, bg) {
3198
+ return text.replace(/\x1b\[([0-9;:]*)m/g, (sequence, parameters) => {
3199
+ const codes = parameters.split(";");
3200
+ let resetFg = false;
3201
+ let resetBg = false;
3202
+ for (let i = 0;i < codes.length; i++) {
3203
+ const code = Number(codes[i].split(":")[0]);
3204
+ if (code === 0) {
3205
+ resetFg = true;
3206
+ resetBg = true;
3207
+ } else if (code === 39)
3208
+ resetFg = true;
3209
+ else if (code === 49)
3210
+ resetBg = true;
3211
+ else if (code === 38 || code === 48) {
3212
+ if (code === 38)
3213
+ resetFg = false;
3214
+ else
3215
+ resetBg = false;
3216
+ if (!codes[i].includes(":"))
3217
+ i += codes[i + 1] === "2" ? 4 : codes[i + 1] === "5" ? 2 : 0;
3218
+ } else if (code >= 30 && code <= 37 || code >= 90 && code <= 97)
3219
+ resetFg = false;
3220
+ else if (code >= 40 && code <= 47 || code >= 100 && code <= 107)
3221
+ resetBg = false;
3222
+ }
3223
+ return sequence + (resetFg ? fg : "") + (resetBg ? bg : "");
3224
+ });
3225
+ }
3191
3226
  function createTheme(palette) {
3192
3227
  const colorCache = new Map;
3193
3228
  const colorOf = (hex) => {
@@ -3201,14 +3236,19 @@ function createTheme(palette) {
3201
3236
  const theme = {};
3202
3237
  for (const name of SLOT_NAMES) {
3203
3238
  const style = palette[name];
3204
- theme[name] = (text) => styleText(text, {
3205
- ...style.fg !== undefined ? { fg: colorOf(style.fg) } : {},
3206
- ...style.bg !== undefined ? { bg: colorOf(style.bg) } : {},
3207
- ...style.bold !== undefined ? { bold: style.bold } : {},
3208
- ...style.italic !== undefined ? { italic: style.italic } : {},
3209
- ...style.strikethrough !== undefined ? { strikethrough: style.strikethrough } : {},
3210
- ...style.underline !== undefined ? { underline: style.underline } : {}
3211
- }, getTerminalColorMode());
3239
+ theme[name] = (text) => {
3240
+ const mode = getTerminalColorMode();
3241
+ if (palette === DARK_PALETTE && style.fg && style.bg)
3242
+ text = surfaceText(text, foregroundAnsi(colorOf(style.fg), mode), backgroundAnsi(colorOf(style.bg), mode));
3243
+ return styleText(text, {
3244
+ ...style.fg !== undefined ? { fg: colorOf(style.fg) } : {},
3245
+ ...style.bg !== undefined ? { bg: colorOf(style.bg) } : {},
3246
+ ...style.bold !== undefined ? { bold: style.bold } : {},
3247
+ ...style.italic !== undefined ? { italic: style.italic } : {},
3248
+ ...style.strikethrough !== undefined ? { strikethrough: style.strikethrough } : {},
3249
+ ...style.underline !== undefined ? { underline: style.underline } : {}
3250
+ }, mode);
3251
+ };
3212
3252
  }
3213
3253
  return theme;
3214
3254
  }
@@ -12886,6 +12926,7 @@ var THINKING_COLLAPSED_LABEL = "\u2727 Thinking\u2026 (click to expand)", Assist
12886
12926
  var init_assistant_message = __esm(() => {
12887
12927
  init_theme();
12888
12928
  AssistantMessageComponent = class AssistantMessageComponent extends Container {
12929
+ #renderTheme = getActiveThemeMode();
12889
12930
  #lastContent;
12890
12931
  #isStreaming = false;
12891
12932
  #thinkingExpanded = false;
@@ -12896,6 +12937,10 @@ var init_assistant_message = __esm(() => {
12896
12937
  this.updateContent(content, isStreaming);
12897
12938
  }
12898
12939
  render(width) {
12940
+ if (this.#renderTheme !== getActiveThemeMode()) {
12941
+ this.#renderTheme = getActiveThemeMode();
12942
+ this.#rebuild();
12943
+ }
12899
12944
  this.#renderWidth = width;
12900
12945
  return super.render(width);
12901
12946
  }
@@ -13748,8 +13793,46 @@ var init_picker = __esm(() => {
13748
13793
  init_theme();
13749
13794
  });
13750
13795
 
13796
+ // packages/tui/src/components/themed-text.ts
13797
+ import { Text as Text3, TruncatedText } from "@earendil-works/pi-tui";
13798
+
13799
+ class ThemedTruncatedText {
13800
+ #content;
13801
+ #paddingX;
13802
+ #paddingY;
13803
+ constructor(content, paddingX = 1, paddingY = 0) {
13804
+ this.#content = content;
13805
+ this.#paddingX = paddingX;
13806
+ this.#paddingY = paddingY;
13807
+ }
13808
+ invalidate() {}
13809
+ render(width) {
13810
+ return new TruncatedText(this.#content(), this.#paddingX, this.#paddingY).render(width);
13811
+ }
13812
+ }
13813
+ var ThemedText;
13814
+ var init_themed_text = __esm(() => {
13815
+ ThemedText = class ThemedText extends Text3 {
13816
+ #raw;
13817
+ #color;
13818
+ constructor(text, color, paddingX = 1, paddingY = 0) {
13819
+ super("", paddingX, paddingY);
13820
+ this.#raw = text;
13821
+ this.#color = color;
13822
+ }
13823
+ setText(text) {
13824
+ this.#raw = text;
13825
+ this.invalidate();
13826
+ }
13827
+ render(width) {
13828
+ super.setText(this.#color(this.#raw));
13829
+ return super.render(width);
13830
+ }
13831
+ };
13832
+ });
13833
+
13751
13834
  // packages/tui/src/components/tool-execution.ts
13752
- import { Box, Container as Container3, MouseRegion as MouseRegion2, Text as Text3 } from "@earendil-works/pi-tui";
13835
+ import { Box, Container as Container3, MouseRegion as MouseRegion2, Text as Text4 } from "@earendil-works/pi-tui";
13753
13836
  function currentToolFrame() {
13754
13837
  return SPINNER_FRAMES[spinnerFrame];
13755
13838
  }
@@ -13789,6 +13872,7 @@ var init_tool_execution = __esm(() => {
13789
13872
  init_theme();
13790
13873
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
13791
13874
  ToolExecutionComponent = class ToolExecutionComponent extends Container3 {
13875
+ #renderTheme = getActiveThemeMode();
13792
13876
  #state;
13793
13877
  #expanded = false;
13794
13878
  #box;
@@ -13796,12 +13880,19 @@ var init_tool_execution = __esm(() => {
13796
13880
  constructor(state) {
13797
13881
  super();
13798
13882
  this.#state = state;
13799
- this.#text = new Text3("", 0, 0);
13883
+ this.#text = new Text4("", 0, 0);
13800
13884
  this.#box = new Box(1, 0, theme.toolPendingBg);
13801
13885
  this.#box.addChild(new MouseRegion2(this.#text, (event) => this.#onClick(event)));
13802
13886
  this.addChild(this.#box);
13803
13887
  this.rebuild();
13804
13888
  }
13889
+ render(width) {
13890
+ if (this.#renderTheme !== getActiveThemeMode()) {
13891
+ this.#renderTheme = getActiveThemeMode();
13892
+ this.rebuild();
13893
+ }
13894
+ return super.render(width);
13895
+ }
13805
13896
  get isRunning() {
13806
13897
  return this.#state.phase === "running";
13807
13898
  }
@@ -13859,11 +13950,12 @@ ${theme.error(state.note)}`;
13859
13950
  });
13860
13951
 
13861
13952
  // packages/tui/src/components/user-message.ts
13862
- import { Container as Container4, Markdown as Markdown2, MouseRegion as MouseRegion3, Text as Text4 } from "@earendil-works/pi-tui";
13953
+ import { Container as Container4, Markdown as Markdown2, MouseRegion as MouseRegion3, Text as Text5 } from "@earendil-works/pi-tui";
13863
13954
  var UserMessageComponent;
13864
13955
  var init_user_message = __esm(() => {
13865
13956
  init_theme();
13866
13957
  UserMessageComponent = class UserMessageComponent extends Container4 {
13958
+ #renderTheme = getActiveThemeMode();
13867
13959
  #text;
13868
13960
  #skill;
13869
13961
  #defaultImagePrompt;
@@ -13884,6 +13976,10 @@ var init_user_message = __esm(() => {
13884
13976
  this.#rebuild();
13885
13977
  }
13886
13978
  render(width) {
13979
+ if (this.#renderTheme !== getActiveThemeMode()) {
13980
+ this.#renderTheme = getActiveThemeMode();
13981
+ this.#rebuild();
13982
+ }
13887
13983
  const bar = theme.userBg(theme.accent("\u258D"));
13888
13984
  return super.render(Math.max(1, width - 1)).map((line) => `${bar}${line}`);
13889
13985
  }
@@ -13891,14 +13987,14 @@ var init_user_message = __esm(() => {
13891
13987
  this.clear();
13892
13988
  const prompt = this.#defaultImagePrompt;
13893
13989
  if (prompt) {
13894
- this.addChild(new MouseRegion3(new Text4(theme.dim(`${this.#expanded ? "v" : ">"} Default image prompt \xB7 Click to ${this.#expanded ? "hide" : "view"}`), 1, 0, theme.userBg), (event) => {
13990
+ this.addChild(new MouseRegion3(new Text5(theme.dim(`${this.#expanded ? "v" : ">"} Default image prompt \xB7 Click to ${this.#expanded ? "hide" : "view"}`), 1, 0, theme.userBg), (event) => {
13895
13991
  if (event.type !== "click" || event.button !== "left")
13896
13992
  return;
13897
13993
  this.setExpanded(!this.#expanded);
13898
13994
  return { handled: true };
13899
13995
  }));
13900
13996
  if (this.#expanded)
13901
- this.addChild(new Text4(prompt.text, 1, 0, theme.userBg));
13997
+ this.addChild(new Text5(prompt.text, 1, 0, theme.userBg));
13902
13998
  if (!this.#text.trim())
13903
13999
  return;
13904
14000
  }
@@ -13908,7 +14004,7 @@ var init_user_message = __esm(() => {
13908
14004
  const preview = args.length > 100 ? `${args.slice(0, 99)}\u2026` : args;
13909
14005
  const head = `${theme.toolTitle(`\u2726 skill:${invocation.name}`)}${preview ? ` ${theme.userText(preview)}` : ""}`;
13910
14006
  const source = theme.dim(`skill body from ${invocation.location} (collapsed)`);
13911
- this.addChild(new Text4(`${head}
14007
+ this.addChild(new Text5(`${head}
13912
14008
  ${source}`, 1, 0, theme.userBg));
13913
14009
  return;
13914
14010
  }
@@ -14002,6 +14098,21 @@ async function queryTerminalMode(ui, onLateReply) {
14002
14098
  }
14003
14099
  return modeFromColors(colors) ?? detectColorFgBgMode() ?? "light";
14004
14100
  }
14101
+ function followTerminalTheme(ui, apply) {
14102
+ let active = true;
14103
+ const accept = (mode) => {
14104
+ if (active)
14105
+ apply(mode);
14106
+ };
14107
+ ui.setTerminalColorSchemeNotifications(true);
14108
+ const unsubscribe = ui.onTerminalColorSchemeChange(accept);
14109
+ queryTerminalMode(ui, accept).then(accept, () => {});
14110
+ return () => {
14111
+ active = false;
14112
+ unsubscribe();
14113
+ ui.setTerminalColorSchemeNotifications(false);
14114
+ };
14115
+ }
14005
14116
  var THEME_QUERY_TIMEOUT_MS = 100;
14006
14117
 
14007
14118
  // packages/core/src/file-mentions.ts
@@ -16822,7 +16933,7 @@ var init_image_input = __esm(() => {
16822
16933
  });
16823
16934
 
16824
16935
  // packages/tui/src/layout.ts
16825
- import { Text as Text5, VStack } from "@earendil-works/pi-tui";
16936
+ import { Text as Text6, VStack } from "@earendil-works/pi-tui";
16826
16937
  function createAppLayout(parts) {
16827
16938
  const normal = (v) => {
16828
16939
  parts.footer.panelOpen = parts.panelOpen();
@@ -16832,7 +16943,7 @@ function createAppLayout(parts) {
16832
16943
  };
16833
16944
  return new VStack([
16834
16945
  {
16835
- component: new Text5("Terminal too small. Resize to 40x10. Ctrl+C Exit", 0, 0),
16946
+ component: new Text6("Terminal too small. Resize to 40x10. Ctrl+C Exit", 0, 0),
16836
16947
  visible: (v) => !normal(v),
16837
16948
  grow: 1
16838
16949
  },
@@ -16840,7 +16951,7 @@ function createAppLayout(parts) {
16840
16951
  { component: parts.panel, basis: 0, grow: 1, minSize: 1, visible: (v) => normal(v) && parts.panelOpen() },
16841
16952
  { component: parts.notices, maxSize: 1, shrink: 0, visible: normal },
16842
16953
  {
16843
- component: parts.busy ?? new Text5("", 0, 0),
16954
+ component: parts.busy ?? new Text6("", 0, 0),
16844
16955
  maxSize: 2,
16845
16956
  shrink: 0,
16846
16957
  visible: (v) => normal(v) && (parts.busyVisible?.() ?? false)
@@ -17016,6 +17127,7 @@ class SkillDetailView {
17016
17127
 
17017
17128
  class SkillPreviewView {
17018
17129
  #header = null;
17130
+ #renderTheme = getActiveThemeMode();
17019
17131
  #markdown = null;
17020
17132
  #error = "";
17021
17133
  #offset = 0;
@@ -17026,7 +17138,7 @@ class SkillPreviewView {
17026
17138
  constructor(name, client, panel) {
17027
17139
  this.#panel = panel;
17028
17140
  client.skillMarket.preview({ name }).then((preview) => {
17029
- this.#header = [`${theme.dim("files:")} ${preview.files.join(" ")}`, ""];
17141
+ this.#header = preview.files.join(" ");
17030
17142
  this.#markdown = new Markdown3(preview.body, 0, 0, getMarkdownTheme());
17031
17143
  this.#panel.changed();
17032
17144
  }).catch((error) => {
@@ -17046,7 +17158,11 @@ class SkillPreviewView {
17046
17158
  lines(width, height) {
17047
17159
  if (this.#markdown === null || this.#header === null)
17048
17160
  return [this.#error !== "" ? theme.error(`Error: ${this.#error}`) : theme.dim("Loading preview...")];
17049
- const flat = [...this.#header, ...this.#markdown.render(width)];
17161
+ if (this.#renderTheme !== getActiveThemeMode()) {
17162
+ this.#renderTheme = getActiveThemeMode();
17163
+ this.#markdown.invalidate();
17164
+ }
17165
+ const flat = [`${theme.dim("files:")} ${this.#header}`, "", ...this.#markdown.render(width)];
17050
17166
  const max = Math.max(0, flat.length - height);
17051
17167
  this.#offset = Math.min(this.#offset, max);
17052
17168
  return flat.slice(this.#offset, this.#offset + height);
@@ -17083,7 +17199,7 @@ var init_skill_market_ui = __esm(() => {
17083
17199
  });
17084
17200
 
17085
17201
  // packages/tui/src/version-ui.ts
17086
- import { matchesKey as matchesKey6, Text as Text6 } from "@earendil-works/pi-tui";
17202
+ import { matchesKey as matchesKey6, Text as Text7 } from "@earendil-works/pi-tui";
17087
17203
  function actionFor(state, installed, runningVersion) {
17088
17204
  const action = updateAction({ gateway: state, frontend: runningVersion, installed });
17089
17205
  return action === "install" && !newer(state?.latestVersion, runningVersion) ? "none" : action;
@@ -17156,7 +17272,7 @@ class VersionDetail {
17156
17272
  this.body = body;
17157
17273
  }
17158
17274
  lines(width, height) {
17159
- const lines = new Text6(this.body, 0, 0).render(width);
17275
+ const lines = new Text7(this.body, 0, 0).render(width);
17160
17276
  this.#offset = Math.min(this.#offset, Math.max(0, lines.length - height));
17161
17277
  return lines.slice(this.#offset, this.#offset + height);
17162
17278
  }
@@ -17245,8 +17361,6 @@ import {
17245
17361
  matchesKey as matchesKey7,
17246
17362
  ProcessTerminal,
17247
17363
  ScrollView,
17248
- Text as Text7,
17249
- TruncatedText,
17250
17364
  TuiAltScreen
17251
17365
  } from "@earendil-works/pi-tui";
17252
17366
 
@@ -17467,7 +17581,7 @@ class NekoTuiApp {
17467
17581
  const notice = versionNotice(this.#gateway.client.application.info.value ?? null, installed);
17468
17582
  this.#versionNotice.clear();
17469
17583
  if (notice)
17470
- this.#versionNotice.addChild(new TruncatedText(formatNotice(notice, "info"), 1, 0));
17584
+ this.#versionNotice.addChild(new ThemedTruncatedText(() => formatNotice(notice, "info"), 1, 0));
17471
17585
  this.#ui.requestRender();
17472
17586
  };
17473
17587
  this.#versionStop = this.#gateway.client.application.info.subscribe(() => void refreshVersion());
@@ -17797,7 +17911,7 @@ class NekoTuiApp {
17797
17911
  const options = [
17798
17912
  { value: "auto", label: "Auto", description: "Follow the terminal background" },
17799
17913
  { value: "light", label: "Light \xB7 sakura-day", description: "Brand light theme" },
17800
- { value: "dark", label: "Dark \xB7 sakura-night", description: "Brand dark theme" }
17914
+ { value: "dark", label: "Dark", description: "Cool dark theme" }
17801
17915
  ];
17802
17916
  const ready = this.#panel.show("Theme", async () => options.map((option) => ({
17803
17917
  value: option.value,
@@ -17868,7 +17982,7 @@ class NekoTuiApp {
17868
17982
  this.#ui.requestRender();
17869
17983
  }, 5000);
17870
17984
  this.#notices.clear();
17871
- this.#notices.addChild(new TruncatedText(formatNotice(text, level), 1, 0));
17985
+ this.#notices.addChild(new ThemedTruncatedText(() => formatNotice(text, level), 1, 0));
17872
17986
  this.#ui.requestRender();
17873
17987
  }
17874
17988
  scheduleApply() {
@@ -18009,7 +18123,7 @@ class NekoTuiApp {
18009
18123
  this.#items.set(item.key, { kind: "tool", component });
18010
18124
  return;
18011
18125
  }
18012
- const component = new Text7(theme.dim(item.text), 1, 0);
18126
+ const component = new ThemedText(item.text, theme.dim, 1, 0);
18013
18127
  this.#imageTranscript.addItem(component);
18014
18128
  this.#items.set(item.key, { kind: "system", component });
18015
18129
  }
@@ -18027,7 +18141,7 @@ class NekoTuiApp {
18027
18141
  return;
18028
18142
  }
18029
18143
  if (entry.kind === "system" && item.type === "system") {
18030
- entry.component.setText(theme.dim(item.text));
18144
+ entry.component.setText(item.text);
18031
18145
  }
18032
18146
  }
18033
18147
  #toolCard(call) {
@@ -18116,13 +18230,10 @@ class NekoTuiApp {
18116
18230
  #startThemeDetection() {
18117
18231
  if (this.#themePreference !== "auto" || this.#terminalSchemeUnsubscribe !== undefined)
18118
18232
  return;
18119
- this.#ui.setTerminalColorSchemeNotifications(true);
18120
- this.#terminalSchemeUnsubscribe = this.#ui.onTerminalColorSchemeChange((scheme) => {
18121
- this.#applyThemeMode(scheme);
18233
+ this.#terminalSchemeUnsubscribe = followTerminalTheme(this.#ui, (mode) => {
18234
+ if (!this.#exitStarted && this.#themePreference === "auto")
18235
+ this.#applyThemeMode(mode);
18122
18236
  });
18123
- queryTerminalMode(this.#ui, (late) => {
18124
- this.#applyThemeMode(late);
18125
- }).then((mode) => this.#applyThemeMode(mode), () => {});
18126
18237
  }
18127
18238
  #applyThemeMode(mode) {
18128
18239
  if (this.#exitStarted || mode === getActiveThemeMode())
@@ -18208,6 +18319,7 @@ var init_tui = __esm(() => {
18208
18319
  init_login();
18209
18320
  init_notice();
18210
18321
  init_picker();
18322
+ init_themed_text();
18211
18323
  init_tool_execution();
18212
18324
  init_user_message();
18213
18325
  init_welcome();
@@ -18293,19 +18405,19 @@ function parseArgs(argv) {
18293
18405
  ...theme !== undefined ? { theme } : {}
18294
18406
  };
18295
18407
  }
18296
- var USAGE = `Usage: neko tui [options]
18408
+ var USAGE = `\u7528\u6CD5: neko tui [\u9009\u9879]
18297
18409
 
18298
- Options:
18299
- --cwd <dir> Workspace (default: launch directory; not an access sandbox)
18300
- --model <ref> provider/modelId for a spawned gateway
18301
- --storage <path> SQLite storage (default: ~/.neko/neko.sqlite)
18302
- --socket-path <path> Gateway Unix socket\uFF08POSIX\uFF1BWindows \u7ECF\u7AEF\u53E3\u6587\u4EF6\u53D1\u73B0 ws \u5730\u5740\uFF09
18303
- --theme <mode> light / dark / auto (overrides NEKO_THEME)
18304
- -h, --help Show help
18410
+ \u9009\u9879:
18411
+ --cwd <dir> \u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u542F\u52A8\u76EE\u5F55\uFF1B\u975E\u8BBF\u95EE\u6C99\u7BB1\uFF09
18412
+ --model <ref> \u6A21\u578B\uFF0C\u683C\u5F0F provider/modelId\uFF08\u7528\u4E8E\u62C9\u8D77\u7684\u7F51\u5173\uFF09
18413
+ --storage <path> SQLite \u5B58\u50A8\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/neko.sqlite\uFF09
18414
+ --socket-path <path> \u7F51\u5173 Unix socket \u8DEF\u5F84\uFF08\u4EC5 POSIX\uFF1BWindows \u7ECF\u7AEF\u53E3\u6587\u4EF6\u53D1\u73B0 ws \u5730\u5740\uFF09
18415
+ --theme <mode> \u4E3B\u9898 light / dark / auto\uFF08\u8986\u76D6 NEKO_THEME\uFF09
18416
+ -h, --help \u663E\u793A\u6B64\u5E2E\u52A9
18305
18417
 
18306
- Resumes the most recently used session in this workspace, or creates one.
18307
- A gateway is started when needed and stopped when its owning TUI exits.
18308
- Configure DeepSeek with /login or DEEPSEEK_API_KEY.`;
18418
+ \u6062\u590D\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6700\u8FD1\u4F7F\u7528\u7684\u4F1A\u8BDD\uFF0C\u4E0D\u5B58\u5728\u5219\u521B\u5EFA\u3002
18419
+ \u6309\u9700\u62C9\u8D77\u7F51\u5173\uFF0C\u5C5E\u4E3B TUI \u9000\u51FA\u65F6\u968F\u4E4B\u505C\u6B62\u3002
18420
+ \u53EF\u7528 /login \u6216 DEEPSEEK_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
18309
18421
  var init_main = __esm(async () => {
18310
18422
  init_tui();
18311
18423
  try {
@@ -18768,7 +18880,7 @@ function parseArgs2(argv) {
18768
18880
  ...model !== undefined ? { model } : {}
18769
18881
  };
18770
18882
  }
18771
- var USAGE2 = `\u7528\u6CD5: main.ts [\u9009\u9879]
18883
+ var USAGE2 = `\u7528\u6CD5: neko web [\u9009\u9879]
18772
18884
 
18773
18885
  \u9009\u9879:
18774
18886
  --port <n> \u9759\u6001 HTTP \u7AEF\u53E3\uFF08\u9ED8\u8BA4 8322\uFF09
@@ -18779,12 +18891,11 @@ var USAGE2 = `\u7528\u6CD5: main.ts [\u9009\u9879]
18779
18891
  --storage <path> SQLite \u5B58\u50A8\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/neko.sqlite\uFF09
18780
18892
  --cwd <dir> \u4F1A\u8BDD\u5DE5\u5177\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
18781
18893
  --model <ref> \u6A21\u578B\uFF0C\u683C\u5F0F provider/modelId\uFF08\u9ED8\u8BA4 deepseek/deepseek-flash\uFF09
18782
- --no-open \u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF08\u9002\u7528\u4E8E\u8FDC\u7A0B\u6216\u65E0\u684C\u9762\u73AF\u5883\uFF09
18894
+ --no-open \u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF08\u9002\u7528\u4E8E\u8FDC\u7A0B\u6216\u65E0\u684C\u9762\u73AF\u5883\uFF09
18783
18895
  -h, --help \u663E\u793A\u6B64\u5E2E\u52A9
18784
18896
 
18785
- \u53EF\u5728 Web \u8BBE\u7F6E\u9875\u914D\u7F6E Key\uFF1Bgateway \u5B50\u8FDB\u7A0B\u4E5F\u4F1A\u7EE7\u627F DEEPSEEK_API_KEY\u3002`;
18897
+ \u53EF\u5728 Web \u8BBE\u7F6E\u9875\u914D\u7F6E Key\uFF1B\u7F51\u5173\u5B50\u8FDB\u7A0B\u4E5F\u4F1A\u7EE7\u627F DEEPSEEK_API_KEY\u3002`;
18786
18898
  var init_main2 = __esm(async () => {
18787
- init_src();
18788
18899
  init_open_browser();
18789
18900
  init_server();
18790
18901
  try {
@@ -18799,14 +18910,12 @@ var init_main2 = __esm(async () => {
18799
18910
  ...options.storagePath !== undefined ? { storagePath: options.storagePath } : {},
18800
18911
  ...options.cwd !== undefined ? { cwd: options.cwd } : {},
18801
18912
  ...options.model !== undefined ? { model: options.model } : {},
18802
- onLog: (line) => console.log(` [gateway] ${line}`)
18913
+ onLog: (line) => console.log(`[gateway] ${line}`)
18803
18914
  }
18804
18915
  });
18805
- const model = options.model ?? DEFAULT_MODEL;
18806
- console.log(`neko web \u5DF2\u542F\u52A8: ${server.url}`);
18807
- console.log(` \u7F51\u5173: ${server.gateway.wsUrl}\uFF08serverId: ${server.gateway.serverId}\uFF09`);
18808
- console.log(` \u6A21\u578B: ${model.provider}/${model.modelId}`);
18809
- console.log("\u6309 Ctrl+C \u505C\u6B62\u3002");
18916
+ console.log(`[web] neko web \u5DF2\u542F\u52A8: ${server.url}`);
18917
+ console.log(`[web] gateway: ${server.gateway.wsUrl}\uFF08serverId: ${server.gateway.serverId}\uFF09`);
18918
+ console.log("[web] \u6309 Ctrl+C \u505C\u6B62\u3002");
18810
18919
  const shutdown = () => {
18811
18920
  console.log(`
18812
18921
  \u6B63\u5728\u5173\u95ED\u2026`);
@@ -18914,7 +19023,7 @@ function parseGatewayArgs(argv) {
18914
19023
  options.port = port;
18915
19024
  return options;
18916
19025
  }
18917
- var GATEWAY_USAGE = `\u7528\u6CD5: main.ts [\u9009\u9879]
19026
+ var GATEWAY_USAGE = `\u7528\u6CD5: neko serve [\u9009\u9879]
18918
19027
 
18919
19028
  \u9009\u9879:
18920
19029
  --host <ip> WebSocket \u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u4EC5\u672C\u673A\uFF09
@@ -18958,22 +19067,26 @@ var init_main3 = __esm(async () => {
18958
19067
  console.log(` Unix socket: ${gateway.socketPath}`);
18959
19068
  if (gateway.wsUrl !== undefined)
18960
19069
  console.log(` WebSocket: ${gateway.wsUrl}`);
18961
- console.log("Ctrl-C \u505C\u6B62\u3002");
19070
+ console.log("\u6309 Ctrl+C \u505C\u6B62\u3002");
18962
19071
  process.on("SIGINT", stop);
18963
19072
  process.on("SIGTERM", stop);
18964
19073
  });
18965
19074
 
18966
19075
  // packages/cli/src/main.ts
18967
19076
  init_application_version();
18968
- var USAGE3 = `\u7528\u6CD5: neko <command> [options]
19077
+ var USAGE3 = `\u7528\u6CD5: neko <\u547D\u4EE4> [\u9009\u9879]
18969
19078
 
18970
19079
  \u547D\u4EE4:
18971
- tui \u542F\u52A8\u7EC8\u7AEF\u4EA4\u4E92\u754C\u9762\uFF08\u81EA\u52A8\u62C9\u8D77 gateway\uFF09
18972
- web \u542F\u52A8 Web UI \u9759\u6001\u6258\u7BA1\uFF08\u81EA\u52A8\u62C9\u8D77 gateway\uFF09
19080
+ tui \u542F\u52A8\u7EC8\u7AEF\u4EA4\u4E92\u754C\u9762\uFF08\u81EA\u52A8\u62C9\u8D77\u7F51\u5173\uFF09
19081
+ web \u542F\u52A8 Web UI \u9759\u6001\u6258\u7BA1\uFF08\u81EA\u52A8\u62C9\u8D77\u7F51\u5173\uFF09
19082
+ serve \u4EC5\u542F\u52A8\u7F51\u5173\uFF08WebSocket\uFF1BPOSIX \u53E6\u6709 Unix socket\uFF09
18973
19083
  update \u5347\u7EA7\u5F53\u524D Bun \u5168\u5C40\u5B89\u88C5\uFF08Windows \u663E\u793A\u624B\u52A8\u5347\u7EA7\u547D\u4EE4\uFF09
18974
- serve \u4EC5\u542F\u52A8 gateway\uFF08WebSocket\uFF1BPOSIX \u53E6\u6709 Unix socket\uFF09
18975
19084
 
18976
- \u5404\u547D\u4EE4\u7684\u9009\u9879\u900F\u4F20\uFF0C\u8BE6\u89C1 neko <command> --help\u3002
19085
+ \u9009\u9879:
19086
+ -v, --version \u663E\u793A\u7248\u672C\u53F7
19087
+ -h, --help \u663E\u793A\u6B64\u5E2E\u52A9
19088
+
19089
+ \u5404\u547D\u4EE4\u7684\u9009\u9879\u8BE6\u89C1 neko <\u547D\u4EE4> --help\u3002
18977
19090
  \u53EF\u901A\u8FC7 TUI /login\u3001Web \u8BBE\u7F6E\u9875\u6216 DEEPSEEK_API_KEY / TAVILY_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
18978
19091
  var [command2, ...rest] = process.argv.slice(2);
18979
19092
  switch (command2) {
@@ -18981,9 +19094,12 @@ switch (command2) {
18981
19094
  if (rest.length) {
18982
19095
  if (rest.length === 1 && (rest[0] === "--help" || rest[0] === "-h"))
18983
19096
  console.log(`\u7528\u6CD5: neko update
19097
+
18984
19098
  \u5347\u7EA7\u5F53\u524D Bun \u5168\u5C40\u5B89\u88C5\uFF1B\u5B8C\u6210\u540E\u91CD\u65B0\u542F\u52A8 neko\u3002
18985
19099
  Windows\uFF1A\u5148\u9000\u51FA neko\uFF0C\u518D\u8FD0\u884C bun add --global neko-harness@latest\u3002
18986
- -h, --help \u663E\u793A\u5E2E\u52A9`);
19100
+
19101
+ \u9009\u9879:
19102
+ -h, --help \u663E\u793A\u6B64\u5E2E\u52A9`);
18987
19103
  else {
18988
19104
  console.error("\u7528\u6CD5: neko update");
18989
19105
  process.exitCode = 1;
package/package.json CHANGED
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "packageManager": "bun@1.4.2",
26
26
  "trustedDependencies": [],
27
- "version": "0.2.8",
27
+ "version": "0.2.9",
28
28
  "description": "A local AI coding assistant with terminal and web interfaces, powered by Pi and Bun.",
29
29
  "keywords": [
30
30
  "ai",