neko-harness 0.2.0 → 0.2.2

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,8 +9,8 @@
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-D6NpJLXy.js"></script>
13
- <link rel="stylesheet" crossorigin href="/assets/index-CxnbrvVk.css">
12
+ <script type="module" crossorigin src="/assets/index-CZ-wxzM4.js"></script>
13
+ <link rel="stylesheet" crossorigin href="/assets/index-6edxlI44.css">
14
14
  </head>
15
15
  <body>
16
16
  <div id="root"></div>
package/dist/gateway.js CHANGED
@@ -2446,6 +2446,7 @@ async function removeGatewayPortFile(nekoDir, serverId) {
2446
2446
  // packages/gateway/src/server-services.ts
2447
2447
  import {
2448
2448
  createRemoteServiceEndpoint,
2449
+ RemoteServiceError,
2449
2450
  RemoteServiceProvider,
2450
2451
  replicatedState
2451
2452
  } from "@earendil-works/chord";
@@ -2455,6 +2456,13 @@ import { spawn } from "child_process";
2455
2456
  import { accessSync as accessSync2, constants as constants2 } from "fs";
2456
2457
  import { realpath, stat } from "fs/promises";
2457
2458
  import { basename as basename2, delimiter, isAbsolute as isAbsolute2, join as join7, relative, resolve as resolve4, sep } from "path";
2459
+
2460
+ class MissingFileSearchDependencyError extends Error {
2461
+ constructor() {
2462
+ super("Install fd (or fdfind) to search workspace files.");
2463
+ this.name = "MissingFileSearchDependencyError";
2464
+ }
2465
+ }
2458
2466
  function findFd() {
2459
2467
  for (const name of process.platform === "win32" ? ["fd.exe", "fdfind.exe"] : ["fd", "fdfind"]) {
2460
2468
  for (const directory of (process.env.PATH ?? "").split(delimiter)) {
@@ -2484,7 +2492,7 @@ async function workspaceTarget(root, path) {
2484
2492
  }
2485
2493
  async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
2486
2494
  if (!fd)
2487
- throw new Error("Install fd (or fdfind) to search workspace files.");
2495
+ throw new MissingFileSearchDependencyError;
2488
2496
  query = relativeQuery(query);
2489
2497
  const slash = query.lastIndexOf("/");
2490
2498
  const prefix = slash < 0 ? "" : query.slice(0, slash + 1);
@@ -2508,6 +2516,8 @@ async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
2508
2516
  "--max-results",
2509
2517
  "101",
2510
2518
  "--print0",
2519
+ "--path-separator",
2520
+ "/",
2511
2521
  "--color",
2512
2522
  "never"
2513
2523
  ];
@@ -2576,6 +2586,26 @@ var NekoWorkspaces = defineService("neko.workspaces");
2576
2586
  var NekoCredentials = defineService("neko.credentials");
2577
2587
  var NekoWorkspaceFiles = defineService("neko.workspace-files");
2578
2588
 
2589
+ // packages/gateway/src/file-search-help.ts
2590
+ function fileSearchInstallHelp(platform = process.platform) {
2591
+ const docs = "https://github.com/sharkdp/fd#installation";
2592
+ let install;
2593
+ switch (platform) {
2594
+ case "win32":
2595
+ install = "\u8BF7\u6267\u884C\uFF1Awinget install sharkdp.fd\u3002";
2596
+ break;
2597
+ case "darwin":
2598
+ install = "\u8BF7\u6267\u884C\uFF1Abrew install fd\u3002";
2599
+ break;
2600
+ case "linux":
2601
+ install = `Debian/Ubuntu \u8BF7\u6267\u884C\uFF1Asudo apt install fd-find\u3002\u5176\u4ED6\u53D1\u884C\u7248\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
2602
+ break;
2603
+ default:
2604
+ install = `\u5B89\u88C5\u65B9\u6CD5\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
2605
+ }
2606
+ return `\u672A\u627E\u5230 fd \u6216 fdfind\uFF0C\u6682\u65F6\u65E0\u6CD5\u4F7F\u7528 @ \u6587\u4EF6\u8865\u5168\u3002\u8BF7\u5728\u8FD0\u884C neko \u7684\u7535\u8111\u4E0A\u5B89\u88C5\u3002${install}\u5B89\u88C5\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u5E76\u91CD\u542F neko\u3002`;
2607
+ }
2608
+
2579
2609
  // packages/gateway/src/workspaces.ts
2580
2610
  import { constants as constants3 } from "fs";
2581
2611
  import { access, readdir as readdir2, stat as stat2 } from "fs/promises";
@@ -2733,6 +2763,10 @@ async function createNekoServerServices(options) {
2733
2763
  if (!roots.some((w) => w.path === input.workspaceRoot))
2734
2764
  throw new Error("Unknown workspace.");
2735
2765
  return await searchWorkspaceFiles(input.workspaceRoot, input.query, AbortSignal.any([controller.signal, ...ctx.abortSignal ? [ctx.abortSignal] : []]));
2766
+ } catch (error) {
2767
+ if (error instanceof MissingFileSearchDependencyError)
2768
+ throw new RemoteServiceError("service_not_allowed", fileSearchInstallHelp());
2769
+ throw error;
2736
2770
  } finally {
2737
2771
  fileQueries--;
2738
2772
  if (fileQuery === controller)
@@ -2823,7 +2857,7 @@ import { open as open2 } from "fs/promises";
2823
2857
  import { basename as basename3, extname } from "path";
2824
2858
  import {
2825
2859
  createRemoteServiceEndpoint as createRemoteServiceEndpoint2,
2826
- RemoteServiceError,
2860
+ RemoteServiceError as RemoteServiceError2,
2827
2861
  RemoteServiceProvider as RemoteServiceProvider2,
2828
2862
  replicatedState as replicatedState2
2829
2863
  } from "@earendil-works/chord";
@@ -2991,7 +3025,7 @@ async function openNekoSessionHandle(options) {
2991
3025
  try {
2992
3026
  return action();
2993
3027
  } catch (error) {
2994
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
3028
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
2995
3029
  }
2996
3030
  };
2997
3031
  const createProvider = () => {
@@ -3048,7 +3082,7 @@ async function openNekoSessionHandle(options) {
3048
3082
  try {
3049
3083
  await session.compact(instructions);
3050
3084
  } catch (error) {
3051
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
3085
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
3052
3086
  }
3053
3087
  },
3054
3088
  beginImage: async (input) => safeUpload(() => uploads.begin(input)),
@@ -3057,15 +3091,15 @@ async function openNekoSessionHandle(options) {
3057
3091
  releaseImage: async (id) => uploads.release(id),
3058
3092
  getImage: async (input) => {
3059
3093
  if (!Number.isSafeInteger(input.offset) || input.offset < 0 || !Number.isSafeInteger(input.contentIndex) || input.contentIndex < 0)
3060
- throw new RemoteServiceError("service_invalid_value", "Invalid image range");
3094
+ throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
3061
3095
  const entry = history.get(input.entryId);
3062
3096
  const content = entry?.model?.[0]?.content;
3063
3097
  const image = Array.isArray(content) ? content[input.contentIndex] : undefined;
3064
3098
  if (image?.type !== "image")
3065
- throw new RemoteServiceError("service_invalid_value", "Image is not part of this conversation");
3099
+ throw new RemoteServiceError2("service_invalid_value", "Image is not part of this conversation");
3066
3100
  const bytes = Buffer.from(image.data, "base64");
3067
3101
  if (input.offset > bytes.length)
3068
- throw new RemoteServiceError("service_invalid_value", "Invalid image range");
3102
+ throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
3069
3103
  return {
3070
3104
  data: bytes.subarray(input.offset, input.offset + IMAGE_CHUNK_BYTES).toString("base64"),
3071
3105
  total: bytes.length,
@@ -3085,8 +3119,8 @@ async function openNekoSessionHandle(options) {
3085
3119
  uploads.release(id);
3086
3120
  } catch (error) {
3087
3121
  if (error instanceof Error && error.message.startsWith("DEEPSEEK_KEY_REQUIRED:"))
3088
- throw new RemoteServiceError("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
3089
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
3122
+ throw new RemoteServiceError2("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
3123
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
3090
3124
  }
3091
3125
  await onActivity();
3092
3126
  },
@@ -3105,7 +3139,7 @@ async function openNekoSessionHandle(options) {
3105
3139
  configure: async (change) => {
3106
3140
  const applied = await runtime.configureConversation(session.id, change);
3107
3141
  if (!applied) {
3108
- throw new RemoteServiceError("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
3142
+ throw new RemoteServiceError2("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
3109
3143
  }
3110
3144
  }
3111
3145
  });
package/dist/neko.js CHANGED
@@ -3465,7 +3465,7 @@ async function workspaceTarget(root, path) {
3465
3465
  }
3466
3466
  async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
3467
3467
  if (!fd)
3468
- throw new Error("Install fd (or fdfind) to search workspace files.");
3468
+ throw new MissingFileSearchDependencyError;
3469
3469
  query = relativeQuery(query);
3470
3470
  const slash = query.lastIndexOf("/");
3471
3471
  const prefix = slash < 0 ? "" : query.slice(0, slash + 1);
@@ -3489,6 +3489,8 @@ async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
3489
3489
  "--max-results",
3490
3490
  "101",
3491
3491
  "--print0",
3492
+ "--path-separator",
3493
+ "/",
3492
3494
  "--color",
3493
3495
  "never"
3494
3496
  ];
@@ -3546,7 +3548,15 @@ async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
3546
3548
  entries.sort((a, b) => (a.kind === b.kind ? 0 : a.kind === "directory" ? -1 : 1) || a.path.localeCompare(b.path));
3547
3549
  return { entries: entries.slice(0, 100), limited: paths.length > 100 };
3548
3550
  }
3549
- var init_workspace_files = () => {};
3551
+ var MissingFileSearchDependencyError;
3552
+ var init_workspace_files = __esm(() => {
3553
+ MissingFileSearchDependencyError = class MissingFileSearchDependencyError extends Error {
3554
+ constructor() {
3555
+ super("Install fd (or fdfind) to search workspace files.");
3556
+ this.name = "MissingFileSearchDependencyError";
3557
+ }
3558
+ };
3559
+ });
3550
3560
 
3551
3561
  // packages/tui/src/file-completion.ts
3552
3562
  import { CombinedAutocompleteProvider } from "@earendil-works/pi-tui";
@@ -3883,6 +3893,26 @@ async function removeGatewayPortFile(nekoDir, serverId) {
3883
3893
  }
3884
3894
  var init_port_file = () => {};
3885
3895
 
3896
+ // packages/gateway/src/file-search-help.ts
3897
+ function fileSearchInstallHelp(platform = process.platform) {
3898
+ const docs = "https://github.com/sharkdp/fd#installation";
3899
+ let install;
3900
+ switch (platform) {
3901
+ case "win32":
3902
+ install = "\u8BF7\u6267\u884C\uFF1Awinget install sharkdp.fd\u3002";
3903
+ break;
3904
+ case "darwin":
3905
+ install = "\u8BF7\u6267\u884C\uFF1Abrew install fd\u3002";
3906
+ break;
3907
+ case "linux":
3908
+ install = `Debian/Ubuntu \u8BF7\u6267\u884C\uFF1Asudo apt install fd-find\u3002\u5176\u4ED6\u53D1\u884C\u7248\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
3909
+ break;
3910
+ default:
3911
+ install = `\u5B89\u88C5\u65B9\u6CD5\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
3912
+ }
3913
+ return `\u672A\u627E\u5230 fd \u6216 fdfind\uFF0C\u6682\u65F6\u65E0\u6CD5\u4F7F\u7528 @ \u6587\u4EF6\u8865\u5168\u3002\u8BF7\u5728\u8FD0\u884C neko \u7684\u7535\u8111\u4E0A\u5B89\u88C5\u3002${install}\u5B89\u88C5\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u5E76\u91CD\u542F neko\u3002`;
3914
+ }
3915
+
3886
3916
  // packages/gateway/src/workspaces.ts
3887
3917
  import { constants as constants3 } from "fs";
3888
3918
  import { access, readdir as readdir2, stat as stat2 } from "fs/promises";
@@ -3950,6 +3980,7 @@ var init_workspaces = __esm(() => {
3950
3980
  // packages/gateway/src/server-services.ts
3951
3981
  import {
3952
3982
  createRemoteServiceEndpoint,
3983
+ RemoteServiceError,
3953
3984
  RemoteServiceProvider,
3954
3985
  replicatedState
3955
3986
  } from "@earendil-works/chord";
@@ -4048,6 +4079,10 @@ async function createNekoServerServices(options) {
4048
4079
  if (!roots.some((w) => w.path === input.workspaceRoot))
4049
4080
  throw new Error("Unknown workspace.");
4050
4081
  return await searchWorkspaceFiles(input.workspaceRoot, input.query, AbortSignal.any([controller.signal, ...ctx.abortSignal ? [ctx.abortSignal] : []]));
4082
+ } catch (error) {
4083
+ if (error instanceof MissingFileSearchDependencyError)
4084
+ throw new RemoteServiceError("service_not_allowed", fileSearchInstallHelp());
4085
+ throw error;
4051
4086
  } finally {
4052
4087
  fileQueries--;
4053
4088
  if (fileQuery === controller)
@@ -4241,7 +4276,7 @@ import { open as open2 } from "fs/promises";
4241
4276
  import { basename as basename4, extname } from "path";
4242
4277
  import {
4243
4278
  createRemoteServiceEndpoint as createRemoteServiceEndpoint2,
4244
- RemoteServiceError,
4279
+ RemoteServiceError as RemoteServiceError2,
4245
4280
  RemoteServiceProvider as RemoteServiceProvider2,
4246
4281
  replicatedState as replicatedState2
4247
4282
  } from "@earendil-works/chord";
@@ -4313,7 +4348,7 @@ async function openNekoSessionHandle(options) {
4313
4348
  try {
4314
4349
  return action();
4315
4350
  } catch (error) {
4316
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
4351
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
4317
4352
  }
4318
4353
  };
4319
4354
  const createProvider = () => {
@@ -4370,7 +4405,7 @@ async function openNekoSessionHandle(options) {
4370
4405
  try {
4371
4406
  await session.compact(instructions);
4372
4407
  } catch (error) {
4373
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
4408
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
4374
4409
  }
4375
4410
  },
4376
4411
  beginImage: async (input) => safeUpload(() => uploads.begin(input)),
@@ -4379,15 +4414,15 @@ async function openNekoSessionHandle(options) {
4379
4414
  releaseImage: async (id) => uploads.release(id),
4380
4415
  getImage: async (input) => {
4381
4416
  if (!Number.isSafeInteger(input.offset) || input.offset < 0 || !Number.isSafeInteger(input.contentIndex) || input.contentIndex < 0)
4382
- throw new RemoteServiceError("service_invalid_value", "Invalid image range");
4417
+ throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
4383
4418
  const entry = history.get(input.entryId);
4384
4419
  const content = entry?.model?.[0]?.content;
4385
4420
  const image = Array.isArray(content) ? content[input.contentIndex] : undefined;
4386
4421
  if (image?.type !== "image")
4387
- throw new RemoteServiceError("service_invalid_value", "Image is not part of this conversation");
4422
+ throw new RemoteServiceError2("service_invalid_value", "Image is not part of this conversation");
4388
4423
  const bytes = Buffer.from(image.data, "base64");
4389
4424
  if (input.offset > bytes.length)
4390
- throw new RemoteServiceError("service_invalid_value", "Invalid image range");
4425
+ throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
4391
4426
  return {
4392
4427
  data: bytes.subarray(input.offset, input.offset + IMAGE_CHUNK_BYTES).toString("base64"),
4393
4428
  total: bytes.length,
@@ -4407,8 +4442,8 @@ async function openNekoSessionHandle(options) {
4407
4442
  uploads.release(id);
4408
4443
  } catch (error) {
4409
4444
  if (error instanceof Error && error.message.startsWith("DEEPSEEK_KEY_REQUIRED:"))
4410
- throw new RemoteServiceError("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
4411
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
4445
+ throw new RemoteServiceError2("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
4446
+ throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
4412
4447
  }
4413
4448
  await onActivity();
4414
4449
  },
@@ -4427,7 +4462,7 @@ async function openNekoSessionHandle(options) {
4427
4462
  configure: async (change) => {
4428
4463
  const applied = await runtime.configureConversation(session.id, change);
4429
4464
  if (!applied) {
4430
- throw new RemoteServiceError("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
4465
+ throw new RemoteServiceError2("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
4431
4466
  }
4432
4467
  }
4433
4468
  });
@@ -6580,7 +6615,7 @@ switch (command2) {
6580
6615
  }
6581
6616
  case "--version":
6582
6617
  case "-v":
6583
- console.log("0.2.0");
6618
+ console.log("0.2.2");
6584
6619
  break;
6585
6620
  case undefined:
6586
6621
  case "-h":
package/package.json CHANGED
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "packageManager": "bun@1.4.2",
26
26
  "trustedDependencies": [],
27
- "version": "0.2.0",
27
+ "version": "0.2.2",
28
28
  "description": "A local AI coding assistant with terminal and web interfaces, powered by Pi and Bun.",
29
29
  "license": "MIT",
30
30
  "repository": {
@@ -1,2 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:"SF Pro Text", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono:ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:1.4;--text-sm:.8125rem;--text-sm--line-height:1.5;--text-base:.875rem;--text-lg:1rem;--text-xl:1.25rem;--text-xl--line-height:1.4;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--tracking-tight:-.025em;--radius-lg:.5rem;--radius-xl:.625rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-base:#fefcfe;--color-surface:#fff;--color-sakura-50:#fff1f3;--color-sakura-100:#ffe5ea;--color-sakura-200:#ffd1da;--color-sakura-300:#ffb7c5;--color-sakura-500:#da7f93;--color-sakura-600:#c26279;--color-sakura-700:#a74660;--color-sakura-800:#8b314a;--color-sakura-900:#6e1d36;--color-tianyi-50:#eff8fe;--color-tianyi-100:#dcf3ff;--color-tianyi-200:#b4e6ff;--color-tianyi-300:#6cf;--color-tianyi-500:#059fd7;--color-tianyi-600:#0086bf;--color-tianyi-700:#006ea7;--color-tianyi-800:#00598f;--color-tianyi-900:#004778;--color-ink-50:#f7f4f8;--color-ink-100:#f0ebf1;--color-ink-200:#e2dbe4;--color-ink-400:#9e95a0;--color-ink-500:#7a717c;--color-ink-600:#6a5f6c;--color-ink-700:#554857;--color-ink-800:#443947;--color-ink-900:#322b34;--breakpoint-mobile:48rem;--radius-control:.625rem;--radius-card:1rem;--radius-bubble:1.25rem;--shadow-soft:0 2px 10px 0 oklch(53% .13 6.2/.08);--shadow-bubble-user:0 3px 16px 0 oklch(53% .13 6.2/.16), 0 1px 4px 0 oklch(53% .13 6.2/.1);--shadow-bubble-assistant:0 3px 16px 0 oklch(43% .14 232.7/.12), 0 1px 4px 0 oklch(53% .13 6.2/.06);--shadow-lift:0 10px 32px 0 oklch(53% .13 6.2/.14);--animate-blink:blink 1.2s step-end infinite}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components{.chrome{color:var(--color-ink-900);background:var(--color-base);font-size:var(--text-sm)}.chrome button,.chrome a,.chrome input{border-radius:var(--radius-control)}.chrome button,.chrome a{cursor:pointer}.chrome button:disabled{cursor:default;opacity:.45}.chrome :is(button,a,input){outline:none}.chrome :is(button,a,input):focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500)}.chrome input:not([type=checkbox]){background:var(--color-surface);border:1px solid var(--color-sakura-200);min-width:0;padding:7px 9px}.chrome svg{flex-shrink:0}.sidebar{--sidebar-width:256px;width:var(--sidebar-width);border-right:1px solid var(--color-sakura-200);flex-direction:column;flex-shrink:0;display:flex}.sidebar-top{padding:12px 10px 6px}.workspace-heading{align-items:center;gap:2px;margin-bottom:14px;display:flex}.workspace-trigger{flex:1;align-items:center;gap:8px;min-width:0;padding:6px 3px;font-weight:600;display:flex}.workspace-mark{background:var(--color-sakura-100);color:var(--color-sakura-800);border-radius:var(--radius-control);padding:5px;display:inline-flex}.icon-btn{width:28px;height:28px;color:var(--color-ink-500);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.chrome :is(.chrome-row,.icon-btn,.workspace-trigger,.workspace-option):hover:not(:disabled){background:var(--color-sakura-50)}.chrome-row{text-align:left;align-items:center;gap:8px;width:100%;min-height:32px;padding:5px 8px;text-decoration:none;display:flex}.search-box{color:var(--color-ink-500);align-items:center;gap:7px;margin-top:8px;padding:0 7px;display:flex}.chrome .search-box input{width:100%;font-size:var(--text-xs);background:0 0;border:0;padding:5px 0}.section-label{font-size:var(--text-xs);color:var(--color-ink-500);justify-content:space-between;padding:12px 18px 6px;font-weight:600;display:flex}.session-scroll{flex:1;min-height:0;padding:2px 8px;overflow-y:auto}.session-list{margin:0;padding:0;list-style:none}.session-row{border-radius:var(--radius-control);align-items:center;margin:1px 0;display:flex}.session-row:hover{background:var(--color-sakura-50)}.chrome .is-active{background:var(--color-sakura-100);font-weight:600}.session-select{flex:1;min-width:0}.row-more{opacity:0;margin-right:3px}.session-row:hover .row-more,.session-row:focus-within .row-more{opacity:1}.sidebar-bottom{border-top:1px solid var(--color-sakura-200);padding:10px}.sidebar-bottom .path-text{max-height:44px;padding:0 7px 7px;font-size:10px;display:block;overflow:auto}.path-text{overflow-wrap:anywhere;color:var(--color-ink-500);font-size:var(--text-xs)}.empty-text{color:var(--color-ink-500);text-align:center;font-size:var(--text-xs);padding:24px 12px}.topbar{border-bottom:1px solid var(--color-sakura-200);background:var(--color-surface);align-items:center;gap:10px;min-height:52px;padding:10px 16px;display:flex}.topbar-path{align-items:center;gap:7px;min-width:0;max-width:40%;display:flex}.conn-status{font-size:var(--text-xs);white-space:nowrap;color:var(--color-ink-500);align-items:center;gap:5px;margin-left:auto;display:flex}.conn-status:before{content:"";background:var(--color-ink-400);border-radius:50%;width:5px;height:5px}.conn-status.is-connected:before{background:var(--color-tianyi-600)}.conn-status.is-error:before{background:var(--color-sakura-600)}.popover{z-index:100;border:1px solid var(--color-sakura-200);background:var(--color-surface);border-radius:var(--radius-card);width:280px;max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);box-shadow:var(--shadow-lift);padding:5px;position:fixed;overflow-y:auto}.popover .section-label{padding:8px}.workspace-options{max-height:45dvh;overflow:auto}.workspace-option{text-align:left;align-items:center;gap:10px;width:100%;padding:9px;display:flex}.workspace-option>span:first-of-type{flex:1;min-width:0}.workspace-option strong,.workspace-option small{overflow-wrap:anywhere;display:block}.workspace-option small{color:var(--color-ink-500);margin-top:2px;font-size:10px;font-weight:400}.popover .add-workspace{border-top:1px solid var(--color-sakura-200);border-radius:0;margin-top:5px}.dialog{border:1px solid var(--color-sakura-200);border-radius:var(--radius-card);background:var(--color-surface);width:min(640px,100vw - 32px);max-height:calc(100dvh - 32px);box-shadow:var(--shadow-lift);margin:auto;padding:0}.dialog[open]{flex-direction:column;display:flex}.dialog::backdrop{background:#322b3454}.dialog-heading{border-bottom:1px solid var(--color-sakura-200);flex-shrink:0;justify-content:space-between;align-items:center;padding:14px 18px;display:flex}.dialog-heading h2{font-weight:600;font-size:var(--text-lg)}.picker-controls{flex-shrink:0;padding:12px 18px}.picker-controls>p{font-size:var(--text-xs);margin-bottom:8px}.breadcrumbs{white-space:nowrap;gap:2px;padding:3px 0 8px;display:flex;overflow-x:auto}.breadcrumbs button{color:var(--color-ink-500);padding:2px 3px}.path-form{gap:6px;display:flex}.path-form input{flex:1}.filter-row{align-items:center;gap:12px;margin-top:10px;display:flex}.filter-row input:not([type=checkbox]){flex:1}.filter-row label{white-space:nowrap;font-size:var(--text-xs);align-items:center;gap:5px;display:flex}.folder-list{border-top:1px solid var(--color-sakura-200);min-inline-size:0;height:300px;min-height:100px;padding:4px 12px;overflow-y:auto}.picker-footer{border-top:1px solid var(--color-sakura-200);flex-shrink:0;padding:14px 18px}.picker-footer .path-text{max-height:50px;margin:3px 0 12px;overflow-y:auto}.actions{justify-content:flex-end;gap:8px;display:flex}.btn-outline{border:1px solid var(--color-sakura-200);background:var(--color-surface);color:var(--color-ink-700);padding:6px 12px;font-weight:500}.btn-outline:hover:not(:disabled){background:var(--color-sakura-50)}.error-text{color:var(--color-sakura-800);overflow-wrap:anywhere;padding:10px 0}.error-text .btn-outline{margin-left:10px}.confirm-body{padding:20px}.confirm-body .actions{margin-top:24px}.drawer{--sidebar-width:min(300px, calc(100vw - 48px));width:var(--sidebar-width);background:var(--color-base);border-radius:0;height:100dvh;max-height:100dvh;margin:0 auto 0 0}@media (hover:none){.row-more{opacity:1}.chrome :is(.icon-btn,.chrome-row,.btn-outline,.workspace-option){min-height:44px}.icon-btn{min-width:44px}}@media (width<=48rem){.topbar{gap:6px;padding:8px}.conn-status{font-size:10px}.dialog:not(.drawer){width:calc(100vw - 16px);max-height:calc(100dvh - 16px)}.picker-controls,.picker-footer{padding:12px}.folder-list{height:min(300px,40dvh)}}.session-select{align-items:flex-start;padding-top:8px;padding-bottom:8px;position:relative}.session-copy{text-align:left;flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.session-meta{font-size:var(--text-xs);color:var(--color-ink-500);white-space:nowrap;font-weight:400}.session-tooltip{z-index:110;box-sizing:border-box;width:min(calc(var(--sidebar-width,256px) - 24px), calc(100vw - 24px));border:1px solid var(--color-sakura-200);border-radius:var(--radius-control);background:var(--color-surface);color:var(--color-ink-900);font-size:var(--text-xs);white-space:normal;overflow-wrap:anywhere;box-shadow:var(--shadow-soft);pointer-events:none;padding:10px;font-weight:400;display:none;position:fixed;bottom:80px;left:12px}.session-select:hover .session-tooltip,.session-select:focus-visible .session-tooltip{display:block}.session-details{overflow-wrap:anywhere;max-width:280px;padding:8px 10px}.session-details strong,.session-details small{display:block}.session-details small{color:var(--color-ink-500);margin-top:4px}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.right-0{right:0}.z-50{z-index:50}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-\[14vh\]{margin-top:14vh}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-10{height:calc(var(--spacing) * 10)}.h-16{height:calc(var(--spacing) * 16)}.h-dvh{height:100dvh}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-\[65dvh\]{max-height:65dvh}.min-h-0{min-height:0}.min-h-4{min-height:calc(var(--spacing) * 4)}.min-h-7{min-height:calc(var(--spacing) * 7)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-dvh{min-height:100dvh}.w-10{width:calc(var(--spacing) * 10)}.w-20{width:calc(var(--spacing) * 20)}.w-28{width:calc(var(--spacing) * 28)}.w-full{width:100%}.max-w-48{max-width:calc(var(--spacing) * 48)}.max-w-64{max-width:calc(var(--spacing) * 64)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:0}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-spin{animation:var(--animate-spin)}.resize{resize:both}.resize-none{resize:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-ink-200{border-color:var(--color-ink-200)}.border-sakura-100{border-color:var(--color-sakura-100)}.border-sakura-200{border-color:var(--color-sakura-200)}.border-sakura-200\/70{border-color:#ffd1dab3}@supports (color:color-mix(in lab, red, red)){.border-sakura-200\/70{border-color:color-mix(in oklab, var(--color-sakura-200) 70%, transparent)}}.border-sakura-300{border-color:var(--color-sakura-300)}.border-sakura-500{border-color:var(--color-sakura-500)}.border-tianyi-100{border-color:var(--color-tianyi-100)}.border-tianyi-200{border-color:var(--color-tianyi-200)}.border-tianyi-300{border-color:var(--color-tianyi-300)}.bg-base{background-color:var(--color-base)}.bg-base\/70{background-color:#fefcfeb3}@supports (color:color-mix(in lab, red, red)){.bg-base\/70{background-color:color-mix(in oklab, var(--color-base) 70%, transparent)}}.bg-ink-50{background-color:var(--color-ink-50)}.bg-ink-100{background-color:var(--color-ink-100)}.bg-sakura-50{background-color:var(--color-sakura-50)}.bg-sakura-50\/70{background-color:#fff1f3b3}@supports (color:color-mix(in lab, red, red)){.bg-sakura-50\/70{background-color:color-mix(in oklab, var(--color-sakura-50) 70%, transparent)}}.bg-sakura-100{background-color:var(--color-sakura-100)}.bg-surface{background-color:var(--color-surface)}.bg-surface\/80{background-color:#fffc}@supports (color:color-mix(in lab, red, red)){.bg-surface\/80{background-color:color-mix(in oklab, var(--color-surface) 80%, transparent)}}.bg-tianyi-50{background-color:var(--color-tianyi-50)}.bg-tianyi-100{background-color:var(--color-tianyi-100)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-5{padding:calc(var(--spacing) * 5)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-base{color:var(--color-base)}.text-ink-400{color:var(--color-ink-400)}.text-ink-500{color:var(--color-ink-500)}.text-ink-600{color:var(--color-ink-600)}.text-ink-700{color:var(--color-ink-700)}.text-ink-900{color:var(--color-ink-900)}.text-sakura-700{color:var(--color-sakura-700)}.text-sakura-800{color:var(--color-sakura-800)}.text-sakura-900{color:var(--color-sakura-900)}.text-tianyi-500{color:var(--color-tianyi-500)}.text-tianyi-600{color:var(--color-tianyi-600)}.text-tianyi-700{color:var(--color-tianyi-700)}.text-tianyi-800{color:var(--color-tianyi-800)}.text-tianyi-900{color:var(--color-tianyi-900)}.italic{font-style:italic}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.shadow-soft{--tw-shadow:0 2px 10px 0 var(--tw-shadow-color,oklch(53% .13 6.2/.08));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-sakura-300{--tw-ring-color:var(--color-sakura-300)}.empty\:mb-0:empty{margin-bottom:0}@media (hover:hover){.hover\:bg-sakura-50:hover{background-color:var(--color-sakura-50)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:py-12{padding-block:calc(var(--spacing) * 12)}}.\[\&\>button\]\:max-w-full>button{max-width:100%}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}html,body{height:100%}body{background-color:var(--color-base);color:var(--color-ink-900);font-family:var(--font-sans);font-size:var(--text-base);-webkit-font-smoothing:antialiased;background-image:linear-gradient(150deg,#fff 0%,#fff5f8 42%,#eff9ff 100%);background-attachment:fixed}.chat-column{width:100%;max-width:48rem;margin-inline:auto}.chat-bubble{max-width:85%}@media (width>=40rem){.chat-bubble{max-width:75%}}:root{accent-color:var(--color-sakura-600)}::selection{background-color:var(--color-sakura-200);color:var(--color-sakura-900)}*{scrollbar-width:thin;scrollbar-color:var(--color-sakura-200) transparent}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background-color:var(--color-sakura-200);border-radius:8px}::-webkit-scrollbar-thumb:hover{background-color:var(--color-sakura-300)}.focus-neko{outline:none}.focus-neko:focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500)}@media (forced-colors:active){.focus-neko:focus-visible{box-shadow:none;outline:2px solid highlight}}input,textarea,select,button{font:inherit;color:inherit}.bubble-user{background-image:linear-gradient(135deg, var(--color-sakura-100) 0%, var(--color-sakura-200) 100%);border:1px solid var(--color-sakura-300);color:var(--color-sakura-900);border-radius:var(--radius-bubble);box-shadow:var(--shadow-bubble-user);border-bottom-right-radius:.375rem}.bubble-assistant{background-color:var(--color-surface);border:1px solid var(--color-tianyi-200);color:var(--color-ink-900);border-radius:var(--radius-bubble);box-shadow:var(--shadow-bubble-assistant);border-bottom-left-radius:.375rem}.btn-primary{background-image:linear-gradient(135deg, var(--color-sakura-200) 0%, var(--color-tianyi-200) 100%);border:1px solid #ffd4dc}@supports (color:color-mix(in lab, red, red)){.btn-primary{border:1px solid color-mix(in oklab, var(--color-sakura-300) 60%, white)}}.btn-primary{color:var(--color-sakura-900);box-shadow:inset 0 1px 0 #ffffffbf, var(--shadow-soft)}.btn-primary:hover{filter:brightness(1.05);box-shadow:inset 0 1px 0 #ffffffbf, var(--shadow-lift)}.btn-primary:disabled{opacity:.45;filter:saturate(.5)}.btn-stop{background-color:var(--color-surface);border:1px solid var(--color-sakura-300);color:var(--color-sakura-800)}.btn-stop:hover{background-color:var(--color-sakura-50)}.btn-lift{transition:transform .16s,box-shadow .16s,filter .16s,background-color .16s}.btn-lift:hover{transform:translateY(-1px)}.btn-lift:active{transform:translateY(0)scale(.97)}@keyframes msg-in-right{0%{opacity:0;transform:translate(10px,8px)}to{opacity:1;transform:none}}@keyframes msg-in-left{0%{opacity:0;transform:translate(-10px,8px)}to{opacity:1;transform:none}}.anim-user{animation:.34s cubic-bezier(.2,.8,.3,1) backwards msg-in-right}.anim-other{animation:.34s cubic-bezier(.2,.8,.3,1) backwards msg-in-left}@keyframes stream-in{0%{opacity:0;transform:translateY(10px)scale(.985)}to{opacity:1;transform:none}}.stream-block{animation:.38s ease-out backwards stream-in}@keyframes dot-bounce{0%,60%,to{opacity:.55;transform:translateY(0)}30%{opacity:1;transform:translateY(-2.5px)}}.dots{align-items:center;gap:3px;display:inline-flex}.dots>span{background-color:var(--color-sakura-500);border-radius:9999px;width:5px;height:5px;animation:1.2s ease-in-out infinite dot-bounce}.dots>span:nth-child(2){animation-delay:.15s}.dots>span:nth-child(3){animation-delay:.3s}.logo-gradient{color:#0000;background-image:linear-gradient(115deg,#c26279 0%,#da7f93 35%,#059fd7 100%);-webkit-background-clip:text;background-clip:text}@keyframes cat-float{0%,to{transform:translateY(0)}50%{transform:translateY(-4px)}}.welcome-cat{animation:3.2s ease-in-out infinite cat-float}.discrete-in{opacity:1;transition:opacity .25s ease, transform .25s ease, display .25s ease allow-discrete;transform:none}@starting-style{.discrete-in{opacity:0;transform:translateY(6px)}}.md{overflow-wrap:anywhere;line-height:1.75}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5rem 0}.md h1,.md h2,.md h3,.md h4{color:var(--color-sakura-800);margin:.9rem 0 .4rem;font-weight:700}.md h1{font-size:1.25rem}.md h2{font-size:1.125rem}.md h3,.md h4{font-size:1rem}.md a{color:var(--color-tianyi-800);text-decoration:underline;-webkit-text-decoration-color:var(--color-sakura-300);-webkit-text-decoration-color:var(--color-sakura-300);-webkit-text-decoration-color:var(--color-sakura-300);text-decoration-color:var(--color-sakura-300);text-underline-offset:3px;border-radius:2px}.md a:hover{background-color:var(--color-tianyi-100)}.md ul,.md ol{margin:.5rem 0;padding-left:1.4rem}.md ul{list-style:outside}.md ol{list-style:decimal}.md li{margin:.2rem 0}.md li::marker{color:var(--color-sakura-500)}.md blockquote{border-left:3px solid var(--color-sakura-300);background-color:var(--color-sakura-50);color:var(--color-ink-700);border-radius:0 .625rem .625rem 0;margin:.6rem 0;padding:.2rem .9rem}.md code{font-family:var(--font-mono);background-color:var(--color-ink-100);color:var(--color-ink-800);border-radius:.25rem;padding:.0625rem .25rem;font-size:.85em}.md pre{background-color:var(--color-tianyi-50);color:var(--color-ink-900);border:1px solid var(--color-tianyi-200);border-radius:.75rem;margin:.6rem 0;padding:.75rem .9rem;font-size:.85rem;line-height:1.6;overflow-x:auto}.md pre code{color:inherit;font-size:inherit;background:0 0;padding:0}.hljs{color:var(--color-ink-900);background:0 0}.hljs-comment,.hljs-quote{color:var(--color-ink-600);font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-tag,.hljs-name{color:var(--color-sakura-800)}.hljs-string,.hljs-doctag,.hljs-regexp{color:var(--color-tianyi-800)}.hljs-number,.hljs-symbol,.hljs-bullet,.hljs-link{color:var(--color-tianyi-700)}.hljs-title,.hljs-section,.hljs-attr{color:var(--color-sakura-700)}.hljs-built_in,.hljs-type,.hljs-attribute,.hljs-selector-class,.hljs-selector-id,.hljs-meta{color:var(--color-tianyi-700)}.hljs-variable,.hljs-template-variable,.hljs-params,.hljs-subst{color:var(--color-ink-900)}.hljs-addition{color:#117555;background-color:oklch(50% .1 165/.08)}.hljs-deletion{color:var(--color-sakura-800);background-color:oklch(53% .13 6.2/.08)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.md hr{border:none;border-top:1px dashed var(--color-sakura-300);margin:.9rem 0}.md table{border-collapse:collapse;margin:.6rem 0;font-size:.9rem}.md th,.md td{border:1px solid var(--color-tianyi-200);padding:.3rem .7rem}.md th{background-color:var(--color-tianyi-50);color:var(--color-tianyi-900);font-weight:600}.stream-caret{background-color:var(--color-sakura-500);vertical-align:-.15em;width:.5rem;height:1rem;animation:var(--animate-blink);border-radius:2px;margin-left:2px;display:inline-block}.chat-row{content-visibility:auto;contain-intrinsic-size:auto 120px}.skip-link{z-index:200;border-radius:var(--radius-control);border:1px solid var(--color-sakura-300);background:var(--color-surface);color:var(--color-sakura-800);font-size:var(--text-sm);box-shadow:var(--shadow-soft);padding:8px 12px;font-weight:500;transition:top .15s;position:absolute;top:-48px;left:8px}.skip-link:focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500);outline:none;top:8px}@media (prefers-reduced-motion:reduce){.anim-user,.anim-other,.stream-block,.welcome-cat{animation:none}.dots>span{opacity:1;animation:none}.stream-caret,.animate-spin{animation:none}.btn-lift,.discrete-in{transition:none}.btn-lift:hover,.btn-lift:active{transform:none}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@keyframes spin{to{transform:rotate(360deg)}}@keyframes blink{0%,to{opacity:1}50%{opacity:0}}