research-copilot 0.2.1 → 0.2.3

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.
Files changed (66) hide show
  1. package/app/out/main/index.mjs +2520 -201
  2. package/app/out/preload/index.js +21 -0
  3. package/app/out/renderer/assets/{MilkdownMarkdownEditor-D7GYpVZn.js → MilkdownMarkdownEditor-DjFLwyh4.js} +50 -50
  4. package/app/out/renderer/assets/{arc-Kp4J_Jd7.js → arc-7NllWjXF.js} +1 -1
  5. package/app/out/renderer/assets/{blockDiagram-c4efeb88-DkMSdn8j.js → blockDiagram-c4efeb88-DsOU-_zA.js} +8 -8
  6. package/app/out/renderer/assets/{c4Diagram-c83219d4-DqAGxrYw.js → c4Diagram-c83219d4-U2jdevgc.js} +3 -3
  7. package/app/out/renderer/assets/{channel-S4GQrISQ.js → channel-Cjd8jvi8.js} +1 -1
  8. package/app/out/renderer/assets/{classDiagram-beda092f-B7AsTCEg.js → classDiagram-beda092f-Yj0hyRiU.js} +6 -6
  9. package/app/out/renderer/assets/{classDiagram-v2-2358418a-B4oFy-In.js → classDiagram-v2-2358418a-CK0FL8Pk.js} +10 -10
  10. package/app/out/renderer/assets/{clone-Dv1e6zYr.js → clone-I16owcK3.js} +1 -1
  11. package/app/out/renderer/assets/{createText-1719965b-HBXHvWlI.js → createText-1719965b-D_GY3BGW.js} +2 -2
  12. package/app/out/renderer/assets/{edges-96097737-B6X5lcC0.js → edges-96097737-DKD2znJk.js} +3 -3
  13. package/app/out/renderer/assets/{erDiagram-0228fc6a-BmBmTBlH.js → erDiagram-0228fc6a-CxrCv4uP.js} +5 -5
  14. package/app/out/renderer/assets/{flowDb-c6c81e3f-CObz36ob.js → flowDb-c6c81e3f-8o4wFApi.js} +1 -1
  15. package/app/out/renderer/assets/{flowDiagram-50d868cf-C2hFHxwF.js → flowDiagram-50d868cf-B1eYKlZC.js} +12 -12
  16. package/app/out/renderer/assets/{flowDiagram-v2-4f6560a1-DEe8EygW.js → flowDiagram-v2-4f6560a1-Y4_T8Xi1.js} +12 -12
  17. package/app/out/renderer/assets/{flowchart-elk-definition-6af322e1-CgTtfYKk.js → flowchart-elk-definition-6af322e1-BjDPU2Ol.js} +6 -6
  18. package/app/out/renderer/assets/{ganttDiagram-a2739b55-C5Pq4zEy.js → ganttDiagram-a2739b55-CdQAT7up.js} +3 -3
  19. package/app/out/renderer/assets/{gitGraphDiagram-82fe8481-oLp0f8Ll.js → gitGraphDiagram-82fe8481-Bl7KP_G5.js} +2 -2
  20. package/app/out/renderer/assets/{graph-51iZ6wgR.js → graph-Bje_pjwC.js} +1 -1
  21. package/app/out/renderer/assets/{index-5325376f-yLvOW-Os.js → index-5325376f-DB5UfMSQ.js} +6 -6
  22. package/app/out/renderer/assets/{index-CmpSV9Ld.js → index-BHl_tELv.js} +5 -5
  23. package/app/out/renderer/assets/{index-DppxBL77.js → index-BWkpc_Fb.js} +3 -3
  24. package/app/out/renderer/assets/{index-BMsuFGn6.js → index-BX5-hWqk.js} +3 -3
  25. package/app/out/renderer/assets/{index-BSd80-j9.js → index-BbbK94Tl.js} +4 -4
  26. package/app/out/renderer/assets/{index-CAOQIqEc.js → index-BlSmCt8A.js} +6 -6
  27. package/app/out/renderer/assets/{index-ohN9yRWw.js → index-BnGN3XMZ.js} +6 -6
  28. package/app/out/renderer/assets/{index-_Z53hJps.js → index-CDWVrn8L.js} +3 -3
  29. package/app/out/renderer/assets/{index-Du-Z3sl4.js → index-CISiP4Vc.js} +1295 -487
  30. package/app/out/renderer/assets/{index-L4DJn7cw.css → index-CT1HtzVp.css} +157 -10
  31. package/app/out/renderer/assets/{index-shoMWskw.js → index-CTrj2aB2.js} +3 -3
  32. package/app/out/renderer/assets/{index-Cn2e13ja.js → index-CU1ei-Q-.js} +6 -6
  33. package/app/out/renderer/assets/{index-Bscx_5dF.js → index-CoIRGlb6.js} +3 -3
  34. package/app/out/renderer/assets/{index-32eUzqVW.js → index-D9YGyBA9.js} +3 -3
  35. package/app/out/renderer/assets/{index-B9a4DKM-.js → index-DCr-i28b.js} +3 -3
  36. package/app/out/renderer/assets/{index-BQA_Kvr6.js → index-DFmh36hv.js} +3 -3
  37. package/app/out/renderer/assets/{index-FGsCVYSr.js → index-DIB3jH2J.js} +1 -1
  38. package/app/out/renderer/assets/{index-CTmGCKqa.js → index-DRgsVAez.js} +4 -4
  39. package/app/out/renderer/assets/{index-BfWWn8B_.js → index-DihmTRDX.js} +6 -6
  40. package/app/out/renderer/assets/{index-UajPJYNV.js → index-DkTtiwdL.js} +3 -3
  41. package/app/out/renderer/assets/{index-D_Y7v6pE.js → index-K7KmUSPg.js} +3 -3
  42. package/app/out/renderer/assets/{index-y1Od1ed6.js → index-TtJcntKb.js} +3 -3
  43. package/app/out/renderer/assets/{index-_iFRQTkA.js → index-UYsogkLv.js} +6 -6
  44. package/app/out/renderer/assets/{index-AuZa-hTj.js → index-bPpSppnq.js} +3 -3
  45. package/app/out/renderer/assets/{index-DjqJjt6u.js → index-hI44wokU.js} +6 -6
  46. package/app/out/renderer/assets/{infoDiagram-8eee0895-Cm0Hm5ZX.js → infoDiagram-8eee0895-Dfty_4jM.js} +2 -2
  47. package/app/out/renderer/assets/{journeyDiagram-c64418c1-A2Gw9bVu.js → journeyDiagram-c64418c1-BzDYpsjx.js} +4 -4
  48. package/app/out/renderer/assets/{layout-C5N2nTfF.js → layout-DbHSNFr9.js} +2 -2
  49. package/app/out/renderer/assets/{line-Dn6BEQAK.js → line-jV7QLjZ_.js} +1 -1
  50. package/app/out/renderer/assets/{linear-8wk0rPUX.js → linear-CIeuNnVa.js} +1 -1
  51. package/app/out/renderer/assets/{mindmap-definition-8da855dc-BVy6ISnb.js → mindmap-definition-8da855dc-CeDEmTB-.js} +3 -3
  52. package/app/out/renderer/assets/{pieDiagram-a8764435-B9_axIHE.js → pieDiagram-a8764435-Bpm1FcX3.js} +3 -3
  53. package/app/out/renderer/assets/{quadrantDiagram-1e28029f-B1kmkDFg.js → quadrantDiagram-1e28029f-CEB7PxJa.js} +3 -3
  54. package/app/out/renderer/assets/{requirementDiagram-08caed73-C_bNWUtT.js → requirementDiagram-08caed73-CZJU8B_1.js} +5 -5
  55. package/app/out/renderer/assets/{sankeyDiagram-a04cb91d-CD2h1LiI.js → sankeyDiagram-a04cb91d-JXpwgeE0.js} +2 -2
  56. package/app/out/renderer/assets/{sequenceDiagram-c5b8d532-B6d6cuqi.js → sequenceDiagram-c5b8d532-CtvDQG5X.js} +3 -3
  57. package/app/out/renderer/assets/{stateDiagram-1ecb1508-CkuNj_3H.js → stateDiagram-1ecb1508-DQRUQm_b.js} +6 -6
  58. package/app/out/renderer/assets/{stateDiagram-v2-c2b004d7-CevZ3tno.js → stateDiagram-v2-c2b004d7-qWcRpqpV.js} +10 -10
  59. package/app/out/renderer/assets/{styles-b4e223ce-DAe5WQrg.js → styles-b4e223ce-6DWryYQB.js} +1 -1
  60. package/app/out/renderer/assets/{styles-ca3715f6-BDSX88bY.js → styles-ca3715f6-CZ9SeO17.js} +1 -1
  61. package/app/out/renderer/assets/{styles-d45a18b0-SE9h7les.js → styles-d45a18b0-Bkmie_Qv.js} +4 -4
  62. package/app/out/renderer/assets/{svgDrawCommon-b86b1483-D1mpNbDQ.js → svgDrawCommon-b86b1483-BWRX1ZPq.js} +1 -1
  63. package/app/out/renderer/assets/{timeline-definition-faaaa080-7Ha-nm4M.js → timeline-definition-faaaa080-XcgfjspK.js} +3 -3
  64. package/app/out/renderer/assets/{xychartDiagram-f5964ef8-DLy7iyZW.js → xychartDiagram-f5964ef8-C0CQzgr8.js} +5 -5
  65. package/app/out/renderer/index.html +2 -2
  66. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./MilkdownMarkdownEditor-D7GYpVZn.js","./MilkdownMarkdownEditor-tTNRIB2K.css"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./MilkdownMarkdownEditor-DjFLwyh4.js","./MilkdownMarkdownEditor-tTNRIB2K.css"])))=>i.map(i=>d[i]);
2
2
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
3
3
  function getDefaultExportFromCjs(x) {
4
4
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -12702,23 +12702,6 @@ const Database = createLucideIcon("Database", [
12702
12702
  ["path", { d: "M3 5V19A9 3 0 0 0 21 19V5", key: "1wlel7" }],
12703
12703
  ["path", { d: "M3 12A9 3 0 0 0 21 12", key: "mv7ke4" }]
12704
12704
  ]);
12705
- /**
12706
- * @license lucide-react v0.469.0 - ISC
12707
- *
12708
- * This source code is licensed under the ISC license.
12709
- * See the LICENSE file in the root directory of this source tree.
12710
- */
12711
- const Eraser = createLucideIcon("Eraser", [
12712
- [
12713
- "path",
12714
- {
12715
- d: "m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21",
12716
- key: "182aya"
12717
- }
12718
- ],
12719
- ["path", { d: "M22 21H7", key: "t4ddhn" }],
12720
- ["path", { d: "m5 11 9 9", key: "1mo9qw" }]
12721
- ]);
12722
12705
  /**
12723
12706
  * @license lucide-react v0.469.0 - ISC
12724
12707
  *
@@ -12915,6 +12898,17 @@ const Globe = createLucideIcon("Globe", [
12915
12898
  ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
12916
12899
  ["path", { d: "M2 12h20", key: "9i4pu4" }]
12917
12900
  ]);
12901
+ /**
12902
+ * @license lucide-react v0.469.0 - ISC
12903
+ *
12904
+ * This source code is licensed under the ISC license.
12905
+ * See the LICENSE file in the root directory of this source tree.
12906
+ */
12907
+ const Info$1 = createLucideIcon("Info", [
12908
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
12909
+ ["path", { d: "M12 16v-4", key: "1dtifu" }],
12910
+ ["path", { d: "M12 8h.01", key: "e9boi3" }]
12911
+ ]);
12918
12912
  /**
12919
12913
  * @license lucide-react v0.469.0 - ISC
12920
12914
  *
@@ -12952,6 +12946,28 @@ const Lightbulb = createLucideIcon("Lightbulb", [
12952
12946
  const LoaderCircle = createLucideIcon("LoaderCircle", [
12953
12947
  ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
12954
12948
  ]);
12949
+ /**
12950
+ * @license lucide-react v0.469.0 - ISC
12951
+ *
12952
+ * This source code is licensed under the ISC license.
12953
+ * See the LICENSE file in the root directory of this source tree.
12954
+ */
12955
+ const LogIn = createLucideIcon("LogIn", [
12956
+ ["path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4", key: "u53s6r" }],
12957
+ ["polyline", { points: "10 17 15 12 10 7", key: "1ail0h" }],
12958
+ ["line", { x1: "15", x2: "3", y1: "12", y2: "12", key: "v6grx8" }]
12959
+ ]);
12960
+ /**
12961
+ * @license lucide-react v0.469.0 - ISC
12962
+ *
12963
+ * This source code is licensed under the ISC license.
12964
+ * See the LICENSE file in the root directory of this source tree.
12965
+ */
12966
+ const LogOut = createLucideIcon("LogOut", [
12967
+ ["path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4", key: "1uf3rs" }],
12968
+ ["polyline", { points: "16 17 21 12 16 7", key: "1gabdz" }],
12969
+ ["line", { x1: "21", x2: "9", y1: "12", y2: "12", key: "1uyos4" }]
12970
+ ]);
12955
12971
  /**
12956
12972
  * @license lucide-react v0.469.0 - ISC
12957
12973
  *
@@ -12961,6 +12977,17 @@ const LoaderCircle = createLucideIcon("LoaderCircle", [
12961
12977
  const MessageSquare = createLucideIcon("MessageSquare", [
12962
12978
  ["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
12963
12979
  ]);
12980
+ /**
12981
+ * @license lucide-react v0.469.0 - ISC
12982
+ *
12983
+ * This source code is licensed under the ISC license.
12984
+ * See the LICENSE file in the root directory of this source tree.
12985
+ */
12986
+ const Monitor = createLucideIcon("Monitor", [
12987
+ ["rect", { width: "20", height: "14", x: "2", y: "3", rx: "2", key: "48i651" }],
12988
+ ["line", { x1: "8", x2: "16", y1: "21", y2: "21", key: "1svkeh" }],
12989
+ ["line", { x1: "12", x2: "12", y1: "17", y2: "21", key: "vw1qmm" }]
12990
+ ]);
12964
12991
  /**
12965
12992
  * @license lucide-react v0.469.0 - ISC
12966
12993
  *
@@ -13254,7 +13281,7 @@ const Zap = createLucideIcon("Zap", [
13254
13281
  }
13255
13282
  ]
13256
13283
  ]);
13257
- const api$a = window.api;
13284
+ const api$b = window.api;
13258
13285
  const KEY_FIELDS = [
13259
13286
  {
13260
13287
  name: "ANTHROPIC_API_KEY",
@@ -13295,21 +13322,40 @@ function ApiKeySetup({ onComplete }) {
13295
13322
  const [visible, setVisible] = reactExports.useState({});
13296
13323
  const [saving, setSaving] = reactExports.useState(false);
13297
13324
  const [error, setError] = reactExports.useState(null);
13325
+ const [codexStatus, setCodexStatus] = reactExports.useState(null);
13326
+ const [codexLoggingIn, setCodexLoggingIn] = reactExports.useState(false);
13298
13327
  reactExports.useEffect(() => {
13299
- api$a.getApiKeyStatus().then((s15) => setStatus(s15));
13328
+ api$b.getApiKeyStatus().then((s15) => setStatus(s15));
13329
+ api$b.getOpenAICodexStatus?.().then((s15) => setCodexStatus(s15)).catch(() => {
13330
+ });
13300
13331
  }, []);
13301
- const hasAnyLlmKey = status.ANTHROPIC_API_KEY || status.OPENAI_API_KEY || !!(values.ANTHROPIC_API_KEY || "").trim() || !!(values.OPENAI_API_KEY || "").trim();
13332
+ const handleCodexLogin = async () => {
13333
+ setCodexLoggingIn(true);
13334
+ try {
13335
+ const result = await api$b.openaiCodexLogin?.();
13336
+ if (result?.success) {
13337
+ setCodexStatus({ isLoggedIn: true });
13338
+ } else {
13339
+ setError(result?.error || "ChatGPT sign-in failed");
13340
+ }
13341
+ } catch (err) {
13342
+ setError(err.message || "ChatGPT sign-in failed");
13343
+ } finally {
13344
+ setCodexLoggingIn(false);
13345
+ }
13346
+ };
13347
+ const hasAnyLlmKey = status.ANTHROPIC_API_KEY || status.OPENAI_API_KEY || codexStatus?.isLoggedIn || !!(values.ANTHROPIC_API_KEY || "").trim() || !!(values.OPENAI_API_KEY || "").trim();
13302
13348
  const handleSave = async () => {
13303
13349
  const entries = Object.entries(values).filter(([, v3]) => v3.trim());
13304
- if (entries.length === 0 && !status.ANTHROPIC_API_KEY && !status.OPENAI_API_KEY) {
13305
- setError("Please enter at least one API key (Anthropic or OpenAI) to continue.");
13350
+ if (entries.length === 0 && !status.ANTHROPIC_API_KEY && !status.OPENAI_API_KEY && !codexStatus?.isLoggedIn) {
13351
+ setError("Please enter at least one API key or sign in with ChatGPT to continue.");
13306
13352
  return;
13307
13353
  }
13308
13354
  setSaving(true);
13309
13355
  setError(null);
13310
13356
  try {
13311
13357
  for (const [key, val] of entries) {
13312
- await api$a.saveApiKey(key, val);
13358
+ await api$b.saveApiKey(key, val);
13313
13359
  }
13314
13360
  onComplete();
13315
13361
  } catch (err) {
@@ -13325,11 +13371,7 @@ function ApiKeySetup({ onComplete }) {
13325
13371
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative mx-auto mb-6 w-fit", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
13326
13372
  "div",
13327
13373
  {
13328
- className: "w-14 h-14 rounded-2xl flex items-center justify-center",
13329
- style: {
13330
- background: "linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-2) 100%)",
13331
- boxShadow: "0 8px 32px var(--color-accent-2-muted)"
13332
- },
13374
+ className: "w-14 h-14 rounded-2xl flex items-center justify-center t-gradient-accent t-gradient-accent-shadow-lg",
13333
13375
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { className: "text-white", size: 22 })
13334
13376
  }
13335
13377
  ) }),
@@ -13380,7 +13422,8 @@ function ApiKeySetup({ onComplete }) {
13380
13422
  className: "w-full text-xs px-2.5 py-1.5 rounded-md border t-border t-bg-base t-text font-mono pr-8\n focus:outline-none focus:ring-1 focus:ring-[var(--color-accent)]",
13381
13423
  placeholder: alreadySet ? "•••••••• (already set — leave blank to keep)" : field.placeholder,
13382
13424
  value: values[field.name] || "",
13383
- onChange: (e) => setValues((prev) => ({ ...prev, [field.name]: e.target.value }))
13425
+ onChange: (e) => setValues((prev) => ({ ...prev, [field.name]: e.target.value })),
13426
+ "aria-label": `${field.label} API key`
13384
13427
  }
13385
13428
  ),
13386
13429
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -13397,10 +13440,33 @@ function ApiKeySetup({ onComplete }) {
13397
13440
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] t-text-muted mt-1", children: field.hint })
13398
13441
  ] }, field.name);
13399
13442
  }) }),
13400
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] t-text-muted mb-1", children: "* You need at least one of Anthropic or OpenAI. Both is fine too." }),
13401
- error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400 mb-3", children: error }),
13443
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border t-border t-bg-surface/50 p-3", children: [
13444
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between mb-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-xs font-medium t-text flex items-center gap-1.5", children: [
13445
+ "ChatGPT Subscription",
13446
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted", children: "(alternative to OpenAI API key)" }),
13447
+ codexStatus?.isLoggedIn && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-0.5 text-[10px] text-green-500", children: [
13448
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 10 }),
13449
+ " signed in"
13450
+ ] })
13451
+ ] }) }),
13452
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
13453
+ "button",
13454
+ {
13455
+ onClick: handleCodexLogin,
13456
+ disabled: codexLoggingIn || codexStatus?.isLoggedIn,
13457
+ className: "flex items-center gap-1.5 px-3 py-1.5 rounded-md border t-border text-xs t-text-secondary hover:t-text t-bg-hover disabled:opacity-50",
13458
+ children: [
13459
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LogIn, { size: 12 }),
13460
+ codexLoggingIn ? "Signing in..." : codexStatus?.isLoggedIn ? "Already signed in" : "Sign in with ChatGPT"
13461
+ ]
13462
+ }
13463
+ ),
13464
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] t-text-muted mt-1", children: "Use your ChatGPT Plus/Pro subscription instead of an API key. No per-token billing." })
13465
+ ] }),
13466
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] t-text-muted mb-1", children: "* You need at least one of Anthropic or OpenAI (API key or ChatGPT subscription)." }),
13467
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400 mb-3", role: "alert", children: error }),
13402
13468
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
13403
- (status.ANTHROPIC_API_KEY || status.OPENAI_API_KEY) && /* @__PURE__ */ jsxRuntimeExports.jsx(
13469
+ (status.ANTHROPIC_API_KEY || status.OPENAI_API_KEY || codexStatus?.isLoggedIn) && /* @__PURE__ */ jsxRuntimeExports.jsx(
13404
13470
  "button",
13405
13471
  {
13406
13472
  onClick: onComplete,
@@ -13414,11 +13480,7 @@ function ApiKeySetup({ onComplete }) {
13414
13480
  {
13415
13481
  onClick: handleSave,
13416
13482
  disabled: saving,
13417
- className: "px-5 py-2 rounded-lg text-white text-sm font-medium hover:opacity-90 transition-all duration-200 disabled:opacity-50",
13418
- style: {
13419
- background: "linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-2) 100%)",
13420
- boxShadow: "0 4px 16px var(--color-accent-2-muted)"
13421
- },
13483
+ className: "px-5 py-2 rounded-lg text-white text-sm font-medium hover:opacity-90 transition-all duration-200 disabled:opacity-50 t-gradient-accent t-gradient-accent-shadow",
13422
13484
  children: saving ? "Saving..." : hasAnyLlmKey ? "Save & Continue" : "Save & Continue"
13423
13485
  }
13424
13486
  )
@@ -13465,11 +13527,44 @@ const createImpl = (createState2) => {
13465
13527
  return useBoundStore;
13466
13528
  };
13467
13529
  const create$1 = (createState2) => createState2 ? createImpl(createState2) : createImpl;
13530
+ const REASONING_MODELS = [
13531
+ "openai:gpt-5.4",
13532
+ "openai:gpt-5.4-mini",
13533
+ "openai:gpt-5.4-nano",
13534
+ "openai:gpt-5.4-pro",
13535
+ "openai-codex:gpt-5.4",
13536
+ "openai-codex:gpt-5.4-mini",
13537
+ "anthropic:claude-opus-4-6"
13538
+ ];
13539
+ const SUPPORTED_MODELS = [
13540
+ // OpenAI (API key)
13541
+ { id: "openai:gpt-5.4", label: "GPT-5.4", provider: "OpenAI" },
13542
+ { id: "openai:gpt-5.4-mini", label: "GPT-5.4 Mini", provider: "OpenAI" },
13543
+ { id: "openai:gpt-5.4-nano", label: "GPT-5.4 Nano", provider: "OpenAI" },
13544
+ { id: "openai:gpt-4o", label: "GPT-4o", provider: "OpenAI" },
13545
+ { id: "openai:gpt-5.4-pro", label: "GPT-5.4 Pro", provider: "OpenAI" },
13546
+ // ChatGPT Subscription (OAuth) — only models registered in pi-ai's openai-codex provider
13547
+ { id: "openai-codex:gpt-5.4", label: "GPT-5.4", provider: "ChatGPT Subscription" },
13548
+ { id: "openai-codex:gpt-5.4-mini", label: "GPT-5.4 Mini", provider: "ChatGPT Subscription" },
13549
+ // Anthropic (API key)
13550
+ { id: "anthropic:claude-opus-4-6", label: "Claude Opus 4.6", provider: "Anthropic" },
13551
+ { id: "anthropic:claude-opus-4-5-20251101", label: "Claude Opus 4.5", provider: "Anthropic" },
13552
+ { id: "anthropic:claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", provider: "Anthropic" },
13553
+ { id: "anthropic:claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", provider: "Anthropic" }
13554
+ ];
13555
+ const DEFAULT_MODEL = "openai:gpt-5.4";
13556
+ function parseModelKey(key) {
13557
+ const i = key.indexOf(":");
13558
+ if (i > 0) return { provider: key.slice(0, i), modelId: key.slice(i + 1) };
13559
+ if (key.startsWith("claude-")) return { provider: "anthropic", modelId: key };
13560
+ if (key.startsWith("gemini-")) return { provider: "google", modelId: key };
13561
+ return { provider: "openai", modelId: key };
13562
+ }
13468
13563
  const useUIStore = create$1((set) => ({
13469
13564
  theme: "light",
13470
13565
  leftTab: "files",
13471
13566
  centerView: "chat",
13472
- selectedModel: "gpt-5.4",
13567
+ selectedModel: DEFAULT_MODEL,
13473
13568
  isIdle: true,
13474
13569
  rightSidebarCollapsed: false,
13475
13570
  leftSidebarCollapsed: false,
@@ -13575,16 +13670,321 @@ async function hydratePreferences() {
13575
13670
  const prefs = await api2?.loadPreferences?.();
13576
13671
  if (!prefs) return;
13577
13672
  const updates = {};
13578
- if (prefs.selectedModel) updates.selectedModel = prefs.selectedModel;
13673
+ if (prefs.selectedModel) {
13674
+ const m = prefs.selectedModel;
13675
+ if (!m.includes(":")) {
13676
+ const { provider, modelId } = parseModelKey(m);
13677
+ updates.selectedModel = `${provider}:${modelId}`;
13678
+ } else {
13679
+ updates.selectedModel = m;
13680
+ }
13681
+ }
13579
13682
  if (prefs.reasoningEffort) updates.reasoningEffort = prefs.reasoningEffort;
13580
13683
  if (prefs.theme) updates.theme = prefs.theme;
13581
13684
  if (Object.keys(updates).length > 0) useUIStore.setState(updates);
13582
13685
  }
13583
13686
  const uiStore = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
13584
13687
  __proto__: null,
13688
+ REASONING_MODELS,
13689
+ SUPPORTED_MODELS,
13585
13690
  hydratePreferences,
13586
13691
  useUIStore
13587
13692
  }, Symbol.toStringTag, { value: "Module" }));
13693
+ const scriptRel = function detectScriptRel() {
13694
+ const relList = typeof document !== "undefined" && document.createElement("link").relList;
13695
+ return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
13696
+ }();
13697
+ const assetsURL = function(dep, importerUrl) {
13698
+ return new URL(dep, importerUrl).href;
13699
+ };
13700
+ const seen = {};
13701
+ const __vitePreload = function preload2(baseModule, deps, importerUrl) {
13702
+ let promise = Promise.resolve();
13703
+ if (deps && deps.length > 0) {
13704
+ const links = document.getElementsByTagName("link");
13705
+ const cspNonceMeta = document.querySelector(
13706
+ "meta[property=csp-nonce]"
13707
+ );
13708
+ const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
13709
+ promise = Promise.allSettled(
13710
+ deps.map((dep) => {
13711
+ dep = assetsURL(dep, importerUrl);
13712
+ if (dep in seen) return;
13713
+ seen[dep] = true;
13714
+ const isCss = dep.endsWith(".css");
13715
+ const cssSelector = isCss ? '[rel="stylesheet"]' : "";
13716
+ const isBaseRelative = !!importerUrl;
13717
+ if (isBaseRelative) {
13718
+ for (let i = links.length - 1; i >= 0; i--) {
13719
+ const link22 = links[i];
13720
+ if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
13721
+ return;
13722
+ }
13723
+ }
13724
+ } else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
13725
+ return;
13726
+ }
13727
+ const link2 = document.createElement("link");
13728
+ link2.rel = isCss ? "stylesheet" : scriptRel;
13729
+ if (!isCss) {
13730
+ link2.as = "script";
13731
+ }
13732
+ link2.crossOrigin = "";
13733
+ link2.href = dep;
13734
+ if (cspNonce) {
13735
+ link2.setAttribute("nonce", cspNonce);
13736
+ }
13737
+ document.head.appendChild(link2);
13738
+ if (isCss) {
13739
+ return new Promise((res, rej) => {
13740
+ link2.addEventListener("load", res);
13741
+ link2.addEventListener(
13742
+ "error",
13743
+ () => rej(new Error(`Unable to preload CSS for ${dep}`))
13744
+ );
13745
+ });
13746
+ }
13747
+ })
13748
+ );
13749
+ }
13750
+ function handlePreloadError(err) {
13751
+ const e = new Event("vite:preloadError", {
13752
+ cancelable: true
13753
+ });
13754
+ e.payload = err;
13755
+ window.dispatchEvent(e);
13756
+ if (!e.defaultPrevented) {
13757
+ throw err;
13758
+ }
13759
+ }
13760
+ return promise.then((res) => {
13761
+ for (const item of res || []) {
13762
+ if (item.status !== "rejected") continue;
13763
+ handlePreloadError(item.reason);
13764
+ }
13765
+ return baseModule().catch(handlePreloadError);
13766
+ });
13767
+ };
13768
+ function extractProgress(tool, data) {
13769
+ if (!data?.partialResult) return void 0;
13770
+ const text2 = data.partialResult?.content?.[0]?.text;
13771
+ if (typeof text2 === "string" && text2.length > 0) {
13772
+ const lines = text2.split("\n").filter(Boolean);
13773
+ const maxLines = tool === "bash" ? 5 : 3;
13774
+ return lines.slice(-maxLines).join("\n");
13775
+ }
13776
+ return void 0;
13777
+ }
13778
+ const useToolEventsStore = create$1((set, get) => ({
13779
+ currentRunEvents: [],
13780
+ onToolCall: (event) => {
13781
+ const toolCallId = event.toolCallId || `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
13782
+ const newEvent = {
13783
+ id: toolCallId,
13784
+ toolCallId,
13785
+ tool: event.tool,
13786
+ status: "running",
13787
+ summary: event.summary,
13788
+ detail: event.detail,
13789
+ startedAt: Date.now()
13790
+ };
13791
+ set((state) => ({
13792
+ currentRunEvents: [...state.currentRunEvents, newEvent]
13793
+ }));
13794
+ },
13795
+ onToolResult: (event) => {
13796
+ set((state) => {
13797
+ const events = [...state.currentRunEvents];
13798
+ const idx = event.toolCallId ? events.findLastIndex((e) => e.toolCallId === event.toolCallId && e.status === "running") : events.findLastIndex((e) => e.tool === event.tool && e.status === "running");
13799
+ if (idx !== -1) {
13800
+ events[idx] = {
13801
+ ...events[idx],
13802
+ status: event.success !== false ? "success" : "error",
13803
+ resultSummary: event.summary,
13804
+ resultDetail: event.resultDetail,
13805
+ durationMs: event.durationMs,
13806
+ completedAt: Date.now()
13807
+ };
13808
+ }
13809
+ return { currentRunEvents: events };
13810
+ });
13811
+ },
13812
+ onToolProgress: (event) => {
13813
+ if (event.phase === "end") return;
13814
+ set((state) => {
13815
+ const events = [...state.currentRunEvents];
13816
+ const idx = events.findLastIndex((e) => e.toolCallId === event.toolCallId);
13817
+ if (idx !== -1) {
13818
+ const progress = extractProgress(event.tool, event.data) ?? events[idx].progress;
13819
+ events[idx] = { ...events[idx], progress };
13820
+ }
13821
+ return { currentRunEvents: events };
13822
+ });
13823
+ },
13824
+ snapshot: () => [...get().currentRunEvents],
13825
+ clearRun: () => set({ currentRunEvents: [] })
13826
+ }));
13827
+ const PAGE_SIZE = 20;
13828
+ const api$a = window.api;
13829
+ let _sessionId = "";
13830
+ const useChatStore = create$1((set, get) => ({
13831
+ messages: [],
13832
+ streamingText: "",
13833
+ isStreaming: false,
13834
+ savedMessageIds: /* @__PURE__ */ new Set(),
13835
+ turnToolEvents: /* @__PURE__ */ new Map(),
13836
+ draftText: "",
13837
+ setDraftText: (text2) => set({ draftText: text2 }),
13838
+ hasMore: false,
13839
+ isLoadingHistory: false,
13840
+ _offset: 0,
13841
+ scrollToMessageId: null,
13842
+ send: async (text2, images) => {
13843
+ const userMsg = {
13844
+ id: crypto.randomUUID(),
13845
+ role: "user",
13846
+ content: text2,
13847
+ images: images?.map((i) => `data:${i.mimeType};base64,${i.base64}`),
13848
+ timestamp: Date.now()
13849
+ };
13850
+ set((s15) => ({
13851
+ messages: [...s15.messages, userMsg],
13852
+ streamingText: "",
13853
+ isStreaming: true
13854
+ }));
13855
+ if (_sessionId) {
13856
+ api$a.saveMessage(_sessionId, userMsg).catch(() => {
13857
+ });
13858
+ }
13859
+ const { useUsageStore: useUsageStore2 } = await __vitePreload(async () => {
13860
+ const { useUsageStore: useUsageStore3 } = await Promise.resolve().then(() => usageStore);
13861
+ return { useUsageStore: useUsageStore3 };
13862
+ }, true ? void 0 : void 0, import.meta.url);
13863
+ useUsageStore2.getState().resetRun();
13864
+ try {
13865
+ const { useUIStore: useUIStore2 } = await __vitePreload(async () => {
13866
+ const { useUIStore: useUIStore3 } = await Promise.resolve().then(() => uiStore);
13867
+ return { useUIStore: useUIStore3 };
13868
+ }, true ? void 0 : void 0, import.meta.url);
13869
+ const model = useUIStore2.getState().selectedModel;
13870
+ await api$a.sendMessage(text2, void 0, model, images);
13871
+ } catch {
13872
+ }
13873
+ },
13874
+ stop: async () => {
13875
+ await api$a.stopAgent();
13876
+ },
13877
+ appendChunk: (chunk) => {
13878
+ set((s15) => ({ streamingText: s15.streamingText + chunk }));
13879
+ },
13880
+ finalize: (result) => {
13881
+ const content2 = result.response || result.error || "No response";
13882
+ const assistantMsg = {
13883
+ id: crypto.randomUUID(),
13884
+ role: "assistant",
13885
+ content: content2,
13886
+ images: result.images?.map((i) => `data:${i.mimeType};base64,${i.base64}`),
13887
+ timestamp: Date.now()
13888
+ };
13889
+ const toolEventsSnapshot = useToolEventsStore.getState().snapshot();
13890
+ useToolEventsStore.getState().clearRun();
13891
+ set((s15) => {
13892
+ const nextToolEvents = new Map(s15.turnToolEvents);
13893
+ if (toolEventsSnapshot.length > 0) {
13894
+ nextToolEvents.set(assistantMsg.id, toolEventsSnapshot);
13895
+ }
13896
+ return {
13897
+ messages: [...s15.messages, assistantMsg],
13898
+ streamingText: "",
13899
+ isStreaming: false,
13900
+ turnToolEvents: nextToolEvents
13901
+ };
13902
+ });
13903
+ if (_sessionId) {
13904
+ api$a.saveMessage(_sessionId, assistantMsg).catch(() => {
13905
+ });
13906
+ }
13907
+ },
13908
+ clear: () => {
13909
+ _sessionId = "";
13910
+ return set({
13911
+ messages: [],
13912
+ streamingText: "",
13913
+ isStreaming: false,
13914
+ savedMessageIds: /* @__PURE__ */ new Set(),
13915
+ turnToolEvents: /* @__PURE__ */ new Map(),
13916
+ draftText: "",
13917
+ hasMore: false,
13918
+ isLoadingHistory: false,
13919
+ _offset: 0,
13920
+ scrollToMessageId: null
13921
+ });
13922
+ },
13923
+ markSaved: (messageId) => {
13924
+ set((s15) => {
13925
+ const next = new Set(s15.savedMessageIds);
13926
+ next.add(messageId);
13927
+ return { savedMessageIds: next };
13928
+ });
13929
+ if (_sessionId) {
13930
+ api$a.markMessageSaved(_sessionId, messageId).catch(() => {
13931
+ });
13932
+ }
13933
+ },
13934
+ insertContextReset: () => {
13935
+ const divider = {
13936
+ id: `ctx-reset-${Date.now()}`,
13937
+ role: "system",
13938
+ content: "AI context has been reset — chat history is preserved.",
13939
+ timestamp: Date.now()
13940
+ };
13941
+ set((s15) => ({ messages: [...s15.messages, divider] }));
13942
+ },
13943
+ loadInitial: async (sessionId) => {
13944
+ _sessionId = sessionId;
13945
+ try {
13946
+ const [count, messages, savedIds] = await Promise.all([
13947
+ api$a.getMessageCount(sessionId),
13948
+ api$a.loadMessages(sessionId, 0, PAGE_SIZE),
13949
+ api$a.loadSavedMessageIds(sessionId)
13950
+ ]);
13951
+ set({
13952
+ messages,
13953
+ hasMore: count > PAGE_SIZE,
13954
+ _offset: PAGE_SIZE,
13955
+ savedMessageIds: new Set(savedIds || [])
13956
+ });
13957
+ } catch {
13958
+ }
13959
+ },
13960
+ loadHistory: async () => {
13961
+ const { hasMore, isLoadingHistory, _offset } = get();
13962
+ if (!hasMore || isLoadingHistory || !_sessionId) return;
13963
+ set({ isLoadingHistory: true });
13964
+ try {
13965
+ const [count, older] = await Promise.all([
13966
+ api$a.getMessageCount(_sessionId),
13967
+ api$a.loadMessages(_sessionId, _offset, PAGE_SIZE)
13968
+ ]);
13969
+ if (older.length > 0) {
13970
+ set((s15) => ({
13971
+ messages: [...older, ...s15.messages],
13972
+ _offset: s15._offset + older.length,
13973
+ hasMore: s15._offset + older.length < count,
13974
+ isLoadingHistory: false
13975
+ }));
13976
+ } else {
13977
+ set({ hasMore: false, isLoadingHistory: false });
13978
+ }
13979
+ } catch {
13980
+ set({ isLoadingHistory: false });
13981
+ }
13982
+ },
13983
+ requestScrollTo: (messageId) => {
13984
+ set({ scrollToMessageId: messageId });
13985
+ setTimeout(() => set({ scrollToMessageId: null }), 100);
13986
+ }
13987
+ }));
13588
13988
  function ok$1() {
13589
13989
  }
13590
13990
  function unreachable() {
@@ -26187,292 +26587,6 @@ const useEntityStore = create$1((set, get) => ({
26187
26587
  await get().refreshAll();
26188
26588
  }
26189
26589
  }));
26190
- const scriptRel = function detectScriptRel() {
26191
- const relList = typeof document !== "undefined" && document.createElement("link").relList;
26192
- return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
26193
- }();
26194
- const assetsURL = function(dep, importerUrl) {
26195
- return new URL(dep, importerUrl).href;
26196
- };
26197
- const seen = {};
26198
- const __vitePreload = function preload2(baseModule, deps, importerUrl) {
26199
- let promise = Promise.resolve();
26200
- if (deps && deps.length > 0) {
26201
- const links = document.getElementsByTagName("link");
26202
- const cspNonceMeta = document.querySelector(
26203
- "meta[property=csp-nonce]"
26204
- );
26205
- const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
26206
- promise = Promise.allSettled(
26207
- deps.map((dep) => {
26208
- dep = assetsURL(dep, importerUrl);
26209
- if (dep in seen) return;
26210
- seen[dep] = true;
26211
- const isCss = dep.endsWith(".css");
26212
- const cssSelector = isCss ? '[rel="stylesheet"]' : "";
26213
- const isBaseRelative = !!importerUrl;
26214
- if (isBaseRelative) {
26215
- for (let i = links.length - 1; i >= 0; i--) {
26216
- const link22 = links[i];
26217
- if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
26218
- return;
26219
- }
26220
- }
26221
- } else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
26222
- return;
26223
- }
26224
- const link2 = document.createElement("link");
26225
- link2.rel = isCss ? "stylesheet" : scriptRel;
26226
- if (!isCss) {
26227
- link2.as = "script";
26228
- }
26229
- link2.crossOrigin = "";
26230
- link2.href = dep;
26231
- if (cspNonce) {
26232
- link2.setAttribute("nonce", cspNonce);
26233
- }
26234
- document.head.appendChild(link2);
26235
- if (isCss) {
26236
- return new Promise((res, rej) => {
26237
- link2.addEventListener("load", res);
26238
- link2.addEventListener(
26239
- "error",
26240
- () => rej(new Error(`Unable to preload CSS for ${dep}`))
26241
- );
26242
- });
26243
- }
26244
- })
26245
- );
26246
- }
26247
- function handlePreloadError(err) {
26248
- const e = new Event("vite:preloadError", {
26249
- cancelable: true
26250
- });
26251
- e.payload = err;
26252
- window.dispatchEvent(e);
26253
- if (!e.defaultPrevented) {
26254
- throw err;
26255
- }
26256
- }
26257
- return promise.then((res) => {
26258
- for (const item of res || []) {
26259
- if (item.status !== "rejected") continue;
26260
- handlePreloadError(item.reason);
26261
- }
26262
- return baseModule().catch(handlePreloadError);
26263
- });
26264
- };
26265
- function extractProgress(tool, data) {
26266
- if (!data?.partialResult) return void 0;
26267
- const text2 = data.partialResult?.content?.[0]?.text;
26268
- if (typeof text2 === "string" && text2.length > 0) {
26269
- const lines = text2.split("\n").filter(Boolean);
26270
- const maxLines = tool === "bash" ? 5 : 3;
26271
- return lines.slice(-maxLines).join("\n");
26272
- }
26273
- return void 0;
26274
- }
26275
- const useToolEventsStore = create$1((set, get) => ({
26276
- currentRunEvents: [],
26277
- onToolCall: (event) => {
26278
- const toolCallId = event.toolCallId || `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
26279
- const newEvent = {
26280
- id: toolCallId,
26281
- toolCallId,
26282
- tool: event.tool,
26283
- status: "running",
26284
- summary: event.summary,
26285
- detail: event.detail,
26286
- startedAt: Date.now()
26287
- };
26288
- set((state) => ({
26289
- currentRunEvents: [...state.currentRunEvents, newEvent]
26290
- }));
26291
- },
26292
- onToolResult: (event) => {
26293
- set((state) => {
26294
- const events = [...state.currentRunEvents];
26295
- const idx = event.toolCallId ? events.findLastIndex((e) => e.toolCallId === event.toolCallId && e.status === "running") : events.findLastIndex((e) => e.tool === event.tool && e.status === "running");
26296
- if (idx !== -1) {
26297
- events[idx] = {
26298
- ...events[idx],
26299
- status: event.success !== false ? "success" : "error",
26300
- resultSummary: event.summary,
26301
- resultDetail: event.resultDetail,
26302
- durationMs: event.durationMs,
26303
- completedAt: Date.now()
26304
- };
26305
- }
26306
- return { currentRunEvents: events };
26307
- });
26308
- },
26309
- onToolProgress: (event) => {
26310
- if (event.phase === "end") return;
26311
- set((state) => {
26312
- const events = [...state.currentRunEvents];
26313
- const idx = events.findLastIndex((e) => e.toolCallId === event.toolCallId);
26314
- if (idx !== -1) {
26315
- const progress = extractProgress(event.tool, event.data) ?? events[idx].progress;
26316
- events[idx] = { ...events[idx], progress };
26317
- }
26318
- return { currentRunEvents: events };
26319
- });
26320
- },
26321
- snapshot: () => [...get().currentRunEvents],
26322
- clearRun: () => set({ currentRunEvents: [] })
26323
- }));
26324
- const PAGE_SIZE = 20;
26325
- const api$8 = window.api;
26326
- let _sessionId = "";
26327
- const useChatStore = create$1((set, get) => ({
26328
- messages: [],
26329
- streamingText: "",
26330
- isStreaming: false,
26331
- savedMessageIds: /* @__PURE__ */ new Set(),
26332
- turnToolEvents: /* @__PURE__ */ new Map(),
26333
- draftText: "",
26334
- setDraftText: (text2) => set({ draftText: text2 }),
26335
- hasMore: false,
26336
- isLoadingHistory: false,
26337
- _offset: 0,
26338
- scrollToMessageId: null,
26339
- send: async (text2, images) => {
26340
- const userMsg = {
26341
- id: crypto.randomUUID(),
26342
- role: "user",
26343
- content: text2,
26344
- images: images?.map((i) => `data:${i.mimeType};base64,${i.base64}`),
26345
- timestamp: Date.now()
26346
- };
26347
- set((s15) => ({
26348
- messages: [...s15.messages, userMsg],
26349
- streamingText: "",
26350
- isStreaming: true
26351
- }));
26352
- if (_sessionId) {
26353
- api$8.saveMessage(_sessionId, userMsg).catch(() => {
26354
- });
26355
- }
26356
- const { useUsageStore: useUsageStore2 } = await __vitePreload(async () => {
26357
- const { useUsageStore: useUsageStore3 } = await Promise.resolve().then(() => usageStore);
26358
- return { useUsageStore: useUsageStore3 };
26359
- }, true ? void 0 : void 0, import.meta.url);
26360
- useUsageStore2.getState().resetRun();
26361
- try {
26362
- const { useUIStore: useUIStore2 } = await __vitePreload(async () => {
26363
- const { useUIStore: useUIStore3 } = await Promise.resolve().then(() => uiStore);
26364
- return { useUIStore: useUIStore3 };
26365
- }, true ? void 0 : void 0, import.meta.url);
26366
- const model = useUIStore2.getState().selectedModel;
26367
- await api$8.sendMessage(text2, void 0, model, images);
26368
- } catch {
26369
- }
26370
- },
26371
- stop: async () => {
26372
- await api$8.stopAgent();
26373
- },
26374
- appendChunk: (chunk) => {
26375
- set((s15) => ({ streamingText: s15.streamingText + chunk }));
26376
- },
26377
- finalize: (result) => {
26378
- const content2 = result.response || result.error || "No response";
26379
- const assistantMsg = {
26380
- id: crypto.randomUUID(),
26381
- role: "assistant",
26382
- content: content2,
26383
- images: result.images?.map((i) => `data:${i.mimeType};base64,${i.base64}`),
26384
- timestamp: Date.now()
26385
- };
26386
- const toolEventsSnapshot = useToolEventsStore.getState().snapshot();
26387
- useToolEventsStore.getState().clearRun();
26388
- set((s15) => {
26389
- const nextToolEvents = new Map(s15.turnToolEvents);
26390
- if (toolEventsSnapshot.length > 0) {
26391
- nextToolEvents.set(assistantMsg.id, toolEventsSnapshot);
26392
- }
26393
- return {
26394
- messages: [...s15.messages, assistantMsg],
26395
- streamingText: "",
26396
- isStreaming: false,
26397
- turnToolEvents: nextToolEvents
26398
- };
26399
- });
26400
- if (_sessionId) {
26401
- api$8.saveMessage(_sessionId, assistantMsg).catch(() => {
26402
- });
26403
- }
26404
- },
26405
- clear: () => {
26406
- _sessionId = "";
26407
- return set({
26408
- messages: [],
26409
- streamingText: "",
26410
- isStreaming: false,
26411
- savedMessageIds: /* @__PURE__ */ new Set(),
26412
- turnToolEvents: /* @__PURE__ */ new Map(),
26413
- draftText: "",
26414
- hasMore: false,
26415
- isLoadingHistory: false,
26416
- _offset: 0,
26417
- scrollToMessageId: null
26418
- });
26419
- },
26420
- markSaved: (messageId) => {
26421
- set((s15) => {
26422
- const next = new Set(s15.savedMessageIds);
26423
- next.add(messageId);
26424
- return { savedMessageIds: next };
26425
- });
26426
- if (_sessionId) {
26427
- api$8.markMessageSaved(_sessionId, messageId).catch(() => {
26428
- });
26429
- }
26430
- },
26431
- loadInitial: async (sessionId) => {
26432
- _sessionId = sessionId;
26433
- try {
26434
- const [count, messages, savedIds] = await Promise.all([
26435
- api$8.getMessageCount(sessionId),
26436
- api$8.loadMessages(sessionId, 0, PAGE_SIZE),
26437
- api$8.loadSavedMessageIds(sessionId)
26438
- ]);
26439
- set({
26440
- messages,
26441
- hasMore: count > PAGE_SIZE,
26442
- _offset: PAGE_SIZE,
26443
- savedMessageIds: new Set(savedIds || [])
26444
- });
26445
- } catch {
26446
- }
26447
- },
26448
- loadHistory: async () => {
26449
- const { hasMore, isLoadingHistory, _offset } = get();
26450
- if (!hasMore || isLoadingHistory || !_sessionId) return;
26451
- set({ isLoadingHistory: true });
26452
- try {
26453
- const [count, older] = await Promise.all([
26454
- api$8.getMessageCount(_sessionId),
26455
- api$8.loadMessages(_sessionId, _offset, PAGE_SIZE)
26456
- ]);
26457
- if (older.length > 0) {
26458
- set((s15) => ({
26459
- messages: [...older, ...s15.messages],
26460
- _offset: s15._offset + older.length,
26461
- hasMore: s15._offset + older.length < count,
26462
- isLoadingHistory: false
26463
- }));
26464
- } else {
26465
- set({ hasMore: false, isLoadingHistory: false });
26466
- }
26467
- } catch {
26468
- set({ isLoadingHistory: false });
26469
- }
26470
- },
26471
- requestScrollTo: (messageId) => {
26472
- set({ scrollToMessageId: messageId });
26473
- setTimeout(() => set({ scrollToMessageId: null }), 100);
26474
- }
26475
- }));
26476
26590
  const useProgressStore = create$1((set) => ({
26477
26591
  items: [],
26478
26592
  upsertItem: (item) => set((state) => {
@@ -26525,10 +26639,10 @@ function findLastIndex(arr, pred) {
26525
26639
  }
26526
26640
  return -1;
26527
26641
  }
26528
- const api$7 = window.api;
26642
+ const api$8 = window.api;
26529
26643
  async function loadFromFramework() {
26530
26644
  try {
26531
- const data = await api$7?.getUsageTotals?.();
26645
+ const data = await api$8?.getUsageTotals?.();
26532
26646
  return data ?? null;
26533
26647
  } catch (e) {
26534
26648
  console.warn("[usage-store] Failed to load persisted totals:", e);
@@ -26644,7 +26758,7 @@ const useUsageStore = create$1((set, get) => {
26644
26758
  },
26645
26759
  // Reset all-time totals (user-initiated)
26646
26760
  resetAllTime: () => {
26647
- api$7?.resetUsageTotals?.().catch?.(() => {
26761
+ api$8?.resetUsageTotals?.().catch?.(() => {
26648
26762
  });
26649
26763
  set({
26650
26764
  runPromptTokens: 0,
@@ -26675,13 +26789,13 @@ const usageStore = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
26675
26789
  __proto__: null,
26676
26790
  useUsageStore
26677
26791
  }, Symbol.toStringTag, { value: "Module" }));
26678
- const api$6 = window.api;
26792
+ const api$7 = window.api;
26679
26793
  const useSessionStore = create$1((set) => ({
26680
26794
  sessionId: "",
26681
26795
  projectPath: "",
26682
26796
  hasProject: false,
26683
26797
  init: async () => {
26684
- const session = await api$6.getCurrentSession();
26798
+ const session = await api$7.getCurrentSession();
26685
26799
  set({
26686
26800
  sessionId: session.sessionId,
26687
26801
  projectPath: session.projectPath,
@@ -26692,7 +26806,7 @@ const useSessionStore = create$1((set) => ({
26692
26806
  }
26693
26807
  },
26694
26808
  pickFolder: async () => {
26695
- const result = await api$6.pickFolder();
26809
+ const result = await api$7.pickFolder();
26696
26810
  if (result) {
26697
26811
  useChatStore.getState().clear();
26698
26812
  useProgressStore.getState().clear();
@@ -26713,7 +26827,7 @@ const useSessionStore = create$1((set) => ({
26713
26827
  return false;
26714
26828
  },
26715
26829
  closeProject: async () => {
26716
- await api$6.closeProject();
26830
+ await api$7.closeProject();
26717
26831
  useChatStore.getState().clear();
26718
26832
  useProgressStore.getState().clear();
26719
26833
  useActivityStore.getState().clear();
@@ -26723,7 +26837,7 @@ const useSessionStore = create$1((set) => ({
26723
26837
  set({ sessionId: "", projectPath: "", hasProject: false });
26724
26838
  }
26725
26839
  }));
26726
- const api$5 = window.api;
26840
+ const api$6 = window.api;
26727
26841
  const ROW_HEIGHT = 28;
26728
26842
  const OVERSCAN_ROWS = 10;
26729
26843
  const MAX_DROP_FILE_SIZE = 100 * 1024 * 1024;
@@ -26822,7 +26936,7 @@ function WorkspaceTree() {
26822
26936
  const parentKey = toKey(relativePath);
26823
26937
  setParentLoading(parentKey, true);
26824
26938
  try {
26825
- const children = await api$5.listTree({
26939
+ const children = await api$6.listTree({
26826
26940
  relativePath,
26827
26941
  showIgnored,
26828
26942
  limit: 2e3
@@ -26847,8 +26961,8 @@ function WorkspaceTree() {
26847
26961
  void Promise.all(dirs.map((dir) => loadChildren(dir)));
26848
26962
  }, 500);
26849
26963
  };
26850
- const unsubFileCreated = api$5.onFileCreated(scheduleRefresh);
26851
- const unsubAgentDone = api$5.onAgentDone(scheduleRefresh);
26964
+ const unsubFileCreated = api$6.onFileCreated(scheduleRefresh);
26965
+ const unsubAgentDone = api$6.onAgentDone(scheduleRefresh);
26852
26966
  return () => {
26853
26967
  if (debounceTimer) clearTimeout(debounceTimer);
26854
26968
  unsubFileCreated();
@@ -26888,7 +27002,7 @@ function WorkspaceTree() {
26888
27002
  }
26889
27003
  setSearching(true);
26890
27004
  try {
26891
- const results = await api$5.searchTree(query.trim(), { showIgnored, maxResults: 4e3 });
27005
+ const results = await api$6.searchTree(query.trim(), { showIgnored, maxResults: 4e3 });
26892
27006
  setSearchResults(results);
26893
27007
  } finally {
26894
27008
  setSearching(false);
@@ -26932,7 +27046,7 @@ function WorkspaceTree() {
26932
27046
  if (node2.type !== "file") return;
26933
27047
  const ext = (node2.name.split(".").pop() || "").toLowerCase();
26934
27048
  if (!TEXT_EXTENSIONS.has(ext)) {
26935
- api$5.openFile(node2.path);
27049
+ api$6.openFile(node2.path);
26936
27050
  return;
26937
27051
  }
26938
27052
  const normalizedNodePath = normalizePath(node2.path);
@@ -26957,14 +27071,14 @@ function WorkspaceTree() {
26957
27071
  });
26958
27072
  }, [data, openPreview]);
26959
27073
  const createArtifact = reactExports.useCallback(async (node2) => {
26960
- await api$5.createArtifactFromFile(node2.path);
27074
+ await api$6.createArtifactFromFile(node2.path);
26961
27075
  await refreshEntities();
26962
27076
  }, [refreshEntities]);
26963
27077
  const handleTrashClick = reactExports.useCallback(async (node2) => {
26964
27078
  if (confirmTrashPath === node2.relativePath) {
26965
27079
  if (confirmTimerRef.current) clearTimeout(confirmTimerRef.current);
26966
27080
  setConfirmTrashPath(null);
26967
- const result = await api$5.trashFile(node2.path);
27081
+ const result = await api$6.trashFile(node2.path);
26968
27082
  if (result.success) {
26969
27083
  const parentRelPath = node2.relativePath.includes("/") ? node2.relativePath.slice(0, node2.relativePath.lastIndexOf("/")) : "";
26970
27084
  await loadChildren(parentRelPath);
@@ -27013,7 +27127,7 @@ function WorkspaceTree() {
27013
27127
  return;
27014
27128
  }
27015
27129
  const relPath = creating.parentDir ? `${creating.parentDir}/${createValue.trim()}` : createValue.trim();
27016
- const result = creating.type === "file" ? await api$5.createFile(relPath) : await api$5.createDir(relPath);
27130
+ const result = creating.type === "file" ? await api$6.createFile(relPath) : await api$6.createDir(relPath);
27017
27131
  if (result.success) {
27018
27132
  await loadChildren(creating.parentDir);
27019
27133
  }
@@ -27040,7 +27154,7 @@ function WorkspaceTree() {
27040
27154
  const parentDir = renaming.includes("/") ? renaming.slice(0, renaming.lastIndexOf("/")) : "";
27041
27155
  const newRelPath = parentDir ? `${parentDir}/${renameValue.trim()}` : renameValue.trim();
27042
27156
  if (newRelPath !== renaming) {
27043
- await api$5.renameFile(renaming, newRelPath);
27157
+ await api$6.renameFile(renaming, newRelPath);
27044
27158
  await loadChildren(parentDir);
27045
27159
  }
27046
27160
  setRenaming(null);
@@ -27082,7 +27196,7 @@ function WorkspaceTree() {
27082
27196
  const base64 = btoa(
27083
27197
  new Uint8Array(buffer).reduce((data2, byte) => data2 + String.fromCharCode(byte), "")
27084
27198
  );
27085
- await api$5.dropToDir(file.name, base64, targetRelPath);
27199
+ await api$6.dropToDir(file.name, base64, targetRelPath);
27086
27200
  }
27087
27201
  await loadChildren(targetRelPath);
27088
27202
  }, [getDropDir, loadChildren]);
@@ -27108,7 +27222,7 @@ function WorkspaceTree() {
27108
27222
  const base64 = btoa(
27109
27223
  new Uint8Array(buffer).reduce((data2, byte) => data2 + String.fromCharCode(byte), "")
27110
27224
  );
27111
- await api$5.dropToDir(file.name, base64, "");
27225
+ await api$6.dropToDir(file.name, base64, "");
27112
27226
  }
27113
27227
  await loadChildren("");
27114
27228
  }, [loadChildren]);
@@ -27170,7 +27284,7 @@ function WorkspaceTree() {
27170
27284
  "div",
27171
27285
  {
27172
27286
  className: "flex items-center gap-1 rounded px-1.5 h-7 text-xs bg-[var(--color-accent)]/10",
27173
- style: { paddingLeft: `${depth * 14 + 6}px` },
27287
+ style: { paddingLeft: `${depth * 1.1 + 0.4}em` },
27174
27288
  children: [
27175
27289
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-3 shrink-0" }),
27176
27290
  creating.type === "directory" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { size: 12, className: "shrink-0 t-text-warning" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { size: 12, className: "shrink-0 t-text-muted" }),
@@ -27197,7 +27311,7 @@ function WorkspaceTree() {
27197
27311
  "div",
27198
27312
  {
27199
27313
  className: "flex items-center gap-1 text-xs t-text-muted h-7",
27200
- style: { paddingLeft: `${row.depth * 14 + 26}px` },
27314
+ style: { paddingLeft: `${row.depth * 1.1 + 2}em` },
27201
27315
  children: [
27202
27316
  /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 11, className: "animate-spin" }),
27203
27317
  "Loading..."
@@ -27330,6 +27444,7 @@ function WorkspaceTree() {
27330
27444
  value: query,
27331
27445
  onChange: (e) => setQuery(e.target.value),
27332
27446
  placeholder: "Filter files...",
27447
+ "aria-label": "Filter files",
27333
27448
  className: "w-full bg-transparent text-xs outline-none t-focus-ring t-text"
27334
27449
  }
27335
27450
  )
@@ -27843,6 +27958,7 @@ function SkillsContent() {
27843
27958
  onClick: () => fileInputRef.current?.click(),
27844
27959
  className: "no-drag flex items-center gap-1 px-1.5 py-0.5 text-[10px] t-text-muted hover:t-text-accent-soft transition-colors rounded t-bg-hover",
27845
27960
  title: "Upload skill (.zip)",
27961
+ "aria-label": "Install skill from zip file",
27846
27962
  children: [
27847
27963
  /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { size: 11 }),
27848
27964
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Install" })
@@ -27870,6 +27986,7 @@ function SkillsContent() {
27870
27986
  value: searchQuery,
27871
27987
  onChange: (e) => setSearchQuery(e.target.value),
27872
27988
  placeholder: "Search skills or tags...",
27989
+ "aria-label": "Search skills",
27873
27990
  className: "w-full pl-6 pr-2 py-1 text-[11px] rounded border t-border t-bg-surface t-text focus:outline-none focus:border-[var(--color-accent-soft)]"
27874
27991
  }
27875
27992
  )
@@ -27957,13 +28074,30 @@ function EntityTabs() {
27957
28074
  return null;
27958
28075
  }
27959
28076
  };
28077
+ const handleTabKeyDown = (e) => {
28078
+ const tabKeys = tabs.map((t) => t.key);
28079
+ const idx = tabKeys.indexOf(leftTab);
28080
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
28081
+ e.preventDefault();
28082
+ const next = tabKeys[(idx + 1) % tabKeys.length];
28083
+ setLeftTab(next);
28084
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
28085
+ e.preventDefault();
28086
+ const prev = tabKeys[(idx - 1 + tabKeys.length) % tabKeys.length];
28087
+ setLeftTab(prev);
28088
+ }
28089
+ };
27960
28090
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col min-h-0", children: [
27961
28091
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b t-border px-1", role: "tablist", "aria-label": "Content categories", children: tabs.map(({ key, label, icon: Icon2 }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
27962
28092
  "button",
27963
28093
  {
27964
28094
  role: "tab",
28095
+ id: `tab-${key}`,
27965
28096
  "aria-selected": leftTab === key,
28097
+ "aria-controls": `tabpanel-${key}`,
28098
+ tabIndex: leftTab === key ? 0 : -1,
27966
28099
  onClick: () => setLeftTab(key),
28100
+ onKeyDown: handleTabKeyDown,
27967
28101
  className: `no-drag flex items-center gap-1 px-2 py-2 text-[11px] font-medium border-b-2 transition-colors ${leftTab === key ? "border-[var(--color-accent-soft)] t-text-accent" : "border-transparent t-text-muted hover:t-text-secondary"}`,
27968
28102
  title: label,
27969
28103
  children: [
@@ -27973,7 +28107,16 @@ function EntityTabs() {
27973
28107
  },
27974
28108
  key
27975
28109
  )) }),
27976
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-hidden flex flex-col", children: renderContent() })
28110
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
28111
+ "div",
28112
+ {
28113
+ role: "tabpanel",
28114
+ id: `tabpanel-${leftTab}`,
28115
+ "aria-labelledby": `tab-${leftTab}`,
28116
+ className: "flex-1 min-h-0 overflow-hidden flex flex-col",
28117
+ children: renderContent()
28118
+ }
28119
+ )
27977
28120
  ] });
27978
28121
  }
27979
28122
  function QuickAction({
@@ -28158,6 +28301,166 @@ function LiteratureSidebar() {
28158
28301
  ] })
28159
28302
  ] });
28160
28303
  }
28304
+ const useComputeStore = create$1((set) => ({
28305
+ runs: /* @__PURE__ */ new Map(),
28306
+ environment: null,
28307
+ updateRun: (runId, data) => set((state) => {
28308
+ const next = new Map(state.runs);
28309
+ const existing = next.get(runId);
28310
+ if (existing) {
28311
+ next.set(runId, { ...existing, ...data });
28312
+ } else {
28313
+ next.set(runId, {
28314
+ runId,
28315
+ status: "running",
28316
+ currentPhase: "full",
28317
+ command: "",
28318
+ sandbox: "process",
28319
+ weight: "heavy",
28320
+ elapsedSeconds: 0,
28321
+ outputBytes: 0,
28322
+ outputLines: 0,
28323
+ stalled: false,
28324
+ outputTail: "",
28325
+ ...data
28326
+ });
28327
+ }
28328
+ return { runs: next };
28329
+ }),
28330
+ removeRun: (runId) => set((state) => {
28331
+ const next = new Map(state.runs);
28332
+ next.delete(runId);
28333
+ return { runs: next };
28334
+ }),
28335
+ setEnvironment: (env2) => set({ environment: env2 }),
28336
+ reset: () => set({ runs: /* @__PURE__ */ new Map(), environment: null })
28337
+ }));
28338
+ function useActiveRuns() {
28339
+ const runs = useComputeStore((s15) => s15.runs);
28340
+ return Array.from(runs.values()).filter(
28341
+ (r) => r.status === "running" || r.status === "stalled"
28342
+ );
28343
+ }
28344
+ function useRecentRuns() {
28345
+ const runs = useComputeStore((s15) => s15.runs);
28346
+ return Array.from(runs.values()).filter((r) => r.status !== "running" && r.status !== "stalled").sort((a, b2) => {
28347
+ const aTime = a.startedAt ? new Date(a.startedAt).getTime() : 0;
28348
+ const bTime = b2.startedAt ? new Date(b2.startedAt).getTime() : 0;
28349
+ return bTime - aTime;
28350
+ }).slice(0, 20);
28351
+ }
28352
+ function useActiveRunCount() {
28353
+ const runs = useComputeStore((s15) => s15.runs);
28354
+ let count = 0;
28355
+ for (const r of runs.values()) {
28356
+ if (r.status === "running" || r.status === "stalled") count++;
28357
+ }
28358
+ return count;
28359
+ }
28360
+ function ResourceBar({ label, percent }) {
28361
+ const clamped = Math.min(100, Math.max(0, percent));
28362
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
28363
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted w-11 text-right shrink-0", children: label }),
28364
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 h-1 rounded-full t-bg-elevated overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
28365
+ "div",
28366
+ {
28367
+ className: `h-full rounded-full transition-all duration-700 ${clamped > 85 ? "" : "t-gradient-accent-h"}`,
28368
+ style: {
28369
+ width: `${clamped}%`,
28370
+ ...clamped > 85 ? { background: "var(--color-text-muted)", opacity: 0.6 } : { opacity: 0.5 }
28371
+ }
28372
+ }
28373
+ ) }),
28374
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] t-text-muted tabular-nums w-7 shrink-0", children: [
28375
+ clamped,
28376
+ "%"
28377
+ ] })
28378
+ ] });
28379
+ }
28380
+ function SandboxExplanation({ environment }) {
28381
+ const [expanded, setExpanded] = reactExports.useState(false);
28382
+ const isDocker = environment.sandbox === "docker";
28383
+ const isMac = environment.os === "darwin";
28384
+ const hasMLX = environment.mlxAvailable;
28385
+ let explanation;
28386
+ if (isDocker && isMac) {
28387
+ explanation = "Docker available. Note: Docker on macOS lacks GPU passthrough. Switch to Process sandbox for MLX/Metal GPU access if needed.";
28388
+ } else if (isDocker) {
28389
+ explanation = "Docker available. Runs execute in isolated containers with resource limits.";
28390
+ } else if (isMac && hasMLX) {
28391
+ explanation = "Process sandbox uses Python virtual environments with process-group isolation. Direct MLX/Metal GPU access for ML training.";
28392
+ } else if (isMac) {
28393
+ explanation = "Process sandbox uses Python virtual environments. Install Docker for stronger container-based isolation.";
28394
+ } else {
28395
+ explanation = "Process sandbox uses Python virtual environments with process-group isolation.";
28396
+ }
28397
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 pt-2 border-t t-border-subtle", children: [
28398
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
28399
+ "button",
28400
+ {
28401
+ onClick: () => setExpanded(!expanded),
28402
+ className: "flex items-center gap-1 text-[10px] t-text-muted hover:t-text-secondary transition-colors w-full text-left",
28403
+ children: [
28404
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 10 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 10 }),
28405
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
28406
+ "Sandbox: ",
28407
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-secondary", children: isDocker ? "Docker" : "Process (venv)" })
28408
+ ] })
28409
+ ]
28410
+ }
28411
+ ),
28412
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1.5 ml-3.5 text-[10px] t-text-muted leading-relaxed", children: explanation })
28413
+ ] });
28414
+ }
28415
+ function ComputeSidebar() {
28416
+ const environment = useComputeStore((s15) => s15.environment);
28417
+ const activeCount = useActiveRunCount();
28418
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col min-h-0", children: [
28419
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 pt-2 pb-2", children: [
28420
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] t-text-accent-soft uppercase tracking-wider px-3 pb-1.5 font-medium", children: "Compute Target" }),
28421
+ environment ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-2.5 rounded-lg t-bg-surface border t-border-subtle", children: [
28422
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
28423
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `w-1.5 h-1.5 rounded-full shrink-0 ${activeCount > 0 ? "bg-[var(--color-accent)]" : "bg-[var(--color-text-muted)]"}`, style: { opacity: activeCount > 0 ? 1 : 0.5 } }),
28424
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs t-text font-medium", children: "Local Machine" })
28425
+ ] }),
28426
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] t-text-muted leading-relaxed space-y-0.5", children: [
28427
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
28428
+ environment.gpu || `${environment.os} ${environment.arch}`,
28429
+ " · ",
28430
+ Math.round(environment.totalMemoryMb / 1024),
28431
+ " GB"
28432
+ ] }),
28433
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
28434
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Python" }),
28435
+ environment.mlxAvailable && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1 py-px rounded text-[9px] bg-[var(--color-accent-soft)]/10 t-text-accent", children: "MLX" }),
28436
+ environment.sandbox === "docker" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1 py-px rounded text-[9px] t-bg-elevated t-text-muted", children: "Docker" })
28437
+ ] })
28438
+ ] }),
28439
+ (environment.freeMemoryMb !== void 0 || environment.freeDiskMb !== void 0) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2.5 space-y-1.5", children: environment.freeMemoryMb !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
28440
+ ResourceBar,
28441
+ {
28442
+ label: "Memory",
28443
+ percent: Math.round((1 - environment.freeMemoryMb / environment.totalMemoryMb) * 100)
28444
+ }
28445
+ ) }),
28446
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SandboxExplanation, { environment }),
28447
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 pt-2 border-t t-border-subtle text-[10px] t-text-muted", children: activeCount > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
28448
+ activeCount,
28449
+ " running"
28450
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Ready" }) })
28451
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-4 rounded-lg t-bg-surface border t-border-subtle", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 t-text-muted", children: [
28452
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 14, className: "opacity-40" }),
28453
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px]", children: "Detecting environment..." })
28454
+ ] }) }),
28455
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1.5 px-3 py-2 rounded-lg border border-dashed t-border-subtle cursor-default", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted opacity-60", children: "+ Add remote target" }) })
28456
+ ] }),
28457
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 border-t t-border" }),
28458
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto px-3 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 text-[10px] t-text-muted leading-relaxed", children: [
28459
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Info$1, { size: 12, className: "shrink-0 mt-0.5 opacity-40" }),
28460
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Ask the agent to run scripts, train models, or process data. Code executes in a sandboxed environment with progress tracking and failure analysis." })
28461
+ ] }) })
28462
+ ] });
28463
+ }
28161
28464
  function UserProfile() {
28162
28465
  const projectPath = useSessionStore((s15) => s15.projectPath);
28163
28466
  const pickFolder = useSessionStore((s15) => s15.pickFolder);
@@ -28181,25 +28484,6 @@ function UserProfile() {
28181
28484
  }
28182
28485
  );
28183
28486
  }
28184
- const REASONING_MODELS = [
28185
- "gpt-5.4",
28186
- "gpt-5.4-mini",
28187
- "gpt-5.4-nano",
28188
- "claude-opus-4-6"
28189
- ];
28190
- const SUPPORTED_MODELS = [
28191
- // GPT
28192
- { id: "gpt-5.4", label: "GPT-5.4", provider: "OpenAI" },
28193
- { id: "gpt-5.4-mini", label: "GPT-5.4 Mini", provider: "OpenAI" },
28194
- { id: "gpt-5.4-nano", label: "GPT-5.4 Nano", provider: "OpenAI" },
28195
- { id: "gpt-4o", label: "GPT-4o", provider: "OpenAI" },
28196
- // Anthropic Claude 4.6
28197
- { id: "claude-opus-4-6", label: "Claude Opus 4.6", provider: "Anthropic" },
28198
- // Anthropic Claude 4.5
28199
- { id: "claude-opus-4-5-20251101", label: "Claude Opus 4.5", provider: "Anthropic" },
28200
- { id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", provider: "Anthropic" },
28201
- { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", provider: "Anthropic" }
28202
- ];
28203
28487
  const providers = [...new Set(SUPPORTED_MODELS.map((m) => m.provider))];
28204
28488
  const groupedModels = {};
28205
28489
  for (const p of providers) {
@@ -28208,6 +28492,8 @@ for (const p of providers) {
28208
28492
  function ModelSelector$1({ selectedModel, onSelectModel }) {
28209
28493
  const [open, setOpen] = reactExports.useState(false);
28210
28494
  const [anthropicStatus, setAnthropicStatus] = reactExports.useState(null);
28495
+ const [codexStatus, setCodexStatus] = reactExports.useState(null);
28496
+ const [codexLoggingIn, setCodexLoggingIn] = reactExports.useState(false);
28211
28497
  const [showAnthropicDialog, setShowAnthropicDialog] = reactExports.useState(false);
28212
28498
  const [showOpenAIDialog, setShowOpenAIDialog] = reactExports.useState(false);
28213
28499
  const ref = reactExports.useRef(null);
@@ -28221,6 +28507,14 @@ function ModelSelector$1({ selectedModel, onSelectModel }) {
28221
28507
  setAnthropicStatus(null);
28222
28508
  }
28223
28509
  }, [api2]);
28510
+ const refreshCodexStatus = reactExports.useCallback(async () => {
28511
+ try {
28512
+ const status = await api2?.getOpenAICodexStatus?.();
28513
+ setCodexStatus(status ?? null);
28514
+ } catch {
28515
+ setCodexStatus(null);
28516
+ }
28517
+ }, [api2]);
28224
28518
  reactExports.useEffect(() => {
28225
28519
  if (!open) return;
28226
28520
  const handler = (e) => {
@@ -28241,13 +28535,47 @@ function ModelSelector$1({ selectedModel, onSelectModel }) {
28241
28535
  }, [open]);
28242
28536
  reactExports.useEffect(() => {
28243
28537
  refreshAnthropicStatus();
28538
+ refreshCodexStatus();
28244
28539
  const unsub = api2?.onAnthropicAuthStatus?.((status) => setAnthropicStatus(status));
28245
28540
  return () => {
28246
28541
  if (typeof unsub === "function") unsub();
28247
28542
  };
28248
- }, [api2, refreshAnthropicStatus]);
28543
+ }, [api2, refreshAnthropicStatus, refreshCodexStatus]);
28544
+ const handleCodexLogin = async () => {
28545
+ setCodexLoggingIn(true);
28546
+ try {
28547
+ const result = await api2?.openaiCodexLogin?.();
28548
+ if (result?.success) {
28549
+ await refreshCodexStatus();
28550
+ } else {
28551
+ console.error("[ModelSelector] Codex login failed:", result?.error);
28552
+ }
28553
+ } catch (err) {
28554
+ console.error("[ModelSelector] Codex login error:", err);
28555
+ } finally {
28556
+ setCodexLoggingIn(false);
28557
+ }
28558
+ };
28559
+ const handleCodexLogout = async () => {
28560
+ await api2?.openaiCodexLogout?.();
28561
+ await refreshCodexStatus();
28562
+ };
28249
28563
  const handleModelSelect = async (model) => {
28250
- if (model.provider === "OpenAI") {
28564
+ const { provider } = parseModelKey(model.id);
28565
+ if (provider === "openai-codex") {
28566
+ if (!codexStatus?.isLoggedIn) {
28567
+ await handleCodexLogin();
28568
+ const status = await api2?.getOpenAICodexStatus?.();
28569
+ if (!status?.isLoggedIn) {
28570
+ setOpen(false);
28571
+ return;
28572
+ }
28573
+ }
28574
+ onSelectModel(model.id);
28575
+ setOpen(false);
28576
+ return;
28577
+ }
28578
+ if (provider === "openai") {
28251
28579
  const status = await api2?.getOpenAIAuthStatus?.();
28252
28580
  if (!status?.hasApiKey) {
28253
28581
  setShowOpenAIDialog(true);
@@ -28255,7 +28583,7 @@ function ModelSelector$1({ selectedModel, onSelectModel }) {
28255
28583
  return;
28256
28584
  }
28257
28585
  }
28258
- if (model.provider === "Anthropic") {
28586
+ if (provider === "anthropic") {
28259
28587
  const status = await api2?.getAnthropicAuthStatus?.();
28260
28588
  if (!status?.hasApiKeyFallback) {
28261
28589
  setShowAnthropicDialog(true);
@@ -28267,18 +28595,27 @@ function ModelSelector$1({ selectedModel, onSelectModel }) {
28267
28595
  setOpen(false);
28268
28596
  refreshAnthropicStatus();
28269
28597
  };
28270
- const authBadge = current?.provider === "Anthropic" ? anthropicStatus?.authMode === "api-key" ? "api" : "auth" : null;
28598
+ const { provider: currentProvider } = current ? parseModelKey(current.id) : { provider: "" };
28599
+ const authBadge = currentProvider === "anthropic" ? anthropicStatus?.authMode === "api-key" ? "api" : "auth" : currentProvider === "openai-codex" ? "sub" : currentProvider === "openai" ? "api" : null;
28271
28600
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref, className: "relative", children: [
28272
28601
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
28273
28602
  "button",
28274
28603
  {
28275
28604
  onClick: () => setOpen(!open),
28276
- className: "no-drag flex items-center gap-1.5 px-2 py-1.5 rounded-lg t-text-secondary text-xs font-medium t-bg-hover transition-colors",
28277
- title: "Select model",
28605
+ className: "no-drag group relative flex items-center gap-1.5 px-2 py-1.5 rounded-lg t-text-secondary text-xs font-medium t-bg-hover transition-colors",
28278
28606
  "aria-label": "Select model",
28279
28607
  "aria-haspopup": "listbox",
28280
28608
  "aria-expanded": open,
28281
28609
  children: [
28610
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
28611
+ "span",
28612
+ {
28613
+ role: "tooltip",
28614
+ className: "pointer-events-none absolute left-1/2 -translate-x-1/2 top-full mt-1 px-2 py-0.5 rounded text-[10px] t-bg-elevated t-text-secondary border t-border shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 z-50",
28615
+ style: { transition: "opacity 0.15s ease", transitionDelay: "0.2s" },
28616
+ children: "Select model"
28617
+ }
28618
+ ),
28282
28619
  /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 14 }),
28283
28620
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate max-w-[100px]", children: current?.label || selectedModel }),
28284
28621
  authBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] px-1 rounded border t-border t-text-muted uppercase", children: authBadge }),
@@ -28288,7 +28625,39 @@ function ModelSelector$1({ selectedModel, onSelectModel }) {
28288
28625
  ),
28289
28626
  open && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-full left-0 mt-1 w-64 max-h-80 overflow-y-auto rounded-xl border t-border t-bg-surface shadow-xl z-50", role: "listbox", "aria-label": "Available models", children: providers.map((provider) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
28290
28627
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-1.5 text-[10px] font-semibold uppercase tracking-wider t-text-muted sticky top-0 t-bg-surface", children: provider }),
28291
- (provider === "OpenAI" || provider === "Anthropic") && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-1 text-[11px] t-text-muted", children: "API Key Only" }),
28628
+ provider === "ChatGPT Subscription" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 pb-1 flex items-center justify-between", children: [
28629
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] t-text-muted", children: codexStatus?.isLoggedIn ? "Signed in" : "OAuth required" }),
28630
+ codexStatus?.isLoggedIn ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
28631
+ "button",
28632
+ {
28633
+ onClick: (e) => {
28634
+ e.stopPropagation();
28635
+ handleCodexLogout();
28636
+ },
28637
+ className: "text-[10px] t-text-muted hover:t-text flex items-center gap-0.5",
28638
+ children: [
28639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LogOut, { size: 10 }),
28640
+ " Sign out"
28641
+ ]
28642
+ }
28643
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
28644
+ "button",
28645
+ {
28646
+ onClick: (e) => {
28647
+ e.stopPropagation();
28648
+ handleCodexLogin();
28649
+ },
28650
+ disabled: codexLoggingIn,
28651
+ className: "text-[10px] t-text-accent flex items-center gap-0.5 hover:opacity-80 disabled:opacity-50",
28652
+ children: [
28653
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LogIn, { size: 10 }),
28654
+ " ",
28655
+ codexLoggingIn ? "Signing in..." : "Sign in"
28656
+ ]
28657
+ }
28658
+ )
28659
+ ] }),
28660
+ (provider === "OpenAI" || provider === "Anthropic") && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-1 text-[11px] t-text-muted", children: "API Key" }),
28292
28661
  groupedModels[provider].map((model) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
28293
28662
  "button",
28294
28663
  {
@@ -28342,7 +28711,39 @@ function ApiKeyDialog({
28342
28711
  const [saving, setSaving] = reactExports.useState(false);
28343
28712
  const [error, setError] = reactExports.useState(null);
28344
28713
  const [showKey, setShowKey] = reactExports.useState(false);
28714
+ const dialogRef = reactExports.useRef(null);
28345
28715
  const api2 = window.api;
28716
+ reactExports.useEffect(() => {
28717
+ const dialog = dialogRef.current;
28718
+ if (!dialog) return;
28719
+ const focusable = dialog.querySelectorAll(
28720
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
28721
+ );
28722
+ if (focusable.length === 0) return;
28723
+ const first = focusable[0];
28724
+ const last = focusable[focusable.length - 1];
28725
+ first.focus();
28726
+ const handler = (e) => {
28727
+ if (e.key === "Escape") {
28728
+ onClose();
28729
+ return;
28730
+ }
28731
+ if (e.key !== "Tab") return;
28732
+ if (e.shiftKey) {
28733
+ if (document.activeElement === first) {
28734
+ e.preventDefault();
28735
+ last.focus();
28736
+ }
28737
+ } else {
28738
+ if (document.activeElement === last) {
28739
+ e.preventDefault();
28740
+ first.focus();
28741
+ }
28742
+ }
28743
+ };
28744
+ dialog.addEventListener("keydown", handler);
28745
+ return () => dialog.removeEventListener("keydown", handler);
28746
+ }, []);
28346
28747
  const handleSave = async () => {
28347
28748
  const trimmed = value.trim();
28348
28749
  if (!trimmed) {
@@ -28360,7 +28761,7 @@ function ApiKeyDialog({
28360
28761
  setSaving(false);
28361
28762
  }
28362
28763
  };
28363
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[90] flex items-center justify-center bg-black/40 p-4", role: "dialog", "aria-modal": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-md rounded-xl border t-border t-bg-surface shadow-2xl p-4 space-y-3", children: [
28764
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[90] flex items-center justify-center bg-black/40 p-4", role: "dialog", "aria-modal": "true", "aria-label": `${provider} API Key Required`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: dialogRef, className: "w-full max-w-md rounded-xl border t-border t-bg-surface shadow-2xl p-4 space-y-3", children: [
28364
28765
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
28365
28766
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-semibold t-text", children: [
28366
28767
  provider,
@@ -28386,6 +28787,7 @@ function ApiKeyDialog({
28386
28787
  onKeyDown: (e) => {
28387
28788
  if (e.key === "Enter") handleSave();
28388
28789
  },
28790
+ "aria-label": `${provider} API key`,
28389
28791
  autoFocus: true
28390
28792
  }
28391
28793
  ),
@@ -28405,7 +28807,7 @@ function ApiKeyDialog({
28405
28807
  /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "px-1 rounded t-bg-surface", children: "~/.research-copilot/config.json" }),
28406
28808
  ". No restart needed."
28407
28809
  ] }),
28408
- error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400", children: error }),
28810
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400", role: "alert", children: error }),
28409
28811
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-2", children: [
28410
28812
  /* @__PURE__ */ jsxRuntimeExports.jsx(
28411
28813
  "button",
@@ -28452,13 +28854,27 @@ const COLORS = {
28452
28854
  };
28453
28855
  function ReasoningToggle$1({ selectedModel, reasoningEffort, onChangeEffort }) {
28454
28856
  if (!REASONING_MODELS.includes(selectedModel)) return null;
28455
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
28857
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
28456
28858
  "button",
28457
28859
  {
28458
28860
  onClick: () => onChangeEffort(CYCLE[reasoningEffort]),
28459
- className: "no-drag p-1.5 rounded-lg t-bg-hover transition-colors",
28460
- title: `Reasoning: ${reasoningEffort}`,
28461
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(Lightbulb, { size: 16, className: COLORS[reasoningEffort] })
28861
+ className: "no-drag group relative p-2.5 rounded-lg t-bg-hover transition-colors",
28862
+ "aria-label": `Reasoning effort: ${reasoningEffort}`,
28863
+ children: [
28864
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Lightbulb, { size: 16, className: COLORS[reasoningEffort] }),
28865
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
28866
+ "span",
28867
+ {
28868
+ role: "tooltip",
28869
+ className: "pointer-events-none absolute left-1/2 -translate-x-1/2 top-full mt-1 px-2 py-0.5 rounded text-[10px] t-bg-elevated t-text-secondary border t-border shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 z-50",
28870
+ style: { transition: "opacity 0.15s ease", transitionDelay: "0.2s" },
28871
+ children: [
28872
+ "Reasoning: ",
28873
+ reasoningEffort
28874
+ ]
28875
+ }
28876
+ )
28877
+ ]
28462
28878
  }
28463
28879
  );
28464
28880
  }
@@ -28475,45 +28891,87 @@ function ReasoningToggle() {
28475
28891
  }
28476
28892
  );
28477
28893
  }
28894
+ function ToolbarButton({ onClick, tooltip, children }) {
28895
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
28896
+ "button",
28897
+ {
28898
+ onClick,
28899
+ "aria-label": tooltip,
28900
+ className: "no-drag group relative p-2.5 rounded-lg t-text-muted t-bg-hover transition-colors",
28901
+ children: [
28902
+ children,
28903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
28904
+ "span",
28905
+ {
28906
+ role: "tooltip",
28907
+ className: "pointer-events-none absolute left-1/2 -translate-x-1/2 top-full mt-1 px-2 py-0.5 rounded text-[10px] t-bg-elevated t-text-secondary border t-border shadow-lg whitespace-nowrap opacity-0 group-hover:opacity-100 z-50",
28908
+ style: { transition: "opacity 0.15s ease", transitionDelay: "0.2s" },
28909
+ children: tooltip
28910
+ }
28911
+ )
28912
+ ]
28913
+ }
28914
+ );
28915
+ }
28478
28916
  function LeftSidebar() {
28479
28917
  const theme = useUIStore((s15) => s15.theme);
28480
28918
  const toggleTheme = useUIStore((s15) => s15.toggleTheme);
28481
28919
  const centerView = useUIStore((s15) => s15.centerView);
28920
+ const noContextShownRef = reactExports.useRef(false);
28921
+ const handleResetContext = reactExports.useCallback(async () => {
28922
+ const messages = useChatStore.getState().messages;
28923
+ const hasContext = messages.some((m) => m.role === "user" || m.role === "assistant");
28924
+ if (!hasContext) {
28925
+ if (!noContextShownRef.current) {
28926
+ noContextShownRef.current = true;
28927
+ useChatStore.getState().insertContextReset();
28928
+ useChatStore.setState((s15) => {
28929
+ const msgs = [...s15.messages];
28930
+ const last = msgs[msgs.length - 1];
28931
+ if (last?.role === "system") {
28932
+ msgs[msgs.length - 1] = { ...last, content: "No context to reset yet." };
28933
+ }
28934
+ return { messages: msgs };
28935
+ });
28936
+ }
28937
+ return;
28938
+ }
28939
+ await window.api.clearSessionMemory();
28940
+ useChatStore.getState().insertContextReset();
28941
+ noContextShownRef.current = false;
28942
+ }, []);
28482
28943
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("aside", { className: "w-80 flex flex-col border-r t-border t-bg-base pt-10", children: [
28483
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pb-3 flex items-center justify-between", children: [
28944
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("nav", { "aria-label": "Sidebar tools", className: "px-4 pb-3 flex items-center justify-between", children: [
28484
28945
  /* @__PURE__ */ jsxRuntimeExports.jsx(ModelSelector, {}),
28485
28946
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
28486
28947
  /* @__PURE__ */ jsxRuntimeExports.jsx(ReasoningToggle, {}),
28487
28948
  /* @__PURE__ */ jsxRuntimeExports.jsx(
28488
- "button",
28949
+ ToolbarButton,
28489
28950
  {
28490
- onClick: () => window.api.clearSessionMemory(),
28491
- className: "no-drag p-1.5 rounded-lg t-text-muted t-bg-hover transition-colors",
28492
- title: "Clear session memory",
28493
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(Eraser, { size: 16 })
28951
+ onClick: handleResetContext,
28952
+ tooltip: "Reset AI context",
28953
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 16 })
28494
28954
  }
28495
28955
  ),
28496
28956
  /* @__PURE__ */ jsxRuntimeExports.jsx(
28497
- "button",
28957
+ ToolbarButton,
28498
28958
  {
28499
28959
  onClick: toggleTheme,
28500
- className: "no-drag p-1.5 rounded-lg t-text-muted t-bg-hover transition-colors",
28501
- title: `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
28960
+ tooltip: `${theme === "dark" ? "Light" : "Dark"} mode`,
28502
28961
  children: theme === "dark" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Sun, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Moon, { size: 16 })
28503
28962
  }
28504
28963
  ),
28505
28964
  /* @__PURE__ */ jsxRuntimeExports.jsx(
28506
- "button",
28965
+ ToolbarButton,
28507
28966
  {
28508
28967
  onClick: () => useUIStore.getState().toggleTerminal(),
28509
- className: "no-drag p-1.5 rounded-lg t-text-muted t-bg-hover transition-colors",
28510
- title: "Toggle terminal (⌘`)",
28968
+ tooltip: "Terminal ⌘`",
28511
28969
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 16 })
28512
28970
  }
28513
28971
  )
28514
28972
  ] })
28515
28973
  ] }),
28516
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: centerView === "literature" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LiteratureSidebar, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(EntityTabs, {}) }),
28974
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: centerView === "literature" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LiteratureSidebar, {}) : centerView === "compute" && window.api?.isComputeEnabled?.() ? /* @__PURE__ */ jsxRuntimeExports.jsx(ComputeSidebar, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(EntityTabs, {}) }),
28517
28975
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-t t-border p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserProfile, {}) })
28518
28976
  ] });
28519
28977
  }
@@ -28524,17 +28982,11 @@ const suggestions = [
28524
28982
  { icon: Sparkles, label: "Brainstorm ideas", prompt: "Help me brainstorm ideas for " }
28525
28983
  ];
28526
28984
  function HeroIdle() {
28985
+ const setDraftText = useChatStore((s15) => s15.setDraftText);
28527
28986
  const handleSuggestion = (prompt) => {
28987
+ setDraftText(prompt);
28528
28988
  const input = document.querySelector("[data-chat-input]");
28529
- if (input) {
28530
- const nativeSetter = Object.getOwnPropertyDescriptor(
28531
- window.HTMLTextAreaElement.prototype,
28532
- "value"
28533
- )?.set;
28534
- nativeSetter?.call(input, prompt);
28535
- input.dispatchEvent(new Event("input", { bubbles: true }));
28536
- input.focus();
28537
- }
28989
+ input?.focus();
28538
28990
  };
28539
28991
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-10 w-full max-w-lg px-8", children: [
28540
28992
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center space-y-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -28936,7 +29388,7 @@ function StatusIcon({ status, size = 13 }) {
28936
29388
  return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size, className: "t-text-error" });
28937
29389
  }
28938
29390
  }
28939
- function formatDuration(ms2) {
29391
+ function formatDuration$1(ms2) {
28940
29392
  if (ms2 < 1e3) return `${ms2}ms`;
28941
29393
  if (ms2 < 6e4) return `${(ms2 / 1e3).toFixed(1)}s`;
28942
29394
  return `${(ms2 / 6e4).toFixed(1)}m`;
@@ -28944,10 +29396,10 @@ function formatDuration(ms2) {
28944
29396
  function ElapsedTimer({ startedAt }) {
28945
29397
  const [elapsed, setElapsed] = reactExports.useState(Date.now() - startedAt);
28946
29398
  reactExports.useEffect(() => {
28947
- const interval = setInterval(() => setElapsed(Date.now() - startedAt), 100);
29399
+ const interval = setInterval(() => setElapsed(Date.now() - startedAt), 500);
28948
29400
  return () => clearInterval(interval);
28949
29401
  }, [startedAt]);
28950
- return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted tabular-nums", children: formatDuration(elapsed) });
29402
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted tabular-nums", children: formatDuration$1(elapsed) });
28951
29403
  }
28952
29404
  const CompactToolLine = React$2.memo(function CompactToolLine2({ event }) {
28953
29405
  const [expanded, setExpanded] = reactExports.useState(false);
@@ -28967,7 +29419,7 @@ const CompactToolLine = React$2.memo(function CompactToolLine2({ event }) {
28967
29419
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium t-text-secondary shrink-0", children: name2 }),
28968
29420
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-muted select-none", children: "·" }),
28969
29421
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-muted truncate flex-1 text-left", children: displaySummary }),
28970
- event.durationMs != null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted tabular-nums shrink-0", children: formatDuration(event.durationMs) }),
29422
+ event.durationMs != null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] t-text-muted tabular-nums shrink-0", children: formatDuration$1(event.durationMs) }),
28971
29423
  hasExpandable && /* @__PURE__ */ jsxRuntimeExports.jsx(
28972
29424
  "span",
28973
29425
  {
@@ -29183,7 +29635,7 @@ function ToolUseStream({ events: propEvents }) {
29183
29635
  running.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 mt-1", children: running.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolUseCard, { event }, event.id)) })
29184
29636
  ] });
29185
29637
  }
29186
- const api$4 = window.api;
29638
+ const api$5 = window.api;
29187
29639
  const remarkPlugins$1 = [remarkGfm];
29188
29640
  function SelectionBookmark() {
29189
29641
  const refreshAll = useEntityStore((s15) => s15.refreshAll);
@@ -29244,7 +29696,7 @@ function SelectionBookmark() {
29244
29696
  try {
29245
29697
  const first = selectedText.split(/[.!?\n]/)[0].trim();
29246
29698
  const title = first.length > 60 ? first.slice(0, 57) + "…" : first || "Untitled selection";
29247
- const created = await api$4.artifactCreate({
29699
+ const created = await api$5.artifactCreate({
29248
29700
  type: "note",
29249
29701
  title,
29250
29702
  content: selectedText,
@@ -29276,7 +29728,7 @@ function SelectionBookmark() {
29276
29728
  {
29277
29729
  ref: tooltipRef,
29278
29730
  onMouseDown: handleSave,
29279
- className: "fixed z-[100] flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium shadow-lg border t-border transition-colors",
29731
+ className: "fixed z-50 flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-medium shadow-lg border t-border transition-colors",
29280
29732
  style: {
29281
29733
  left: pos.x,
29282
29734
  top: pos.y,
@@ -29309,7 +29761,7 @@ const MessageBubble = React$2.memo(function MessageBubble2({ msg, isSaved }) {
29309
29761
  try {
29310
29762
  const first = msg.content.replace(/^#+\s*/, "").split(/[.!?\n]/)[0].trim();
29311
29763
  const title = first.length > 60 ? first.slice(0, 57) + "…" : first || "Untitled note";
29312
- const created = await api$4.artifactCreate({
29764
+ const created = await api$5.artifactCreate({
29313
29765
  type: "note",
29314
29766
  title,
29315
29767
  content: msg.content,
@@ -29343,6 +29795,7 @@ const MessageBubble = React$2.memo(function MessageBubble2({ msg, isSaved }) {
29343
29795
  {
29344
29796
  src,
29345
29797
  alt: "",
29798
+ loading: "lazy",
29346
29799
  className: `rounded-lg border t-border cursor-pointer hover:opacity-90 transition-opacity ${isUser ? "max-h-48" : "max-h-80"}`,
29347
29800
  onClick: () => window.open(src, "_blank")
29348
29801
  },
@@ -29365,10 +29818,10 @@ const MessageBubble = React$2.memo(function MessageBubble2({ msg, isSaved }) {
29365
29818
  ) });
29366
29819
  });
29367
29820
  function ThinkingIndicator() {
29368
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 mt-3 ml-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex gap-[3px] items-center", children: [
29369
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce", style: { animationDelay: "0ms" } }),
29370
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce", style: { animationDelay: "150ms" } }),
29371
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce", style: { animationDelay: "300ms" } })
29821
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1.5 mt-3 ml-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex gap-[3px] items-center", "aria-label": "Thinking", children: [
29822
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce animation-delay-0" }),
29823
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce animation-delay-150" }),
29824
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-[5px] h-[5px] rounded-full t-bg-accent-soft animate-bounce animation-delay-300" })
29372
29825
  ] }) });
29373
29826
  }
29374
29827
  function StreamingBubble() {
@@ -29454,6 +29907,13 @@ function ChatMessages() {
29454
29907
  children: [
29455
29908
  isLoadingHistory && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 16, className: "animate-spin t-text-muted" }) }),
29456
29909
  messages.map((msg, i) => {
29910
+ if (msg.role === "system") {
29911
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 my-5 px-2", children: [
29912
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 h-px t-bg-elevated" }),
29913
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] t-text-muted shrink-0", children: msg.content }),
29914
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 h-px t-bg-elevated" })
29915
+ ] }, msg.id);
29916
+ }
29457
29917
  const prev = messages[i - 1];
29458
29918
  const gap = prev && prev.role !== msg.role ? "mt-5" : "mt-3";
29459
29919
  const toolEvents = msg.role === "assistant" ? turnToolEvents.get(msg.id) : void 0;
@@ -29482,7 +29942,7 @@ function ChatMessages() {
29482
29942
  )
29483
29943
  ] });
29484
29944
  }
29485
- const api$3 = window.api;
29945
+ const api$4 = window.api;
29486
29946
  const typeIcons$1 = {
29487
29947
  note: /* @__PURE__ */ jsxRuntimeExports.jsx(StickyNote, { size: 13, className: "t-text-warning" }),
29488
29948
  paper: /* @__PURE__ */ jsxRuntimeExports.jsx(BookOpen, { size: 13, className: "t-text-info" }),
@@ -29531,7 +29991,7 @@ function MentionPopover({ query, onSelect, onClose }) {
29531
29991
  setLoading(true);
29532
29992
  clearTimeout(debounceRef.current);
29533
29993
  debounceRef.current = setTimeout(() => {
29534
- api$3.getCandidates(query).then((result) => {
29994
+ api$4.getCandidates(query).then((result) => {
29535
29995
  if (stale) return;
29536
29996
  setCandidates(result || []);
29537
29997
  setSelectedIdx(0);
@@ -29577,8 +30037,8 @@ function MentionPopover({ query, onSelect, onClose }) {
29577
30037
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
29578
30038
  "div",
29579
30039
  {
29580
- className: "absolute z-50 w-[32rem] max-h-80 overflow-y-auto rounded-xl border t-border t-bg-surface shadow-xl",
29581
- style: { bottom: "100%", left: 48, marginBottom: 8 },
30040
+ className: "absolute z-50 w-[32rem] max-w-[calc(100vw-2rem)] max-h-80 overflow-y-auto rounded-xl border t-border t-bg-surface shadow-xl",
30041
+ style: { bottom: "100%", left: 0, marginBottom: 8 },
29582
30042
  children: [
29583
30043
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-1.5 border-b t-border flex items-center gap-2 text-xs t-text-secondary", children: [
29584
30044
  /* @__PURE__ */ jsxRuntimeExports.jsx(AtSign, { size: 11 }),
@@ -29671,7 +30131,7 @@ function CommandPopover({ query, commands, onSelect, onClose }) {
29671
30131
  "div",
29672
30132
  {
29673
30133
  className: "absolute z-50 w-72 rounded-xl border t-border t-bg-surface shadow-xl",
29674
- style: { bottom: "100%", left: 48, marginBottom: 8 },
30134
+ style: { bottom: "100%", left: 0, marginBottom: 8 },
29675
30135
  children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-3 text-xs t-text-muted", children: [
29676
30136
  'No matching commands for "/',
29677
30137
  query,
@@ -29684,7 +30144,7 @@ function CommandPopover({ query, commands, onSelect, onClose }) {
29684
30144
  "div",
29685
30145
  {
29686
30146
  className: "absolute z-50 w-80 max-h-64 overflow-y-auto rounded-xl border t-border t-bg-surface shadow-xl",
29687
- style: { bottom: "100%", left: 48, marginBottom: 8 },
30147
+ style: { bottom: "100%", left: 0, marginBottom: 8 },
29688
30148
  children: [
29689
30149
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 border-b t-border flex items-center gap-2 text-xs t-text-secondary", children: [
29690
30150
  /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { size: 12 }),
@@ -29724,7 +30184,7 @@ const SLASH_COMMANDS = [
29724
30184
  { name: "/delete", description: "Delete an entity", args: "<id>" },
29725
30185
  { name: "/help", description: "Show available commands" }
29726
30186
  ];
29727
- const api$2 = window.api;
30187
+ const api$3 = window.api;
29728
30188
  const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
29729
30189
  const MAX_IMAGES = 5;
29730
30190
  const ACCEPTED_DOC_EXTENSIONS = [".pdf", ".csv", ".md", ".txt", ".json", ".xml", ".html", ".docx"];
@@ -29845,7 +30305,7 @@ function ChatInput() {
29845
30305
  const dataUrl = reader.result;
29846
30306
  const base64 = dataUrl.split(",")[1];
29847
30307
  try {
29848
- const result = await api$2.convertFileToText(file.name, base64);
30308
+ const result = await api$3.convertFileToText(file.name, base64);
29849
30309
  if (result?.success && result.content) {
29850
30310
  setPendingFiles((p) => p.map(
29851
30311
  (f) => f.name === file.name ? { ...f, content: result.content, loading: false } : f
@@ -29962,19 +30422,19 @@ ${fileBlocks}`;
29962
30422
  try {
29963
30423
  switch (cmd) {
29964
30424
  case "/notes": {
29965
- const notes = await api$2.listNotes();
30425
+ const notes = await api$3.listNotes();
29966
30426
  result = notes?.length ? `**Notes (${notes.length}):**
29967
30427
  ` + notes.map((n) => `- ${n.title} \`${n.id.slice(0, 8)}\``).join("\n") : "No notes yet.";
29968
30428
  break;
29969
30429
  }
29970
30430
  case "/papers": {
29971
- const papers = await api$2.listLiterature();
30431
+ const papers = await api$3.listLiterature();
29972
30432
  result = papers?.length ? `**Papers (${papers.length}):**
29973
30433
  ` + papers.map((p) => `- ${p.title} \`${p.citeKey}\``).join("\n") : "No papers yet.";
29974
30434
  break;
29975
30435
  }
29976
30436
  case "/data": {
29977
- const data = await api$2.listData();
30437
+ const data = await api$3.listData();
29978
30438
  result = data?.length ? `**Data (${data.length}):**
29979
30439
  ` + data.map((d) => `- ${d.name} \`${d.id.slice(0, 8)}\``).join("\n") : "No data attachments yet.";
29980
30440
  break;
@@ -29984,7 +30444,7 @@ ${fileBlocks}`;
29984
30444
  result = "Usage: `/search <query>`";
29985
30445
  break;
29986
30446
  }
29987
- const results = await api$2.search(rest);
30447
+ const results = await api$3.search(rest);
29988
30448
  result = results?.length ? `**Search results for "${rest}":**
29989
30449
  ` + results.map((r) => `- [${r.type}] ${r.title} \`${r.id.slice(0, 8)}\``).join("\n") : `No results for "${rest}".`;
29990
30450
  break;
@@ -29994,7 +30454,7 @@ ${fileBlocks}`;
29994
30454
  result = "Usage: `/note <title>`";
29995
30455
  break;
29996
30456
  }
29997
- const r = await api$2.artifactCreate({ type: "note", title: rest, content: "" });
30457
+ const r = await api$3.artifactCreate({ type: "note", title: rest, content: "" });
29998
30458
  result = r?.success ? `Note saved: **${rest}**` : `Failed: ${r?.error || "unknown error"}`;
29999
30459
  refreshEntities();
30000
30460
  break;
@@ -30017,7 +30477,7 @@ ${fileBlocks}`;
30017
30477
  const bibtex = flags.bibtex || `@article{${citeKey},
30018
30478
  title = {${cleaned}}
30019
30479
  }`;
30020
- const r = await api$2.artifactCreate({
30480
+ const r = await api$3.artifactCreate({
30021
30481
  type: "paper",
30022
30482
  title: cleaned,
30023
30483
  authors,
@@ -30044,7 +30504,7 @@ ${fileBlocks}`;
30044
30504
  result = "Usage: `/save-data <name> --path <file>`";
30045
30505
  break;
30046
30506
  }
30047
- const r = await api$2.artifactCreate({
30507
+ const r = await api$3.artifactCreate({
30048
30508
  type: "data",
30049
30509
  title: cleaned,
30050
30510
  filePath: flags.path,
@@ -30055,7 +30515,7 @@ ${fileBlocks}`;
30055
30515
  break;
30056
30516
  }
30057
30517
  case "/summary": {
30058
- const summaryResult = await api$2.sessionSummaryGet();
30518
+ const summaryResult = await api$3.sessionSummaryGet();
30059
30519
  if (!summaryResult?.success || !summaryResult?.summary) {
30060
30520
  result = "No session summary available yet.";
30061
30521
  break;
@@ -30082,7 +30542,7 @@ ${s15.openQuestions.map((q2) => `- ${q2}`).join("\n")}`;
30082
30542
  result = "Usage: `/delete <id>`";
30083
30543
  break;
30084
30544
  }
30085
- const r = await api$2.deleteEntity(rest);
30545
+ const r = await api$3.deleteEntity(rest);
30086
30546
  result = r?.success ? `Deleted \`${rest}\`` : `Failed: ${r?.error || "not found"}`;
30087
30547
  refreshEntities();
30088
30548
  break;
@@ -30270,6 +30730,7 @@ ${s15.openQuestions.map((q2) => `- ${q2}`).join("\n")}`;
30270
30730
  onKeyDown: handleKeyDown,
30271
30731
  onPaste: handlePaste,
30272
30732
  placeholder: "Ask anything, attach files, or type /commands...",
30733
+ "aria-label": "Chat message input",
30273
30734
  rows: 1,
30274
30735
  className: "flex-1 bg-transparent text-sm t-text placeholder:t-text-muted resize-none outline-none"
30275
30736
  }
@@ -30461,7 +30922,7 @@ function PaperRow({
30461
30922
  ] })
30462
30923
  ] });
30463
30924
  }
30464
- function CoverageBar({ papers }) {
30925
+ function CoverageBar$1({ papers }) {
30465
30926
  const topicCounts = reactExports.useMemo(() => {
30466
30927
  const counts = /* @__PURE__ */ new Map();
30467
30928
  for (const p of papers) {
@@ -30493,16 +30954,15 @@ function CoverageBar({ papers }) {
30493
30954
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 max-w-48", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 rounded-full t-bg-elevated overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
30494
30955
  "div",
30495
30956
  {
30496
- className: "h-full rounded-full",
30957
+ className: "h-full rounded-full t-gradient-accent-h",
30497
30958
  style: {
30498
- width: `${Math.min(100, highRelevance / Math.max(papers.length, 1) * 100)}%`,
30499
- background: "linear-gradient(90deg, var(--color-accent), var(--color-accent-2))"
30959
+ width: `${Math.min(100, highRelevance / Math.max(papers.length, 1) * 100)}%`
30500
30960
  }
30501
30961
  }
30502
30962
  ) }) })
30503
30963
  ] });
30504
30964
  }
30505
- function FilterBar() {
30965
+ function FilterBar$1() {
30506
30966
  const filter = useUIStore((s15) => s15.literatureFilter);
30507
30967
  const setFilter = useUIStore((s15) => s15.setLiteratureFilter);
30508
30968
  const [showFilters, setShowFilters] = reactExports.useState(false);
@@ -30638,7 +31098,7 @@ function LiteratureView() {
30638
31098
  }, [papers, filter]);
30639
31099
  const hasTopics = papers.some((p) => p.subTopic);
30640
31100
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col min-h-0", children: [
30641
- /* @__PURE__ */ jsxRuntimeExports.jsx(FilterBar, {}),
31101
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FilterBar$1, {}),
30642
31102
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex min-h-0", children: [
30643
31103
  hasTopics && /* @__PURE__ */ jsxRuntimeExports.jsx(
30644
31104
  TopicTree,
@@ -30713,18 +31173,360 @@ function LiteratureView() {
30713
31173
  )) })
30714
31174
  ] })
30715
31175
  ] }),
30716
- /* @__PURE__ */ jsxRuntimeExports.jsx(CoverageBar, { papers: filtered })
31176
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CoverageBar$1, { papers: filtered })
31177
+ ] });
31178
+ }
31179
+ function formatDuration(seconds) {
31180
+ if (!seconds || seconds < 0) return "--";
31181
+ if (seconds < 60) return `${Math.round(seconds)}s`;
31182
+ if (seconds < 3600) {
31183
+ const m2 = Math.floor(seconds / 60);
31184
+ const s15 = Math.round(seconds % 60);
31185
+ return s15 > 0 ? `${m2}m ${s15}s` : `${m2}m`;
31186
+ }
31187
+ const h2 = Math.floor(seconds / 3600);
31188
+ const m = Math.floor(seconds % 3600 / 60);
31189
+ return `${h2}h ${m}m`;
31190
+ }
31191
+ function formatBytes(bytes) {
31192
+ if (!bytes || bytes < 0) return "--";
31193
+ if (bytes < 1024) return `${bytes} B`;
31194
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
31195
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
31196
+ }
31197
+ function timeAgo(iso) {
31198
+ const diff = Date.now() - new Date(iso).getTime();
31199
+ if (diff < 6e4) return "just now";
31200
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
31201
+ if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
31202
+ return `${Math.floor(diff / 864e5)}d ago`;
31203
+ }
31204
+ const STATUS_LABELS = {
31205
+ running: "Running",
31206
+ stalled: "Stalled",
31207
+ completed: "Completed",
31208
+ failed: "Failed",
31209
+ timed_out: "Timed out",
31210
+ cancelled: "Cancelled"
31211
+ };
31212
+ function StatusDot({ status }) {
31213
+ const isActive = status === "running";
31214
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-block w-1.5 h-1.5 rounded-full shrink-0 ${isActive ? "bg-[var(--color-accent)]" : "t-bg-elevated"}`, style: isActive ? {} : { opacity: 0.8 } });
31215
+ }
31216
+ function ProgressBar({ percentage }) {
31217
+ const value = Math.min(100, Math.max(0, percentage || 0));
31218
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1 rounded-full t-bg-elevated overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31219
+ "div",
31220
+ {
31221
+ className: "h-full rounded-full transition-all duration-700 t-gradient-accent-h",
31222
+ style: { width: `${value}%` }
31223
+ }
31224
+ ) });
31225
+ }
31226
+ function RunRow({
31227
+ run,
31228
+ expanded,
31229
+ onToggle
31230
+ }) {
31231
+ const isActive = run.status === "running" || run.status === "stalled";
31232
+ const hasProgress = run.progress?.percentage !== void 0;
31233
+ const setCenterView = useUIStore((s15) => s15.setCenterView);
31234
+ const sendToChat = (text2) => {
31235
+ setCenterView("chat");
31236
+ setTimeout(() => {
31237
+ const inputEl = document.querySelector("[data-chat-input]");
31238
+ if (inputEl) {
31239
+ inputEl.value = text2;
31240
+ inputEl.focus();
31241
+ inputEl.dispatchEvent(new Event("input", { bubbles: true }));
31242
+ }
31243
+ }, 100);
31244
+ };
31245
+ const hasMetrics = run.progress?.metrics && Object.keys(run.progress.metrics).length > 0;
31246
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b t-border last:border-b-0", children: [
31247
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
31248
+ "div",
31249
+ {
31250
+ className: "flex items-center gap-3 px-3 py-2 hover:bg-[var(--color-accent-soft)]/5 transition-colors cursor-pointer",
31251
+ onClick: onToggle,
31252
+ children: [
31253
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "shrink-0 t-text-muted", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 14 }) }),
31254
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusDot, { status: run.status }),
31255
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
31256
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[13px] t-text font-medium truncate leading-tight font-mono", children: run.command }),
31257
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] t-text-muted truncate mt-0.5", children: [
31258
+ run.runId,
31259
+ run.status !== "running" && run.status !== "stalled" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31260
+ " · ",
31261
+ STATUS_LABELS[run.status] ?? run.status
31262
+ ] }),
31263
+ run.stalled && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31264
+ " · ",
31265
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-accent-soft", children: "stalled — no output for a while" })
31266
+ ] }),
31267
+ run.failure && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31268
+ " · ",
31269
+ run.failure.code
31270
+ ] }),
31271
+ run.parentRunId && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: " · retry" })
31272
+ ] })
31273
+ ] }),
31274
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[11px] t-text-muted tabular-nums w-14 text-right", children: formatDuration(run.elapsedSeconds) }),
31275
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[11px] t-text-muted w-14 text-right", children: isActive ? run.currentPhase : run.startedAt ? timeAgo(run.startedAt) : "--" })
31276
+ ]
31277
+ }
31278
+ ),
31279
+ isActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-10 pb-1.5", children: [
31280
+ hasProgress ? /* @__PURE__ */ jsxRuntimeExports.jsx(ProgressBar, { percentage: run.progress.percentage }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1 rounded-full t-bg-elevated overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full w-1/3 rounded-full animate-pulse t-gradient-accent-h opacity-30" }) }),
31281
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-1 text-[10px] t-text-muted", children: [
31282
+ hasProgress && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31283
+ run.progress.percentage,
31284
+ "%"
31285
+ ] }),
31286
+ run.progress?.currentStep !== void 0 && run.progress?.totalSteps !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31287
+ "Step ",
31288
+ run.progress.currentStep,
31289
+ "/",
31290
+ run.progress.totalSteps
31291
+ ] }),
31292
+ run.progress?.phase && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: run.progress.phase }),
31293
+ run.progress?.etaSeconds !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31294
+ "ETA ",
31295
+ formatDuration(run.progress.etaSeconds)
31296
+ ] }),
31297
+ run.startedAt && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31298
+ "started ",
31299
+ timeAgo(run.startedAt)
31300
+ ] }),
31301
+ run.outputLines > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31302
+ run.outputLines,
31303
+ " lines"
31304
+ ] })
31305
+ ] }),
31306
+ hasMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5", children: Object.entries(run.progress.metrics).map(([key, val]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-secondary font-mono", children: [
31307
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-muted", children: key }),
31308
+ " ",
31309
+ typeof val === "number" ? val < 0.01 ? val.toExponential(2) : val.toFixed(4) : val
31310
+ ] }, key)) })
31311
+ ] }),
31312
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-10 pb-3 space-y-2", children: [
31313
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
31314
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-muted", children: run.sandbox }),
31315
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-muted", children: run.weight }),
31316
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-muted", children: [
31317
+ formatBytes(run.outputBytes),
31318
+ " · ",
31319
+ run.outputLines,
31320
+ " lines"
31321
+ ] }),
31322
+ run.exitCode !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-muted", children: [
31323
+ "exit ",
31324
+ run.exitCode
31325
+ ] }),
31326
+ run.startedAt && !isActive && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-muted", children: [
31327
+ "started ",
31328
+ timeAgo(run.startedAt)
31329
+ ] })
31330
+ ] }),
31331
+ !isActive && hasMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1.5", children: Object.entries(run.progress.metrics).map(([key, val]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-1.5 py-0.5 text-[10px] rounded t-bg-elevated t-text-secondary font-mono", children: [
31332
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-muted", children: key }),
31333
+ " ",
31334
+ typeof val === "number" ? val < 0.01 ? val.toExponential(2) : val.toFixed(4) : val
31335
+ ] }, key)) }),
31336
+ run.failure && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs t-text-secondary leading-relaxed", children: [
31337
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "font-medium", children: [
31338
+ run.failure.code,
31339
+ ": ",
31340
+ run.failure.message
31341
+ ] }),
31342
+ run.failure.suggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "mt-1 space-y-0.5", children: run.failure.suggestions.map((s15, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "flex items-start gap-1.5 t-text-muted", children: [
31343
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 mt-1.5 w-1 h-1 rounded-full t-bg-elevated" }),
31344
+ s15
31345
+ ] }, i)) })
31346
+ ] }),
31347
+ run.parentRunId && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[10px] t-text-muted", children: [
31348
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 10 }),
31349
+ "Retry of ",
31350
+ run.parentRunId
31351
+ ] }),
31352
+ run.outputTail && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "p-2 rounded t-bg-elevated text-[10px] t-text-secondary font-mono overflow-x-auto max-h-40 overflow-y-auto whitespace-pre-wrap leading-relaxed", children: run.outputTail.slice(-2048) }),
31353
+ run.failure?.retryable && /* @__PURE__ */ jsxRuntimeExports.jsx(
31354
+ "button",
31355
+ {
31356
+ onClick: (e) => {
31357
+ e.stopPropagation();
31358
+ sendToChat(`Compute run ${run.runId} failed with ${run.failure.code}. Please review the error and fix the code, then retry.`);
31359
+ },
31360
+ className: "text-[10px] t-text-accent hover:underline",
31361
+ children: "Fix & retry in chat"
31362
+ }
31363
+ )
31364
+ ] })
31365
+ ] });
31366
+ }
31367
+ function FilterBar({
31368
+ search: search2,
31369
+ onSearchChange
31370
+ }) {
31371
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-2 border-b t-border", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
31372
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 13, className: "absolute left-2.5 top-1/2 -translate-y-1/2 t-text-muted" }),
31373
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
31374
+ "input",
31375
+ {
31376
+ type: "text",
31377
+ value: search2,
31378
+ onChange: (e) => onSearchChange(e.target.value),
31379
+ placeholder: "Search runs by command or ID...",
31380
+ className: "w-full pl-8 pr-3 py-1.5 text-xs rounded-lg border t-border t-bg-surface t-text focus:outline-none focus:border-[var(--color-accent-soft)]"
31381
+ }
31382
+ ),
31383
+ search2 && /* @__PURE__ */ jsxRuntimeExports.jsx(
31384
+ "button",
31385
+ {
31386
+ onClick: () => onSearchChange(""),
31387
+ className: "absolute right-2 top-1/2 -translate-y-1/2 t-text-muted hover:t-text",
31388
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { size: 12 })
31389
+ }
31390
+ )
31391
+ ] }) });
31392
+ }
31393
+ function CoverageBar({ runs }) {
31394
+ const completed = runs.filter((r) => r.status === "completed").length;
31395
+ const failed = runs.filter((r) => ["failed", "timed_out"].includes(r.status)).length;
31396
+ const total = runs.length;
31397
+ const successRate = total > 0 ? completed / total : 0;
31398
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 px-4 py-2 border-t t-border t-bg-surface text-[11px] t-text-muted", children: [
31399
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31400
+ total,
31401
+ " runs"
31402
+ ] }),
31403
+ completed > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31404
+ completed,
31405
+ " completed"
31406
+ ] }),
31407
+ failed > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31408
+ failed,
31409
+ " failed"
31410
+ ] }),
31411
+ total > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31412
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 max-w-48", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1.5 rounded-full t-bg-elevated overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31413
+ "div",
31414
+ {
31415
+ className: "h-full rounded-full t-gradient-accent-h",
31416
+ style: { width: `${successRate * 100}%` }
31417
+ }
31418
+ ) }) }),
31419
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
31420
+ Math.round(successRate * 100),
31421
+ "% success"
31422
+ ] })
31423
+ ] })
31424
+ ] });
31425
+ }
31426
+ function EmptyState() {
31427
+ const environment = useComputeStore((s15) => s15.environment);
31428
+ const setCenterView = useUIStore((s15) => s15.setCenterView);
31429
+ const goToChat = (text2) => {
31430
+ setCenterView("chat");
31431
+ setTimeout(() => {
31432
+ const inputEl = document.querySelector("[data-chat-input]");
31433
+ if (inputEl) {
31434
+ inputEl.value = text2;
31435
+ inputEl.focus();
31436
+ inputEl.dispatchEvent(new Event("input", { bubbles: true }));
31437
+ }
31438
+ }, 100);
31439
+ };
31440
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center h-full text-center px-8", children: [
31441
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 32, className: "t-text-muted mb-3 opacity-30" }),
31442
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm t-text font-medium mb-1", children: "Your compute environment is ready" }),
31443
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs t-text-muted max-w-sm leading-relaxed mb-4", children: "Ask the agent to run scripts, train models, or process data. Code executes in a sandboxed environment with progress tracking and failure analysis." }),
31444
+ environment && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-2.5 rounded-lg t-bg-elevated text-[11px] t-text-secondary mb-5", children: [
31445
+ environment.gpu || `${environment.os} ${environment.arch}`,
31446
+ " · ",
31447
+ Math.round(environment.totalMemoryMb / 1024),
31448
+ " GB",
31449
+ environment.mlxAvailable && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31450
+ " · ",
31451
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "t-text-accent", children: "MLX" })
31452
+ ] }),
31453
+ environment.sandbox === "docker" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: " · Docker" })
31454
+ ] }),
31455
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1.5 w-full max-w-sm", children: [
31456
+ "Train a model on my dataset",
31457
+ "Run my analysis script",
31458
+ "Process and clean this data"
31459
+ ].map((prompt) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
31460
+ "button",
31461
+ {
31462
+ onClick: () => goToChat(prompt),
31463
+ className: "w-full flex items-center justify-between px-3 py-2 rounded-lg border t-border-subtle hover:bg-[var(--color-accent-soft)]/5 transition-colors text-left",
31464
+ children: [
31465
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs t-text-secondary", children: [
31466
+ '"',
31467
+ prompt,
31468
+ '"'
31469
+ ] }),
31470
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { size: 12, className: "t-text-muted" })
31471
+ ]
31472
+ },
31473
+ prompt
31474
+ )) })
31475
+ ] });
31476
+ }
31477
+ function ComputeView() {
31478
+ const activeRuns = useActiveRuns();
31479
+ const recentRuns = useRecentRuns();
31480
+ const [expandedId, setExpandedId] = reactExports.useState(null);
31481
+ const [search2, setSearch] = reactExports.useState("");
31482
+ const allRuns = reactExports.useMemo(() => [...activeRuns, ...recentRuns], [activeRuns, recentRuns]);
31483
+ const filtered = reactExports.useMemo(() => {
31484
+ if (!search2.trim()) return allRuns;
31485
+ const q2 = search2.toLowerCase();
31486
+ return allRuns.filter(
31487
+ (r) => r.command.toLowerCase().includes(q2) || r.runId.toLowerCase().includes(q2)
31488
+ );
31489
+ }, [allRuns, search2]);
31490
+ const isEmpty = allRuns.length === 0;
31491
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col min-h-0", children: [
31492
+ !isEmpty && /* @__PURE__ */ jsxRuntimeExports.jsx(FilterBar, { search: search2, onSearchChange: setSearch }),
31493
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto", children: isEmpty ? /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, {}) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center h-full text-center px-8", children: [
31494
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { size: 32, className: "t-text-muted mb-3 opacity-40" }),
31495
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm t-text-muted", children: "No runs match your search." })
31496
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
31497
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-3 py-1.5 border-b t-border t-bg-surface", children: [
31498
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5" }),
31499
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-1.5" }),
31500
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 text-[10px] uppercase tracking-wider font-medium t-text-muted", children: "Command" }),
31501
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-14 text-right text-[10px] uppercase tracking-wider font-medium t-text-muted", children: "Duration" }),
31502
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-14 text-right text-[10px] uppercase tracking-wider font-medium t-text-muted", children: "Phase" })
31503
+ ] }),
31504
+ filtered.map((run) => /* @__PURE__ */ jsxRuntimeExports.jsx(
31505
+ RunRow,
31506
+ {
31507
+ run,
31508
+ expanded: expandedId === run.runId,
31509
+ onToggle: () => setExpandedId(expandedId === run.runId ? null : run.runId)
31510
+ },
31511
+ run.runId
31512
+ ))
31513
+ ] }) }),
31514
+ !isEmpty && /* @__PURE__ */ jsxRuntimeExports.jsx(CoverageBar, { runs: allRuns })
30717
31515
  ] });
30718
31516
  }
31517
+ const api$2 = window.api;
31518
+ const computeEnabled = api$2?.isComputeEnabled?.() ?? false;
30719
31519
  const viewTabs = [
30720
31520
  { key: "chat", label: "Chat", icon: MessageSquare, shortcut: "⌘1" },
30721
- { key: "literature", label: "Literature", icon: BookOpen, shortcut: "⌘2" }
31521
+ { key: "literature", label: "Literature", icon: BookOpen, shortcut: "⌘2" },
31522
+ ...computeEnabled ? [{ key: "compute", label: "Compute", icon: Cpu, shortcut: "⌘3" }] : []
30722
31523
  ];
30723
31524
  function ViewSwitcher() {
30724
31525
  const centerView = useUIStore((s15) => s15.centerView);
30725
31526
  const setCenterView = useUIStore((s15) => s15.setCenterView);
30726
31527
  const paperCount = useEntityStore((s15) => s15.papers.length);
30727
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-0.5 px-4 pt-10 pb-1", children: viewTabs.map(({ key, label, icon: Icon2, shortcut }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
31528
+ const activeComputeRuns = useActiveRunCount();
31529
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("nav", { "aria-label": "View switcher", className: "flex items-center gap-0.5 px-4 pt-10 pb-1", children: viewTabs.map(({ key, label, icon: Icon2, shortcut }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
30728
31530
  "button",
30729
31531
  {
30730
31532
  onClick: () => setCenterView(key),
@@ -30733,7 +31535,9 @@ function ViewSwitcher() {
30733
31535
  children: [
30734
31536
  /* @__PURE__ */ jsxRuntimeExports.jsx(Icon2, { size: 13 }),
30735
31537
  label,
30736
- key === "literature" && paperCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-0.5 px-1 py-px text-[9px] rounded-full t-bg-elevated t-text-muted tabular-nums", children: paperCount })
31538
+ key === "literature" && paperCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-0.5 px-1 py-px text-[9px] rounded-full t-bg-elevated t-text-muted tabular-nums", children: paperCount }),
31539
+ key === "compute" && activeComputeRuns > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-0.5 px-1 py-px text-[9px] rounded-full bg-[var(--color-accent-soft)]/10 t-text-accent tabular-nums", children: activeComputeRuns }),
31540
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] t-text-muted opacity-40 ml-0.5", children: shortcut })
30737
31541
  ]
30738
31542
  },
30739
31543
  key
@@ -30745,12 +31549,18 @@ function CenterPanel() {
30745
31549
  const messages = useChatStore((s15) => s15.messages);
30746
31550
  const showHero = isIdle && messages.length === 0;
30747
31551
  if (centerView === "literature") {
30748
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "flex-1 flex flex-col min-w-0", children: [
31552
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { id: "main-content", className: "flex-1 flex flex-col min-w-0", children: [
30749
31553
  /* @__PURE__ */ jsxRuntimeExports.jsx(ViewSwitcher, {}),
30750
31554
  /* @__PURE__ */ jsxRuntimeExports.jsx(LiteratureView, {})
30751
31555
  ] });
30752
31556
  }
30753
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "flex-1 flex flex-col min-w-0", children: [
31557
+ if (centerView === "compute") {
31558
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { id: "main-content", className: "flex-1 flex flex-col min-w-0", children: [
31559
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ViewSwitcher, {}),
31560
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ComputeView, {})
31561
+ ] });
31562
+ }
31563
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { id: "main-content", className: "flex-1 flex flex-col min-w-0", children: [
30754
31564
  /* @__PURE__ */ jsxRuntimeExports.jsx(ViewSwitcher, {}),
30755
31565
  showHero ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(HeroIdle, {}) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 px-6 pt-4 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto h-full", style: { maxWidth: "48rem" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChatMessages, {}) }) }),
30756
31566
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-6 pb-5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto", style: { maxWidth: "48rem" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChatInput, {}) }) })
@@ -30758,7 +31568,7 @@ function CenterPanel() {
30758
31568
  }
30759
31569
  const remarkPlugins = [remarkGfm];
30760
31570
  const LazyMilkdownMarkdownEditor = reactExports.lazy(async () => {
30761
- const mod = await __vitePreload(() => import("./MilkdownMarkdownEditor-D7GYpVZn.js").then((n) => n.bL), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
31571
+ const mod = await __vitePreload(() => import("./MilkdownMarkdownEditor-DjFLwyh4.js").then((n) => n.bL), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
30762
31572
  return { default: mod.MilkdownMarkdownEditor };
30763
31573
  });
30764
31574
  const typeIcons = {
@@ -31383,7 +32193,7 @@ function StatusBar() {
31383
32193
  const hasSkills = activeSkills.length > 0;
31384
32194
  const hasRunUsage = runTokens > 0;
31385
32195
  const hasProjectUsage = allTimeTokens > 0;
31386
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-7 flex items-center px-4 gap-5 border-t t-border t-bg-surface text-[11px] t-text-muted select-none shrink-0", children: [
32196
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-7 flex items-center px-4 gap-5 border-t t-border t-bg-surface text-[11px] t-text-secondary select-none shrink-0", children: [
31387
32197
  hasSkills && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 overflow-hidden", children: activeSkills.map((name2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 px-1.5 py-0.5 rounded t-bg-accent/10 t-text-accent whitespace-nowrap", children: [
31388
32198
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px]", children: "⚡" }),
31389
32199
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: name2 })
@@ -40007,22 +40817,17 @@ function TerminalPanel() {
40007
40817
  };
40008
40818
  reactExports.useEffect(() => {
40009
40819
  if (!termRef.current) return;
40010
- const isDark = theme === "dark";
40820
+ const cs2 = getComputedStyle(document.documentElement);
40011
40821
  const term = new Dl({
40012
40822
  fontSize: 13,
40013
40823
  fontFamily: 'Menlo, Monaco, "Courier New", monospace',
40014
40824
  cursorBlink: true,
40015
40825
  allowProposedApi: true,
40016
- theme: isDark ? {
40017
- background: "#1a1a1a",
40018
- foreground: "#e0e0e0",
40019
- cursor: "#e0e0e0",
40020
- selectionBackground: "#3a3a5a"
40021
- } : {
40022
- background: "#fafafa",
40023
- foreground: "#1a1a1a",
40024
- cursor: "#1a1a1a",
40025
- selectionBackground: "#c0d0e0"
40826
+ theme: {
40827
+ background: cs2.getPropertyValue("--color-bg-base").trim(),
40828
+ foreground: cs2.getPropertyValue("--color-text").trim(),
40829
+ cursor: cs2.getPropertyValue("--color-text").trim(),
40830
+ selectionBackground: cs2.getPropertyValue("--color-bg-elevated").trim()
40026
40831
  }
40027
40832
  });
40028
40833
  const fit = new o();
@@ -40079,17 +40884,12 @@ function TerminalPanel() {
40079
40884
  reactExports.useEffect(() => {
40080
40885
  const term = xtermRef.current;
40081
40886
  if (!term) return;
40082
- const isDark = theme === "dark";
40083
- term.options.theme = isDark ? {
40084
- background: "#1a1a1a",
40085
- foreground: "#e0e0e0",
40086
- cursor: "#e0e0e0",
40087
- selectionBackground: "#3a3a5a"
40088
- } : {
40089
- background: "#fafafa",
40090
- foreground: "#1a1a1a",
40091
- cursor: "#1a1a1a",
40092
- selectionBackground: "#c0d0e0"
40887
+ const cs2 = getComputedStyle(document.documentElement);
40888
+ term.options.theme = {
40889
+ background: cs2.getPropertyValue("--color-bg-base").trim(),
40890
+ foreground: cs2.getPropertyValue("--color-text").trim(),
40891
+ cursor: cs2.getPropertyValue("--color-text").trim(),
40892
+ selectionBackground: cs2.getPropertyValue("--color-bg-elevated").trim()
40093
40893
  };
40094
40894
  }, [theme]);
40095
40895
  reactExports.useEffect(() => {
@@ -40116,8 +40916,9 @@ function TerminalPanel() {
40116
40916
  "button",
40117
40917
  {
40118
40918
  onClick: handleRestart,
40119
- className: "p-0.5 rounded t-text-muted hover:t-text-secondary t-bg-hover",
40919
+ className: "p-1 rounded t-text-muted hover:t-text-secondary t-bg-hover",
40120
40920
  title: "Restart terminal",
40921
+ "aria-label": "Restart terminal",
40121
40922
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 12 })
40122
40923
  }
40123
40924
  ),
@@ -40125,8 +40926,9 @@ function TerminalPanel() {
40125
40926
  "button",
40126
40927
  {
40127
40928
  onClick: handleClose,
40128
- className: "p-0.5 rounded t-text-muted hover:t-text-secondary t-bg-hover",
40929
+ className: "p-1 rounded t-text-muted hover:t-text-secondary t-bg-hover",
40129
40930
  title: "Close terminal",
40931
+ "aria-label": "Close terminal",
40130
40932
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(X$1, { size: 12 })
40131
40933
  }
40132
40934
  )
@@ -40227,19 +41029,14 @@ function FolderGate({ onOpenSettings }) {
40227
41029
  /* @__PURE__ */ jsxRuntimeExports.jsx(
40228
41030
  "div",
40229
41031
  {
40230
- className: "w-14 h-14 rounded-2xl flex items-center justify-center",
40231
- style: {
40232
- background: "linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-2) 100%)",
40233
- boxShadow: "0 8px 32px var(--color-accent-2-muted)"
40234
- },
41032
+ className: "w-14 h-14 rounded-2xl flex items-center justify-center t-gradient-accent t-gradient-accent-shadow-lg",
40235
41033
  children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-white text-xl font-bold tracking-tight", children: "P" })
40236
41034
  }
40237
41035
  ),
40238
41036
  /* @__PURE__ */ jsxRuntimeExports.jsx(
40239
41037
  "div",
40240
41038
  {
40241
- className: "absolute -inset-2 rounded-3xl opacity-15 blur-xl -z-10",
40242
- style: { background: "linear-gradient(135deg, var(--color-accent), var(--color-accent-2))" }
41039
+ className: "absolute -inset-2 rounded-3xl opacity-15 blur-xl -z-10 t-gradient-accent"
40243
41040
  }
40244
41041
  )
40245
41042
  ] }),
@@ -40260,11 +41057,7 @@ function FolderGate({ onOpenSettings }) {
40260
41057
  "button",
40261
41058
  {
40262
41059
  onClick: handlePick,
40263
- className: "inline-flex items-center gap-2 px-5 py-2.5 rounded-lg text-white text-sm font-medium\n hover:opacity-90 transition-all duration-200",
40264
- style: {
40265
- background: "linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-2) 100%)",
40266
- boxShadow: "0 4px 16px var(--color-accent-2-muted)"
40267
- },
41060
+ className: "inline-flex items-center gap-2 px-5 py-2.5 rounded-lg text-white text-sm font-medium\n hover:opacity-90 transition-all duration-200 t-gradient-accent t-gradient-accent-shadow",
40268
41061
  children: [
40269
41062
  /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { size: 16 }),
40270
41063
  "Open Project Folder"
@@ -40423,6 +41216,19 @@ function App() {
40423
41216
  const unsub6 = api.onEntityCreated(() => {
40424
41217
  refreshEntities();
40425
41218
  });
41219
+ if (api?.isComputeEnabled?.()) {
41220
+ api.probeComputeEnvironment?.().catch(() => {
41221
+ });
41222
+ }
41223
+ const unsubComputeUpdate = api.onComputeRunUpdate((event) => {
41224
+ useComputeStore.getState().updateRun(event.runId, event);
41225
+ });
41226
+ const unsubComputeComplete = api.onComputeRunComplete((event) => {
41227
+ useComputeStore.getState().updateRun(event.runId, event);
41228
+ });
41229
+ const unsubComputeEnv = api.onComputeEnvironment((event) => {
41230
+ useComputeStore.getState().setEnvironment(event);
41231
+ });
40426
41232
  return () => {
40427
41233
  unsub1();
40428
41234
  unsub2();
@@ -40435,6 +41241,9 @@ function App() {
40435
41241
  unsubToolProgress();
40436
41242
  unsubSkillLoaded();
40437
41243
  unsubUsage();
41244
+ unsubComputeUpdate();
41245
+ unsubComputeComplete();
41246
+ unsubComputeEnv();
40438
41247
  };
40439
41248
  }, [hasProject]);
40440
41249
  reactExports.useEffect(() => {
@@ -40446,12 +41255,6 @@ function App() {
40446
41255
  reactExports.useEffect(() => {
40447
41256
  const handler = (e) => {
40448
41257
  if (previewEditorFocused) return;
40449
- if ((e.metaKey || e.ctrlKey) && e.key === "n") {
40450
- e.preventDefault();
40451
- useChatStore.getState().clear();
40452
- useUIStore.getState().setIdle(true);
40453
- useUIStore.getState().closePreview();
40454
- }
40455
41258
  if ((e.metaKey || e.ctrlKey) && e.key === "1") {
40456
41259
  e.preventDefault();
40457
41260
  useUIStore.getState().setCenterView("chat");
@@ -40460,6 +41263,10 @@ function App() {
40460
41263
  e.preventDefault();
40461
41264
  useUIStore.getState().setCenterView("literature");
40462
41265
  }
41266
+ if ((e.metaKey || e.ctrlKey) && e.key === "3" && api?.isComputeEnabled?.()) {
41267
+ e.preventDefault();
41268
+ useUIStore.getState().setCenterView("compute");
41269
+ }
40463
41270
  if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "K") {
40464
41271
  e.preventDefault();
40465
41272
  if (useChatStore.getState().isStreaming) {
@@ -40491,6 +41298,7 @@ function App() {
40491
41298
  return /* @__PURE__ */ jsxRuntimeExports.jsx(FolderGate, { onOpenSettings: () => setShowApiKeySetup(true) });
40492
41299
  }
40493
41300
  return /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-screen w-screen t-bg-base t-text", children: [
41301
+ /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "#main-content", className: "skip-link", children: "Skip to content" }),
40494
41302
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "drag-region fixed top-0 left-0 right-0 h-8 z-50" }),
40495
41303
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 min-h-0", children: [
40496
41304
  !leftCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: previewEntity ? "hidden" : "contents", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LeftSidebar, {}) }),