@pablopunk/webr 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { S as createAstro, f as renderHead, i as renderComponent, m as createRenderInstruction, p as addAttribute, s as renderSlot, u as renderTemplate } from "./server_CnGYDmxV.mjs";
2
2
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
3
3
  import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
4
- import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Copy, Cpu, Folder, FolderTree, GitBranch, ImagePlus, LayoutList, LoaderCircle, MessageCircleQuestion, Monitor, Moon, PanelBottom, PanelLeft, PanelLeftClose, PanelLeftOpen, PanelRight, Plus, Search, Server, Settings2, SquarePen, SquareTerminal, Sun, TerminalSquare, Trash2, TriangleAlert, X } from "lucide-react";
4
+ import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Copy, Cpu, Folder, FolderTree, GitBranch, ImagePlus, LayoutList, LoaderCircle, MessageCircleQuestion, Monitor, Moon, PanelBottom, PanelLeft, PanelLeftClose, PanelLeftOpen, PanelRight, Pencil, Plus, Search, Server, Settings2, SquarePen, SquareTerminal, Sun, TerminalSquare, Trash2, TriangleAlert, X } from "lucide-react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { AlertDialog } from "@base-ui/react/alert-dialog";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -191,7 +191,7 @@ var defaultShortcuts = {
191
191
  previousThread: "ctrl+k",
192
192
  nextPane: "ctrl+l",
193
193
  previousPane: "ctrl+h",
194
- toggleSidebar: "ctrl+b",
194
+ toggleSidebar: "meta+b",
195
195
  newThread: "ctrl+x",
196
196
  toggleTerminal: "ctrl+q"
197
197
  };
@@ -330,6 +330,11 @@ var renameThread = (thread, title) => post$1(`/api/threads/${encodeURIComponent(
330
330
  machineId: thread.machineId,
331
331
  title
332
332
  });
333
+ var renameProject = (project, name) => post$1("/api/projects/rename", {
334
+ machineId: project.machineId ?? "local",
335
+ logicalId: project.logicalId ?? project.id,
336
+ name
337
+ });
333
338
  var toggleThreadTerminal = (thread, direction) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/terminal`, {
334
339
  machineId: thread.machineId,
335
340
  direction
@@ -2259,6 +2264,39 @@ function ThreadTitleEditor({ thread, onDone, onError }) {
2259
2264
  });
2260
2265
  }
2261
2266
  //#endregion
2267
+ //#region src/components/ProjectNameEditor.tsx
2268
+ function ProjectNameEditor({ project, onDone, onError }) {
2269
+ const [name, setName] = useState(project.name);
2270
+ const input = useRef(null);
2271
+ const finished = useRef(false);
2272
+ useEffect(() => {
2273
+ input.current?.focus();
2274
+ input.current?.select();
2275
+ }, []);
2276
+ const finish = (save) => {
2277
+ if (finished.current) return;
2278
+ finished.current = true;
2279
+ onDone();
2280
+ const next = name.trim();
2281
+ if (save && next && next !== project.name) renameProject(project, next).catch(onError);
2282
+ };
2283
+ return /* @__PURE__ */ jsx("input", {
2284
+ ref: input,
2285
+ className: "thread-name-input group-name-input",
2286
+ "aria-label": `Rename ${project.name}`,
2287
+ value: name,
2288
+ maxLength: 90,
2289
+ onChange: (event) => setName(event.target.value),
2290
+ onBlur: () => finish(true),
2291
+ onClick: (event) => event.stopPropagation(),
2292
+ onKeyDown: (event) => {
2293
+ event.stopPropagation();
2294
+ if (event.key === "Enter") finish(true);
2295
+ if (event.key === "Escape") finish(false);
2296
+ }
2297
+ });
2298
+ }
2299
+ //#endregion
2262
2300
  //#region src/components/useFlipList.ts
2263
2301
  var durationMs = 220;
2264
2302
  var itemSelector = "[data-flip-id]";
@@ -2304,6 +2342,10 @@ function groupProjectLocations(projects, currentId) {
2304
2342
  return [...groups.values()];
2305
2343
  }
2306
2344
  //#endregion
2345
+ //#region src/client/home-path.ts
2346
+ var homePrefix = /^\/(?:Users|home)\/[^/]+/;
2347
+ var withTildeHome = (path) => path.replace(homePrefix, "~");
2348
+ //#endregion
2307
2349
  //#region src/components/Sidebar.tsx
2308
2350
  var statusLabel = {
2309
2351
  working: "Working",
@@ -2433,6 +2475,63 @@ function ThreadLink({ thread, project, machineName, current, showProject, editor
2433
2475
  })
2434
2476
  });
2435
2477
  }
2478
+ function ProjectGroupHeader({ project, closed, onToggle }) {
2479
+ const [renaming, setRenaming] = useState(false);
2480
+ const Title = renaming ? "div" : "button";
2481
+ const label = renaming ? /* @__PURE__ */ jsx(ProjectNameEditor, {
2482
+ project,
2483
+ onDone: () => setRenaming(false),
2484
+ onError: reportFailure
2485
+ }) : /* @__PURE__ */ jsx("span", {
2486
+ className: "group-name",
2487
+ children: project.name
2488
+ });
2489
+ return /* @__PURE__ */ jsxs(ContextMenu.Root, { children: [/* @__PURE__ */ jsx(ContextMenu.Trigger, {
2490
+ className: "group-header",
2491
+ children: /* @__PURE__ */ jsxs(Title, {
2492
+ className: "group-title",
2493
+ onClick: renaming ? void 0 : onToggle,
2494
+ "aria-expanded": renaming ? void 0 : !closed,
2495
+ children: [
2496
+ /* @__PURE__ */ jsx("span", {
2497
+ className: "group-chevron",
2498
+ "aria-hidden": "true",
2499
+ children: closed ? /* @__PURE__ */ jsx(ChevronRight, { size: 14 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 14 })
2500
+ }),
2501
+ /* @__PURE__ */ jsx(ProjectIcon, { project }),
2502
+ label
2503
+ ]
2504
+ })
2505
+ }), /* @__PURE__ */ jsx(ContextMenu.Portal, { children: /* @__PURE__ */ jsx(ContextMenu.Positioner, {
2506
+ className: "thread-menu-positioner",
2507
+ children: /* @__PURE__ */ jsxs(ContextMenu.Popup, {
2508
+ className: "thread-menu",
2509
+ children: [
2510
+ /* @__PURE__ */ jsxs(ContextMenu.Item, {
2511
+ className: "thread-menu-item",
2512
+ onClick: () => void navigate(`/new?project=${encodeURIComponent(project.id)}`),
2513
+ children: [/* @__PURE__ */ jsx(Plus, {
2514
+ size: 14,
2515
+ "aria-hidden": "true"
2516
+ }), "New thread"]
2517
+ }),
2518
+ /* @__PURE__ */ jsxs(ContextMenu.Item, {
2519
+ className: "thread-menu-item",
2520
+ onClick: () => setRenaming(true),
2521
+ children: [/* @__PURE__ */ jsx(Pencil, {
2522
+ size: 14,
2523
+ "aria-hidden": "true"
2524
+ }), "Rename project"]
2525
+ }),
2526
+ /* @__PURE__ */ jsx("div", {
2527
+ className: "thread-menu-path",
2528
+ title: project.path,
2529
+ children: withTildeHome(project.path)
2530
+ })
2531
+ ]
2532
+ })
2533
+ }) })] });
2534
+ }
2436
2535
  function ArchivedThreads({ threads, renderThread }) {
2437
2536
  const [open, setOpen] = useState(false);
2438
2537
  const [deleting, setDeleting] = useState(false);
@@ -2597,36 +2696,10 @@ function Sidebar({ projects, threads, archived = [], machines, currentId, mode,
2597
2696
  "aria-label": "Agent threads",
2598
2697
  children: [mode === "projects" ? projectGroups.filter((group) => threads.some((thread) => group.locations.includes(thread.projectId))).sort((a, b) => (threads.find((thread) => b.locations.includes(thread.projectId))?.updatedAt ?? "").localeCompare(threads.find((thread) => a.locations.includes(thread.projectId))?.updatedAt ?? "")).map(({ id, project, locations }) => /* @__PURE__ */ jsxs("section", {
2599
2698
  className: "thread-group",
2600
- children: [/* @__PURE__ */ jsxs("div", {
2601
- className: "group-header",
2602
- children: [/* @__PURE__ */ jsxs("button", {
2603
- className: "group-title",
2604
- onClick: () => setClosedProjects((value) => value.includes(id) ? value.filter((item) => item !== id) : [...value, id]),
2605
- "aria-expanded": !closedProjects.includes(id),
2606
- children: [
2607
- /* @__PURE__ */ jsx("span", {
2608
- className: "group-chevron",
2609
- "aria-hidden": "true",
2610
- children: closedProjects.includes(id) ? /* @__PURE__ */ jsx(ChevronRight, { size: 14 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 14 })
2611
- }),
2612
- /* @__PURE__ */ jsx(ProjectIcon, { project }),
2613
- /* @__PURE__ */ jsx("span", {
2614
- className: "group-name",
2615
- children: project.name
2616
- })
2617
- ]
2618
- }), /* @__PURE__ */ jsx(Tooltip$1, {
2619
- label: `New thread in ${project.name}`,
2620
- children: /* @__PURE__ */ jsx("a", {
2621
- className: "group-new-thread",
2622
- href: `/new?project=${encodeURIComponent(project.id)}`,
2623
- "aria-label": `New thread in ${project.name}`,
2624
- children: /* @__PURE__ */ jsx(SquarePen, {
2625
- size: 15,
2626
- "aria-hidden": "true"
2627
- })
2628
- })
2629
- })]
2699
+ children: [/* @__PURE__ */ jsx(ProjectGroupHeader, {
2700
+ project,
2701
+ closed: closedProjects.includes(id),
2702
+ onToggle: () => setClosedProjects((value) => value.includes(id) ? value.filter((item) => item !== id) : [...value, id])
2630
2703
  }), !closedProjects.includes(id) && threads.filter((thread) => locations.includes(thread.projectId)).map((thread) => threadLink(thread, false))]
2631
2704
  }, id)) : threads.map((thread) => threadLink(thread, true)), !threads.length && !archived.length && /* @__PURE__ */ jsx("p", {
2632
2705
  className: "sidebar-empty",
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_CmESMUHE.mjs";
4
+ import { n as $$Base, t as App } from "./App_C1XnVbA2.mjs";
5
5
  //#region src/pages/threads/[id].astro
6
6
  var _id__exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$Id,
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_CmESMUHE.mjs";
4
+ import { n as $$Base, t as App } from "./App_C1XnVbA2.mjs";
5
5
  //#region src/pages/sessions/[session]/tabs/[tab].astro
6
6
  var _tab__exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$Tab,
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_CmESMUHE.mjs";
4
+ import { n as $$Base, t as App } from "./App_C1XnVbA2.mjs";
5
5
  //#region src/pages/new.astro
6
6
  var new_exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$New,
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_CmESMUHE.mjs";
4
+ import { n as $$Base, t as App } from "./App_C1XnVbA2.mjs";
5
5
  //#region src/pages/settings.astro
6
6
  var settings_exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$Settings,
@@ -2427,10 +2427,10 @@ var renderers = [Object.assign({
2427
2427
  //#endregion
2428
2428
  //#region \0virtual:astro:pages
2429
2429
  var _page0 = () => import("./chunks/node_BcIF-BVp.mjs").then((n) => n.t);
2430
- var _page1 = () => import("./chunks/new_NXgn74q9.mjs");
2431
- var _page2 = () => import("./chunks/_tab__D-smYrCo.mjs");
2432
- var _page3 = () => import("./chunks/settings_D1jcCWzt.mjs");
2433
- var _page4 = () => import("./chunks/_id__CQzMt2xL.mjs");
2430
+ var _page1 = () => import("./chunks/new_BfRTqcQ4.mjs");
2431
+ var _page2 = () => import("./chunks/_tab__BJ-ULrm_.mjs");
2432
+ var _page3 = () => import("./chunks/settings_BD_xdLnR.mjs");
2433
+ var _page4 = () => import("./chunks/_id__CAx6DC51.mjs");
2434
2434
  var _page5 = () => import("./chunks/index_DRfLd22k.mjs");
2435
2435
  var pageMap = /* @__PURE__ */ new Map([
2436
2436
  ["node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js", _page0],
@@ -2442,7 +2442,7 @@ var pageMap = /* @__PURE__ */ new Map([
2442
2442
  ]);
2443
2443
  //#endregion
2444
2444
  //#region \0virtual:astro:manifest
2445
- var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id__CQzMt2xL.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__D-smYrCo.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/sharp@0.35.5_@types+node@22.20.4/node_modules/sharp/dist/index.mjs":"chunks/dist_CpTFMtqT.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_DRfLd22k.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_NXgn74q9.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_BcIF-BVp.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_D1jcCWzt.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_qIaxzsTp.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.B3aX6cef.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.B3aX6cef.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.1PYvyCIo.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"kpOW6gtP/BJ7iqBQDQqpBOrd4J4biYexNq45sJL0wTE=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2445
+ var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.DijFEuO0.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.DijFEuO0.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.DijFEuO0.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.DijFEuO0.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id__CAx6DC51.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__BJ-ULrm_.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/sharp@0.35.5_@types+node@22.20.4/node_modules/sharp/dist/index.mjs":"chunks/dist_CpTFMtqT.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_DRfLd22k.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_BfRTqcQ4.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_BcIF-BVp.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_BD_xdLnR.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_qIaxzsTp.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.Z2LzeAW7.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.Z2LzeAW7.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.DijFEuO0.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"SdLQO/gzpqcLAntfdRcrf7WtBDiQy1OYy4Y4ew2Wto8=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2446
2446
  var manifestRoutes = _manifest.routes;
2447
2447
  var manifest = Object.assign(_manifest, {
2448
2448
  renderers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pablopunk/webr",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Control your Herdr agents from anywhere.",
5
5
  "keywords": [
6
6
  "herdr",
package/server/cli.ts CHANGED
@@ -8,6 +8,7 @@ import { defaultDeps, installPlugin, pluginStatus, uninstallWebrPlugin, updatePl
8
8
  import { checkHerdr } from './herdr-check';
9
9
  import { modeLines, pluginMode } from './plugin/mode';
10
10
  import { openWebr, statusLines } from './where';
11
+ import { isPortOpen } from './plugin/probe';
11
12
  import { packageInfo } from './package-info';
12
13
  import { realUpdateEnvironment, runUpdate } from './update';
13
14
  import { refreshLatestVersionCache, updateHint } from './update/hint';
@@ -16,7 +17,7 @@ const HELP = `Webr — control your Herdr agents from anywhere.
16
17
 
17
18
  Usage
18
19
  webr [start] [options] Run the server in this terminal
19
- webr plugin install [options] Start Webr whenever the Herdr server starts
20
+ webr plugin install [options] Start Webr whenever the Herdr server starts, and open it
20
21
  webr plugin uninstall Remove the Herdr plugin
21
22
  webr plugin status Show whether the plugin is installed and Webr is running
22
23
  webr invite [--port <port>] Print a one-time code and QR to connect a device
@@ -27,6 +28,7 @@ Usage
27
28
  Options
28
29
  --port <port> Port to listen on (default: first free port from 4444, then remembered)
29
30
  --host <address> Listen on a specific address (default 0.0.0.0, every interface)
31
+ --no-open Don't open the browser after "plugin install"
30
32
  --origin <url> Public URL when served through a proxy, e.g. https://mac.tailnet.ts.net
31
33
  -v, --version Print the version
32
34
  -h, --help Print this help
@@ -36,7 +38,7 @@ Localhost never needs a token. Any other device asks for access and you approve
36
38
  `;
37
39
 
38
40
  const options = {
39
- port: { type: 'string' }, host: { type: 'string' }, origin: { type: 'string' },
41
+ port: { type: 'string' }, host: { type: 'string' }, origin: { type: 'string' }, 'no-open': { type: 'boolean' },
40
42
  help: { type: 'boolean', short: 'h' }, version: { type: 'boolean', short: 'v' },
41
43
  } as const;
42
44
 
@@ -80,6 +82,14 @@ async function pluginCommand(action: string, values: ReturnType<typeof parseComm
80
82
  const deps = defaultDeps();
81
83
  const lines = action === 'install' ? await installPlugin(pluginSettings(values), deps) : action === 'uninstall' ? await uninstallWebrPlugin(deps) : await pluginStatus(deps);
82
84
  for (const line of lines.filter(Boolean)) console.log(line);
85
+ if (action === 'install' && !values['no-open']) await openWhenUp(knownPort(values.port ? ['--port', values.port] : []));
86
+ }
87
+
88
+ async function openWhenUp(port: number) {
89
+ for (let attempt = 0; attempt < 30; attempt++) {
90
+ if (await isPortOpen({ host: '127.0.0.1', port })) return void (await openWebr(port)).forEach((line) => console.log(line));
91
+ await new Promise((resolve) => setTimeout(resolve, 500));
92
+ }
83
93
  }
84
94
 
85
95
  export async function main(argv: string[]) {
@@ -62,7 +62,7 @@ export async function updatePlugin(deps: PluginDeps) {
62
62
  if (!installedPlugin(deps.run)) return undefined;
63
63
  const address = probeAddress(readPluginConfig(pluginConfigDir(deps.run)));
64
64
  if ((await isPortOpen(address)) && !(await stopPluginWebr(deps.stateDir, address))) return [`Webr is running on port ${address.port} but the plugin did not start it. Stop it and run "webr plugin install" to use the new version.`];
65
- const refreshed = deps.run('webr', ['plugin', 'install']);
65
+ const refreshed = deps.run('webr', ['plugin', 'install', '--no-open']);
66
66
  return refreshed.ok ? quiet(refreshed.output) : [`The new Webr could not refresh the plugin. Run "webr plugin install". ${quiet(refreshed.output).join(' ')}`];
67
67
  }
68
68
 
@@ -115,6 +115,10 @@ export async function createHost(manager: RuntimeManager, origin: string, ssr?:
115
115
  const input = z.object({ machineId: opaqueId, archived: z.boolean() }).strict().parse(request.body);
116
116
  manager.archive(input.machineId, id, input.archived); return { archived: input.archived };
117
117
  });
118
+ app.post('/api/projects/rename', async (request) => {
119
+ const input = z.object({ machineId: opaqueId, logicalId: z.string().min(1).max(1000), name: z.string().trim().min(1).max(90).refine((name) => !/[\x00-\x1f\x7f]/.test(name)) }).strict().parse(request.body);
120
+ manager.renameProject(input.machineId, input.logicalId, input.name); return { name: input.name };
121
+ });
118
122
  app.post('/api/threads/:id/rename', async (request) => {
119
123
  const { id } = z.object({ id: z.uuid() }).parse(request.params);
120
124
  const input = z.object({ machineId: opaqueId, title: z.string().trim().min(1).max(90).refine((title) => !/[\x00-\x1f\x7f]/.test(title)) }).strict().parse(request.body);
@@ -3,7 +3,7 @@ import type { TargetAdapter, TerminalDirection } from './target';
3
3
  import type { Thread } from '../../lib/models';
4
4
  import { threadAgent, threadShell } from '../../lib/terminal-split';
5
5
  import { TargetSupervisor } from './supervisor';
6
- import { reconcile } from './reconcile';
6
+ import { projectNameKey, reconcile } from './reconcile';
7
7
  import type { MetadataDatabase } from '../storage/database';
8
8
  import type { Bootstrap, Projection } from '../../shared/runtime';
9
9
  import type { Machine } from '../../lib/machines';
@@ -109,6 +109,12 @@ export class RuntimeManager extends EventEmitter {
109
109
  this.database.saveThread({ ...row.metadata, archivedAt: archived ? new Date().toISOString() : undefined }, row.anchors, row.alias);
110
110
  this.publish(supervisor);
111
111
  }
112
+ renameProject(machineId: string, logicalId: string, name: string) {
113
+ const supervisor = this.supervisors.get(machineId);
114
+ if (!supervisor) throw new Error('machine_disconnected');
115
+ this.database.setSetting(projectNameKey(machineId, logicalId), name);
116
+ this.publish(supervisor);
117
+ }
112
118
  async renameThread(machineId: string, threadId: string, title: string) {
113
119
  const supervisor = this.supervisors.get(machineId);
114
120
  const thread = this.projections.get(machineId)?.threads.find((thread) => thread.id === threadId);
@@ -4,9 +4,11 @@ import type { NativeSnapshot } from '../protocol/native';
4
4
  import type { Project, Thread } from '../../lib/models';
5
5
  import type { TargetAdapter } from './target';
6
6
 
7
+ export const projectNameKey = (machineId: string, logicalId: string) => `project-name:${machineId}:${logicalId}`;
7
8
  export const projectIdentity = (machineId: string, repository: string) => createHash('sha256').update(machineId + '\0' + repository).digest('hex').slice(0, 32);
8
9
  const sameBinding = (thread: Thread, target: TargetAdapter) => thread.session === target.session && thread.bindingFingerprint === target.fingerprint && thread.bindingConfigVersion === target.configVersion;
9
10
  const paneStatus = (panes: NativeSnapshot['panes']) => { const agents = panes.filter((pane) => pane.agent); return ['blocked', 'working', 'done', 'idle', 'unknown'].find((status) => (agents.length ? agents : panes).some((pane) => pane.agent_status === status)) as Thread['status'] | undefined; };
11
+ const readingAwareStatus = (status: string) => status === 'done' ? 'idle' : status;
10
12
  const threadTitle = (label: string | null | undefined, panes: NativeSnapshot['panes'], fallback = 'Terminal') => {
11
13
  const namedTab = label?.trim();
12
14
  if (namedTab && !/^\d+$/.test(namedTab)) return namedTab;
@@ -22,7 +24,8 @@ export function reconcile(database: MetadataDatabase, target: TargetAdapter, sna
22
24
  const cwd = workspace.worktree?.repo_root ?? snapshot.panes.find((pane) => pane.workspace_id === workspace.workspace_id)?.cwd ?? '';
23
25
  const location = target.locations.find((location) => location.workspaceId === workspace.workspace_id || location.path === cwd);
24
26
  const id = location?.projectId ?? projectIdentity(target.id, workspace.worktree?.repo_key ?? (cwd || workspace.workspace_id));
25
- if (!projects.some((project) => project.id === id)) projects.push({ id, machineId: target.id, localId: location?.localId, logicalId: location?.logicalId ?? workspace.worktree?.repo_key ?? id, name: workspace.worktree?.repo_name ?? workspace.label, path: location?.path ?? cwd, color: '#9b83df', initial: (workspace.label[0] ?? 'P').toUpperCase(), iconUrl: target.icon && location ? `/api/projects/${encodeURIComponent(target.id)}/${encodeURIComponent(id)}/icon` : undefined });
27
+ const logicalId = location?.logicalId ?? workspace.worktree?.repo_key ?? id;
28
+ if (!projects.some((project) => project.id === id)) projects.push({ id, machineId: target.id, localId: location?.localId, logicalId, name: database.getSetting(projectNameKey(target.id, logicalId)) ?? workspace.worktree?.repo_name ?? workspace.label, path: location?.path ?? cwd, color: '#9b83df', initial: (workspace.label[0] ?? 'P').toUpperCase(), iconUrl: target.icon && location ? `/api/projects/${encodeURIComponent(target.id)}/${encodeURIComponent(id)}/icon` : undefined });
26
29
  for (const tab of snapshot.tabs.filter((tab) => tab.workspace_id === workspace.workspace_id)) tabProjects.set(tab.tab_id, id);
27
30
  }
28
31
  const launching = database.operations().some((operation) => operation.input.machineId === target.id && ['pending', 'running'].includes(operation.state));
@@ -40,7 +43,7 @@ export function reconcile(database: MetadataDatabase, target: TargetAdapter, sna
40
43
  const members = tab ? snapshot.panes.filter((pane) => pane.tab_id === tab.tab_id && pane.workspace_id === tab.workspace_id) : [];
41
44
  const attached = sameBinding(row.metadata, target) && members.length > 0;
42
45
  const panes = attached ? members : [];
43
- const signature = JSON.stringify([tab?.label, panes.map((pane) => [pane.terminal_id, pane.pane_id, pane.agent, pane.agent_status, pane.title, pane.terminal_title_stripped, pane.focused, snapshot.agents.find((agent) => agent.terminal_id === pane.terminal_id)?.state_change_seq])]);
46
+ const signature = JSON.stringify([tab?.label, panes.map((pane) => [pane.terminal_id, pane.pane_id, pane.agent, readingAwareStatus(pane.agent_status), pane.title, pane.terminal_title_stripped])]);
44
47
  const meaningfulChange = attached && row.metadata.semanticSignature !== signature;
45
48
  const operation = operations.find((operation) => operation.threadId === row.id);
46
49
  const thread: Thread = { ...row.metadata, title: attached ? threadTitle(tab?.label, panes, row.metadata.title) : row.metadata.title, projectId: attached ? tabProjects.get(tab!.tab_id) ?? row.metadata.projectId : row.metadata.projectId, bindingState: attached ? 'attached' : operation && !row.anchors.length ? 'pending' : 'detached', status: attached ? paneStatus(panes) ?? 'unknown' : 'unknown', agent: attached ? panes.find((pane) => pane.agent)?.agent ?? '' : row.metadata.agent, semanticSignature: attached ? signature : row.metadata.semanticSignature, updatedAt: meaningfulChange ? new Date().toISOString() : row.metadata.updatedAt, panes: panes.map((pane) => ({ id: pane.pane_id, terminalId: pane.terminal_id, title: pane.title ?? pane.terminal_title_stripped ?? pane.agent ?? 'Shell', kind: pane.agent ? 'agent' : 'shell' })), operation: operation ? { id: operation.id, state: operation.state, step: operation.step } : undefined };
@@ -2,7 +2,7 @@ import { readdir, realpath, stat, readFile } from 'node:fs/promises';
2
2
  import { basename, dirname, extname, isAbsolute, join, relative, sep } from 'node:path';
3
3
 
4
4
  const contentTypes: Record<string, string> = { '.ico': 'image/x-icon', '.png': 'image/png', '.svg': 'image/svg+xml', '.webp': 'image/webp' };
5
- const directories = ['', 'public', 'static', 'assets', 'web/public', 'app/public', 'src/assets', 'src/app', 'frontend/public'];
5
+ const directories = ['', 'public', 'static', 'assets', 'web', 'web/public', 'app/public', 'src/assets', 'src/app', 'frontend/public'];
6
6
  export type Icon = { bytes: Buffer; contentType: string };
7
7
 
8
8
  function withinRoot(root: string, path: string) {
@@ -1 +0,0 @@
1
- @font-face{font-family:JetBrains Mono;src:url(/_astro/jetbrains-mono.CFJaiWaY.woff2)format("woff2");font-weight:100 800;font-style:normal;font-display:swap}@font-face{font-family:Webr Symbols;src:url(/_astro/symbols-nerd.CEdkKAK8.woff2)format("woff2");font-display:swap;unicode-range:U+E0A0-E0D7,U+E5FA-E6B7,U+E700-E7C5,U+F400-F4A8}:root{color-scheme:light;--page:#f8f8f7;--surface:#fff;--surface-raised:#fff;--surface-muted:#f4f4f3;--surface-hover:#f0f0ef;--surface-active:#eaeae9;--sidebar:#f3f3f2;--card-hover:#fff;--card-edge:#1a1d2017;--card-shadow:inset 0 1px 2px #fffffff2, 0 1px 2px #00000029, 0 3px 7px #00000012;--card-shadow-hover:inset 0 1px 2px #fffffff2, 0 2px 4px #0000002b, 0 5px 10px #00000014;--ink:#1b1c1d;--ink-soft:#5b5d61;--ink-muted:#8b8d90;--line:#e9e9e7;--line-strong:#dedfdd;--accent:#357dba;--action-blue:#345fba;--accent-soft:#e9f3fa;--focus-ring:#579bcf;--working:#58a47a;--blocked:#dca362;--danger:#c9534d;--done:#7196ca;--idle:#9a9fa3;--terminal-bg:#fefefd;--terminal-ink:#333639;--terminal-cursor:#555b60;--terminal-selection:#bbd8e8;--shadow:0 1px 2px #1c202308, 0 10px 36px #1c202308;--ease:cubic-bezier(.22, 1, .36, 1);--font-ui:"DM Sans", Inter, system-ui, sans-serif;--font-mono:"JetBrains Mono", "Webr Symbols", ui-monospace, Menlo, monospace}html[data-theme=dark]{color-scheme:dark;--page:#121313;--surface:#1b1c1c;--surface-raised:#202121;--surface-muted:#242626;--surface-hover:#2a2c2c;--surface-active:#333535;--sidebar:#111212;--card-hover:#2b2d2d;--card-edge:#ffffff14;--card-shadow:inset 0 1px 2px #ffffff2b, 0 1px 2px #0000005c, 0 3px 7px #0000002e;--card-shadow-hover:inset 0 1px 2px #ffffff40, 0 2px 4px #00000061, 0 5px 10px #0000002e;--ink:#f1f2f0;--ink-soft:#b1b3b0;--ink-muted:#777b7b;--line:#292b2b;--line-strong:#393b3b;--accent:#91cae8;--action-blue:#365eb9;--accent-soft:#203239;--focus-ring:#9dd5e9;--working:#85cba2;--blocked:#ecc188;--danger:#e07a74;--done:#a0bdec;--idle:#858d8b;--terminal-bg:#181a1a;--terminal-ink:#d8dcda;--terminal-cursor:#d8dcda;--terminal-selection:#37505a;--shadow:0 1px 2px #00000024, 0 10px 36px #0000001f}*,:before,:after{box-sizing:border-box}html{background:var(--page)}body{color:var(--ink);background:var(--page);font:13px/1.45 var(--font-ui);-webkit-font-smoothing:antialiased;margin:0}button,input,textarea,select{font:inherit}button{cursor:pointer}a{color:inherit;text-decoration:none}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:0}.app-shell{min-height:100dvh;display:flex}.main-panel{flex-direction:column;flex:1;min-width:0;height:100dvh;min-height:100dvh;display:flex;overflow-y:auto}.icon-button{width:30px;height:30px;color:var(--ink-muted);background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;display:inline-flex}.icon-button:hover{background:var(--surface-hover);color:var(--ink)}.not-found{max-width:680px;margin:14vh auto 0;padding:25px}.not-found h1{font-size:26px;font-weight:600}.not-found p{color:var(--ink-muted)}.mobile-controls{display:none}@media (width<=760px){.mobile-controls{z-index:8;top:max(6px, env(safe-area-inset-top));right:max(6px, env(safe-area-inset-right));border:1px solid var(--line);background:var(--sidebar);border-radius:6px;gap:2px;padding:2px;display:flex;position:fixed}.mobile-controls button{width:28px;height:28px;color:var(--ink-soft);background:0 0;border:0;border-radius:4px;place-items:center;display:grid}.mobile-controls button:hover{background:var(--surface-hover)}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}@media (pointer:coarse){.app-shell.is-thread{top:var(--app-top,0px);height:var(--app-height,100dvh);min-height:0;position:fixed;left:0;right:0;overflow:hidden}.app-shell.is-thread .main-panel{height:100%;min-height:0;padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}html:has(.app-shell.is-thread),html:has(.app-shell.is-thread) body{overscroll-behavior:none}}.sidebar{border-right:1px solid var(--line);background:var(--sidebar);flex-direction:column;flex:0 0 280px;min-width:0;height:100dvh;display:flex;position:sticky;top:0}.sidebar.is-collapsed{flex-basis:44px}.sidebar-header{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;height:48px;padding:0 10px 0 16px;display:flex}.sidebar.is-collapsed .sidebar-header{justify-content:center;padding:0}.brand{letter-spacing:-.02em;align-items:center;gap:7px;font-size:12px;font-weight:700;display:inline-flex}.sidebar-collapse{width:26px;height:26px}.sidebar-actions{flex:none;align-items:center;gap:5px;margin:12px 8px 4px;display:flex}.sidebar-new-thread{border:1px solid var(--card-edge);background:var(--surface-raised);height:34px;box-shadow:var(--card-shadow);color:var(--ink);border-radius:7px;flex:none;justify-content:center;align-items:center;gap:6px;padding:0 10px;font-size:11px;font-weight:600;transition:background-color .2s,box-shadow .2s;display:inline-flex}.sidebar-new-thread span{transform:translateY(1px)}.sidebar-new-thread:hover{background:var(--card-hover);box-shadow:var(--card-shadow-hover)}.sidebar.is-collapsed .sidebar-actions{justify-content:center;margin:10px 0 0}.sidebar.is-collapsed .sidebar-new-thread{width:28px;height:28px;padding:0}.sidebar-open-worktree{border:1px solid var(--card-edge);background:var(--surface-raised);width:34px;height:34px;box-shadow:var(--card-shadow);color:var(--ink-soft);border-radius:7px;flex:none;place-items:center;padding:0;display:grid}.sidebar-open-worktree:hover:not(:disabled){background:var(--card-hover);box-shadow:var(--card-shadow-hover);color:var(--ink)}.sidebar.is-collapsed .sidebar-actions{flex-direction:column}.sidebar.is-collapsed .sidebar-open-worktree{width:28px;height:28px}.sidebar-view-switch{border:1px solid var(--line);background:var(--surface-muted);border-radius:7px;flex:none;gap:2px;width:70px;height:34px;margin-left:auto;padding:2px;display:flex}.sidebar-view-switch button{min-width:0;color:var(--ink-soft);background:0 0;border:1px solid #0000;border-radius:5px;flex:1;place-items:center;padding:0;display:grid}.sidebar-view-switch button:hover{color:var(--ink);background:var(--surface-hover)}.sidebar-view-switch button[aria-pressed=true]{border-color:var(--card-edge);background:var(--surface-raised);box-shadow:var(--card-shadow);color:var(--ink)}.thread-navigation{scrollbar-width:thin;flex:1;padding:8px;overflow-y:auto}.thread-row{min-height:60px;color:var(--ink-soft);border:1px solid #0000;border-radius:7px;grid-template-rows:17px 1fr;grid-template-columns:25px minmax(0,1fr) auto;column-gap:9px;padding:7px 10px;font-size:11.5px;transition:background-color .2s,border-color .2s,box-shadow .2s;display:grid}.thread-item{display:block;position:relative}.thread-item+.thread-item{margin-top:4px}.thread-archive{width:26px;height:26px;color:var(--ink-soft);opacity:0;pointer-events:none;background:0 0;border:0;border-radius:5px;place-items:center;padding:0;transition:opacity .15s,background-color .15s;display:grid;position:absolute;top:50%;right:9px;transform:translateY(-50%)}.thread-item:hover .thread-archive,.thread-archive:focus-visible{opacity:1;pointer-events:auto}.thread-archive:hover{background:var(--surface-hover);color:var(--ink)}.thread-item:hover .thread-time,.thread-item:focus-within .thread-time{visibility:hidden}@media (hover:none){.thread-archive{opacity:1;pointer-events:auto}.thread-item .thread-time{visibility:hidden}}.thread-menu-positioner{z-index:30}.thread-menu{border:1px solid var(--card-edge);background:var(--surface-raised);min-width:180px;color:var(--ink);box-shadow:var(--card-shadow-hover), 0 12px 32px #0000002e;border-radius:8px;outline:0;padding:4px}.thread-menu-item{cursor:pointer;border-radius:5px;outline:0;align-items:center;gap:8px;min-height:32px;padding:6px 8px;font-size:12px;display:flex}.thread-menu-item[data-highlighted]{background:var(--surface-hover)}.worktree-menu{width:260px;padding:0;overflow:hidden}.worktree-menu-list{max-height:min(320px, var(--available-height));padding:4px;overflow-y:auto}.worktree-menu-note{color:var(--ink-soft);margin:0;padding:8px;font-size:12px}.thread-menu-item>svg,.thread-menu-item>.thread-project{flex:none}.worktree-menu-name{overflow-wrap:anywhere;min-width:0;display:grid}.worktree-menu-name small{color:var(--ink-soft);font-size:10.5px}.worktree-menu-chevron{color:var(--ink-soft);margin-left:auto}.thread-menu-item.is-danger{color:var(--danger)}.archived-threads{border-top:1px solid var(--line);scrollbar-width:thin;flex:none;max-height:45%;padding:6px 8px;overflow-y:auto}.archived-threads .group-header{z-index:1;background:var(--sidebar);position:sticky;top:-6px}.group-count{font:10px var(--font-mono);color:var(--ink-muted);margin-left:2px}.archived-delete-all{height:26px;color:var(--ink-soft);background:0 0;border:1px solid #0000;border-radius:5px;flex:none;margin-right:4px;padding:0 8px;font-size:11px}.archived-delete-all:hover:not(:disabled){border-color:var(--line);background:var(--surface-hover);color:var(--danger)}.archived-delete-all:disabled{opacity:.6}.thread-avatar{grid-area:1/1/3;justify-content:center;align-self:center;align-items:center;width:25px;height:25px;display:inline-flex;position:relative}.thread-row:hover,.thread-row.is-current:hover{border-color:var(--card-edge);background:var(--card-hover);box-shadow:var(--card-shadow-hover);color:var(--ink)}.thread-row.needs-attention.status-blocked{border-color:color-mix(in srgb, var(--blocked) 45%, transparent)}.thread-row.needs-attention.status-done{border-color:color-mix(in srgb, var(--done) 45%, transparent)}.thread-row.is-current{border-color:var(--card-edge);background:var(--surface-raised);box-shadow:var(--card-shadow);color:var(--ink)}.thread-row.is-current:hover{background:var(--card-hover);box-shadow:var(--card-shadow-hover)}.thread-copy{flex-direction:column;grid-area:1/2/3;justify-content:center;gap:4px;min-width:0;display:flex}.thread-context{color:var(--ink-soft);align-items:center;gap:5px;font-size:10px;display:flex}.thread-name{white-space:nowrap;text-overflow:ellipsis;color:var(--ink);overflow:hidden}.thread-name-input{border:1px solid var(--focus-ring);background:var(--surface);width:100%;min-width:0;color:var(--ink);font:inherit;border-radius:4px;outline:0;margin:-2px -4px;padding:1px 4px}.thread-time{font:10px var(--font-mono);color:var(--ink-soft);grid-area:1/3;justify-self:end}.thread-status{background:var(--sidebar);color:var(--ink-soft);border-radius:50%;padding:1px;position:absolute;top:-5px;left:-5px}.thread-row.is-current .thread-status{background:var(--surface-raised)}.thread-row:hover .thread-status{background:var(--card-hover)}.thread-status.status-blocked{color:var(--blocked)}.thread-status.status-done{color:var(--done)}.thread-project{background:color-mix(in srgb, var(--project-color) 17%, transparent);width:17px;height:17px;color:var(--project-color);font:700 10px var(--font-mono);border-radius:3px;flex:none;place-items:center;display:grid;position:relative;overflow:hidden}.thread-project img{object-fit:contain;background:var(--surface-raised);opacity:0;width:100%;height:100%;position:absolute;inset:0}.thread-project.has-icon img{opacity:1}.group-header{align-items:center;min-width:0;display:flex}.group-title{min-width:0;color:var(--ink-soft);text-align:left;background:0 0;border:0;border-radius:5px;flex:1;align-items:center;gap:6px;padding:8px;font-size:11px;display:flex}.group-title:hover{background:var(--surface-hover);color:var(--ink)}.group-name{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.group-new-thread{width:28px;height:28px;color:var(--ink-soft);border-radius:5px;flex:none;place-items:center;margin-right:4px;display:grid}.group-new-thread:hover{background:var(--surface-hover);color:var(--ink)}.group-chevron{flex:none;justify-content:center;align-items:center;width:12px;height:17px;display:inline-flex}.thread-group+.thread-group{margin-top:8px}.sidebar-footer{border-top:1px solid var(--line);flex:none;align-items:center;gap:8px;padding:4px 12px 4px 4px;display:flex}.sidebar-settings{height:35px;color:var(--ink-soft);border-radius:6px;flex:1;align-items:center;gap:10px;padding:0 12px;font-size:11.5px;display:flex}.sidebar-palette{border:1px solid var(--line-strong);color:var(--ink-soft);background:0 0;border-radius:4px;padding:3px 6px}.sidebar-palette:hover{color:var(--ink);background:var(--surface-hover)}.sidebar-palette kbd{font:10px var(--font-mono)}.sidebar-settings:hover{color:var(--ink);background:var(--surface-hover)}.sidebar-empty{color:var(--ink-muted);padding:8px;font-size:11px}.sidebar-scrim,.mobile-close{display:none}@media (width<=760px){.sidebar,.sidebar.is-collapsed{z-index:11;flex-basis:auto;width:min(280px,86vw);transition:transform .2s;position:fixed;transform:translate(-100%)}.sidebar.is-mobile-open{transform:translate(0)}.sidebar.is-collapsed .sidebar-header{justify-content:space-between;padding:0 10px 0 16px}.sidebar.is-collapsed .thread-navigation,.sidebar.is-collapsed .sidebar-footer{display:flex}.mobile-close{display:inline-flex}.sidebar-collapse{display:none}.sidebar-scrim{z-index:10;background:#00000073;border:0;display:block;position:fixed;inset:0}}.thread-worktree{opacity:.7;flex:none}.form-page{width:min(520px,100%);margin:8vh auto 0;padding:0 20px 60px}.form-page h1{letter-spacing:-.03em;margin:0 0 26px;font-size:22px;font-weight:600}.form-error{color:#d16a64;font-size:12px}.settings-page section{margin-top:28px}.settings-page h2{margin:18px 0 8px;font-size:12px;font-weight:600}.settings-row{justify-content:space-between;align-items:center;gap:12px;min-height:44px;font-size:12px;display:flex}.settings-section-title{justify-content:space-between;align-items:center;display:flex}.settings-section-title button{color:var(--ink-soft);background:0 0;border:0;border-radius:5px;padding:6px 9px;font-size:11px}.settings-section-title button:hover{background:var(--surface-hover)}.settings-section-title button:hover,.shortcut-key:hover{color:var(--ink)}.shortcut-key{min-height:32px;color:var(--ink-soft);background:0 0;border:0;align-items:center;gap:4px;padding:3px 0;font-size:11px;display:inline-flex}.shortcut-key kbd{background:var(--surface-raised);min-width:25px;height:25px;box-shadow:var(--card-shadow);color:var(--ink-soft);font:11px var(--font-mono);border:0;border-radius:5px;place-items:center;padding:0 5px;display:inline-grid}.shortcut-key:hover kbd,.shortcut-key:focus-visible kbd{color:var(--ink);background:var(--card-hover);box-shadow:var(--card-shadow-hover)}.shortcut-key[data-recording=true]{box-shadow:inset 0 0 0 1px var(--focus-ring);color:var(--ink);border-radius:5px;padding:3px 8px}.settings-note{color:var(--ink-soft);margin:0 0 10px;font-size:11px}.theme-control{background:var(--surface-muted);border-radius:7px;gap:2px;padding:2px;display:flex}.theme-option{color:var(--ink-soft);text-transform:capitalize;background:0 0;border:0;border-radius:5px;align-items:center;gap:5px;padding:6px 8px;font-size:11px;display:flex}.theme-option:hover{color:var(--ink)}.theme-option.is-selected{background:var(--surface-raised);box-shadow:var(--card-shadow);color:var(--ink)}.theme-control--labels .theme-option{text-transform:none}.mobile-menu{display:none}@media (width<=760px){.form-page{margin-top:35px}.mobile-menu{color:var(--ink-soft);background:0 0;border:0;margin-bottom:30px;padding:4px 0;display:block}}.new-thread-page{flex-direction:column;justify-content:center;width:min(800px,100%);min-height:100dvh;margin:0 auto;padding:60px 20px;display:flex;position:relative}.new-thread-page h1{color:var(--ink);letter-spacing:-.035em;text-align:center;margin:0 0 26px;font-size:clamp(24px,2.8vw,32px);font-weight:500;line-height:1.25}.thread-composer{border:1px solid var(--line-strong);background:var(--surface);box-shadow:var(--card-shadow);border-radius:18px;transition:border-color .2s,box-shadow .2s}.thread-composer:focus-within{border-color:color-mix(in srgb, var(--focus-ring) 50%, var(--line-strong));box-shadow:var(--card-shadow-hover)}.workspace-composer{gap:12px;padding:18px;display:grid}.workspace-composer label{color:var(--ink-soft);gap:6px;font-size:12px;display:grid}.workspace-composer input{border:1px solid var(--line-strong);width:100%;color:var(--ink);background:0 0;border-radius:8px;padding:10px}.workspace-composer button{justify-self:end;padding:8px 12px}.workspace-composer .path-field{position:relative}.path-suggestions{z-index:5;border:1px solid var(--card-edge);background:var(--surface-raised);box-shadow:var(--card-shadow-hover);border-radius:8px;max-height:240px;margin:4px 0 0;padding:4px;list-style:none;position:absolute;top:100%;left:0;right:0;overflow-y:auto}.path-suggestions li{color:var(--ink);cursor:pointer;border-radius:5px;padding:6px 8px;font-size:13px}.path-suggestions li[data-highlighted]{background:var(--surface-hover)}.workspace-cancel{color:var(--ink-soft);background:0 0;border:0;border-radius:6px;align-self:center;padding:8px 12px}.workspace-cancel:hover{background:var(--surface-hover);color:var(--ink)}.thread-composer textarea{resize:vertical;width:100%;min-height:94px;max-height:45vh;color:var(--ink);background:0 0;border:0;outline:0;padding:17px 18px 0;font-size:14px;line-height:1.5;display:block}.thread-composer textarea::placeholder{color:var(--ink-muted)}.composer-toolbar{align-items:center;gap:10px;padding:9px 12px 12px;display:flex}.composer-selectors{flex-wrap:wrap;align-items:center;gap:3px;min-width:0;display:flex}.composer-actions{flex:none;align-items:center;gap:5px;margin-left:auto;display:inline-flex}.composer-preview-note{color:var(--ink-soft);text-align:center;margin:12px 0 0;font-size:11px}.worktree-option{width:30px;height:32px;color:var(--ink-soft);cursor:pointer;border-radius:6px;place-items:center;display:inline-grid;position:relative}.worktree-option:hover{color:var(--ink);background:var(--surface-hover)}.worktree-option:focus-within{outline:0}.worktree-option input{opacity:0;width:1px;height:1px;position:absolute}.worktree-option:has(input:not(:checked)){color:var(--ink-muted)}.worktree-option:has(input:checked){background:var(--surface-muted);color:var(--ink)}.composer-send{background:var(--action-blue);color:#fff;border:0;border-radius:50%;flex:none;place-items:center;width:36px;height:36px;display:inline-grid}.composer-send:hover:not(:disabled){filter:brightness(1.12)}.composer-send:disabled{background:var(--surface-active);color:var(--ink-muted);cursor:not-allowed}.new-thread-page .form-error{margin:12px 4px}@media (width<=760px){.new-thread-page{padding:60px 16px}.new-thread-page .mobile-menu{margin:0;position:absolute;top:16px;left:16px}}@media (width<=480px){.composer-toolbar{gap:6px;padding:8px}}.thread-composer.is-dropping{border-style:dashed;border-color:var(--focus-ring);box-shadow:var(--card-shadow-hover)}.composer-images{flex-wrap:wrap;gap:8px;margin:0;padding:14px 18px 0;list-style:none;display:flex}.composer-images li{border:1px solid var(--line-strong);background:var(--surface-raised);border-radius:9px;width:56px;height:56px;position:relative;overflow:hidden}.composer-images img{object-fit:cover;width:100%;height:100%;display:block}.composer-images button{background:color-mix(in srgb, var(--surface) 85%, transparent);width:18px;height:18px;color:var(--ink);cursor:pointer;opacity:0;border:0;border-radius:50%;place-items:center;padding:0;transition:opacity .15s;display:grid;position:absolute;top:3px;right:3px}.composer-images li:hover button,.composer-images button:focus-visible{opacity:1}.composer-images li.is-uploading{background:linear-gradient(100deg, var(--surface-raised) 30%, var(--surface-hover) 50%, var(--surface-raised) 70%) 0 0 / 200% 100%;animation:1.1s linear infinite composer-image-loading}@keyframes composer-image-loading{to{background-position:-200% 0}}@media (hover:none){.composer-images button{opacity:1}}.composer-select{min-width:0;max-width:min(220px,100%);height:32px;color:var(--ink-soft);background:0 0;border:1px solid #0000;border-radius:6px;justify-content:flex-start;align-items:center;gap:5px;padding:0 6px;font-size:11px;display:inline-flex}.composer-select:hover,.composer-select[data-popup-open]{border-color:var(--line);background:var(--surface-hover);color:var(--ink)}.composer-select-icon{flex:none;justify-content:center;align-items:center;display:inline-flex}.composer-select-icon .thread-project{width:14px;height:14px}.composer-select-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.composer-select-caret{flex:none;margin-left:8px}.composer-select-positioner{z-index:20}.composer-select-popup{width:min(280px, var(--available-width));max-height:var(--available-height);border:1px solid var(--card-edge);background:var(--surface-raised);color:var(--ink);box-shadow:var(--card-shadow-hover), 0 12px 32px #0000002e;border-radius:9px;overflow:hidden}.composer-select-search{border-bottom:1px solid var(--line);color:var(--ink-soft);align-items:center;gap:8px;padding:10px 12px;display:flex}.composer-select-search input{width:100%;min-width:0;color:var(--ink);background:0 0;border:0;outline:0;padding:0;font-size:16px;line-height:1.4}.composer-select-search input::placeholder{color:var(--ink-soft)}.composer-select-list{max-height:min(240px, calc(var(--available-height) - 50px));overscroll-behavior:contain;padding:5px;overflow-y:auto}.composer-select-option{cursor:pointer;border-radius:5px;align-items:center;gap:8px;min-height:36px;padding:8px;font-size:12px;display:flex}.composer-select-option[data-highlighted]{background:var(--surface-hover);outline:0}.composer-select-option[data-disabled]{opacity:.5;cursor:not-allowed}.composer-select-option-icon{flex:none;align-items:center;display:inline-flex}.composer-select-option-label{overflow-wrap:anywhere;flex:1;min-width:0}.composer-select-check{color:var(--ink-soft);flex:none;display:inline-flex}.composer-select-action{border:0;border-top:1px solid var(--line);width:calc(100% - 10px);min-height:36px;color:var(--ink);text-align:left;background:0 0;border-radius:5px;align-items:center;gap:8px;margin:2px 5px 5px;padding:8px;font-size:12px;display:flex}.composer-select-action:hover{background:var(--surface-hover)}.composer-select-empty{color:var(--ink-soft);font-size:12px}.composer-select-empty:not(:empty){padding:12px}@media (width>=761px){.composer-select-search input{font-size:12px}}@media (width<=480px){.composer-select-icon{display:none}}.pane-layout{background:var(--line-strong);grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;height:100dvh;min-height:0;display:grid}.pane-layout.single-pane{grid-template-columns:minmax(0,1fr)}.terminal-pane{background:var(--terminal-bg);min-width:0;min-height:0;overflow:hidden}.terminal-host{width:100%;height:100%;padding:12px 8px 8px 13px}.terminal-host .xterm{height:100%}.terminal-host .xterm-viewport{scrollbar-width:thin}@media (width<=760px){.pane-layout:not(.single-pane){grid-template-rows:repeat(2,minmax(0,1fr));grid-template-columns:minmax(0,1fr)}}.native-thread-layout{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.native-pane-layout{flex:1;min-height:160px;position:relative}.native-pane-position{padding:2px;position:absolute}.terminal-peek{background:var(--surface-muted);color:var(--ink-soft);border:0;border-radius:6px;justify-content:center;align-items:center;gap:6px;padding:0;font-size:11px;transition:background-color .15s,color .15s;display:flex;position:absolute}.terminal-peek:hover,.terminal-peek:focus-visible{background:var(--surface-hover);color:var(--ink)}.terminal-peek.is-right{flex-direction:column;width:24px;top:2px;bottom:2px;right:2px}.terminal-peek.is-right span{writing-mode:vertical-rl}.terminal-peek.is-bottom{height:24px;bottom:2px;left:2px;right:2px}.native-pane-position .terminal-pane{flex-direction:column;height:100%;display:flex}.native-pane-layout.is-split .terminal-pane{outline:1px solid var(--line);outline-offset:-1px;border-radius:5px;transition:outline-color .15s}.native-pane-layout.is-split .terminal-pane.is-active{outline-color:color-mix(in srgb, var(--focus-ring) 15%, var(--line-strong))}.terminal-host{flex:1;height:auto;min-height:0;overflow:hidden}.terminal-pane{position:relative}.terminal-input-capture{opacity:.01;resize:none;border:0;width:2px;height:2px;padding:0;position:absolute;bottom:8px;left:12px}.terminal-status{clip-path:inset(50%);width:1px;height:1px;position:absolute;overflow:hidden}.terminal-control-conflict{z-index:10;border:1px solid var(--line-strong);background:var(--surface-raised);max-width:calc(100% - 16px);color:var(--ink-soft);box-shadow:var(--card-shadow);white-space:nowrap;border-radius:8px;align-items:center;gap:10px;padding:6px 10px;display:flex;position:absolute;top:8px;left:50%;transform:translate(-50%)}.terminal-control-conflict button{background:var(--surface-hover);color:var(--ink);border:0;border-radius:5px;padding:3px 7px}.adoption-picker{color:var(--ink-soft);gap:8px;padding:16px;font-size:13px;display:grid}.adoption-picker select{margin-left:8px}.runtime-notice{color:var(--ink-muted);padding:10px 16px;font-size:13px}.launch-progress{align-content:center;justify-items:start;gap:18px;width:min(520px,100%);height:100%;margin:0 auto;padding:0 20px;display:grid}.launch-progress h1{letter-spacing:-.03em;margin:0;font-size:22px;font-weight:600}.launch-prompt{border-left:2px solid var(--line-strong);color:var(--ink-soft);white-space:pre-wrap;overflow-wrap:anywhere;max-height:9em;margin:0;padding:10px 14px;font-size:13px;overflow:auto}.launch-steps{gap:10px;margin:0;padding:0;font-size:13px;list-style:none;display:grid}.launch-step{color:var(--ink-muted);align-items:center;gap:10px;display:flex}.launch-step.is-done{color:var(--ink-soft)}.launch-step.is-active{color:var(--ink)}.launch-step.is-active svg{animation:1s linear infinite launch-spin}.launch-step.is-failed{color:#d16a64}.launch-failure{color:var(--ink-soft);gap:10px;font-size:13px;display:grid}.launch-failure p{margin:0}.launch-retry{border:1px solid var(--line-strong);background:var(--surface-raised);color:var(--ink);border-radius:7px;justify-self:start;padding:7px 11px;font-size:12px;text-decoration:none}@keyframes launch-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.launch-step.is-active svg{animation:none}}.terminal-host .xterm{font-variant-ligatures:none;font-feature-settings:"liga" 0, "calt" 0}.terminal-drop-overlay{z-index:15;border:1.5px dashed var(--line-strong);background:color-mix(in srgb, var(--terminal-bg) 82%, transparent);color:var(--ink);pointer-events:none;border-radius:10px;place-content:center;justify-items:center;gap:8px;font-size:13px;display:grid;position:absolute;inset:8px}.terminal-attach-notice{z-index:16;border:1px solid var(--line-strong);background:var(--surface-raised);max-width:calc(100% - 28px);box-shadow:var(--card-shadow);color:var(--ink-soft);border-radius:8px;padding:6px 10px;font-size:12px;position:absolute;bottom:12px;right:14px}.terminal-attach-notice.is-error{color:#d16a64}.terminal-key-bar{padding:6px 8px calc(6px + env(safe-area-inset-bottom));border-top:1px solid var(--line);background:var(--surface-muted);flex:none;gap:6px;display:flex}.terminal-key-bar button{background:var(--surface-raised);min-height:38px;box-shadow:var(--card-shadow);color:var(--ink);touch-action:manipulation;-webkit-user-select:none;user-select:none;border:0;border-radius:8px;flex:1;place-items:center;font-size:13px;display:inline-grid}.terminal-key-bar button.is-armed{background:var(--action-blue);color:#fff}.palette-overlay{z-index:30;background:#0000006e;justify-content:center;align-items:flex-start;padding:min(16vh,140px) 16px 16px;display:flex;position:fixed;inset:0}.palette{border:1px solid var(--card-edge);background:var(--surface);border-radius:9px;width:min(520px,100%);overflow:hidden;box-shadow:inset 0 1px 2px #ffffff1f,0 16px 60px #00000038}.palette>input{border:0;border-bottom:1px solid var(--line);width:100%;height:48px;color:var(--ink);background:0 0;outline:0;padding:0 16px;font-size:13px;display:block}.palette>input::placeholder{color:var(--ink-muted)}.palette-results{max-height:min(400px,55vh);padding:6px;overflow-y:auto}.palette-results button{width:100%;min-height:36px;color:var(--ink);text-align:left;background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:10px;padding:0 10px;font-size:12px;transition:background-color .2s,border-color .2s,box-shadow .2s;display:flex}.palette-icon{width:22px;height:22px;color:var(--ink-soft);flex:none;justify-content:center;align-items:center;display:inline-flex}.palette-label{white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.palette-results button:hover,.palette-results button.is-selected{border-color:var(--card-edge);background:var(--surface-raised);box-shadow:var(--card-shadow)}.palette-results button:hover{background:var(--card-hover);box-shadow:var(--card-shadow-hover)}.palette-results button small{color:var(--ink-soft);white-space:nowrap;font-size:10px}.palette-results button kbd{border:1px solid var(--line-strong);min-width:34px;color:var(--ink-soft);font:10px var(--font-mono);text-align:center;white-space:nowrap;border-radius:4px;margin-left:6px;padding:3px 5px}.palette-results p{color:var(--ink-muted);padding:4px 10px}.palette-help{border-top:1px solid var(--line);color:var(--ink-soft);font:10px var(--font-mono);gap:14px;padding:9px 14px;display:flex}.dialog-backdrop{z-index:40;background:#0000006e;position:fixed;inset:0}.dialog{z-index:41;border:1px solid var(--card-edge);background:var(--surface);width:min(400px,100vw - 32px);color:var(--ink);border-radius:10px;outline:0;padding:18px;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:inset 0 1px 2px #ffffff1f,0 16px 60px #00000038}.dialog-message{margin:0 0 16px;font-size:13px;line-height:1.5}.dialog-actions{justify-content:flex-end;gap:8px;display:flex}.dialog-button{border:1px solid var(--line-strong);background:var(--surface-raised);min-height:32px;color:var(--ink);cursor:pointer;border-radius:6px;padding:0 12px;font-size:12px}.dialog-button:hover{background:var(--surface-hover)}.dialog-button.is-primary{background:var(--action-blue);color:#fff;border-color:#0000}.dialog-button.is-danger{background:var(--danger)}.pairing-title{margin:0 0 6px;font-size:14px;font-weight:600}.pairing-code{font:700 40px/1 var(--font-mono);letter-spacing:.18em;text-align:center;text-indent:.18em;margin:0 0 16px}.pairing-invite .dialog-message code{font-family:var(--font-mono)}.qr-code{aspect-ratio:1;border-radius:8px;width:min(240px,100%);margin:0 auto 14px;display:block}.pairing-token{justify-content:center;align-items:center;gap:8px;margin:0 0 6px;display:flex}.pairing-token code{font:600 14px var(--font-mono);letter-spacing:.08em;user-select:all}.pairing-token .dialog-button{place-items:center;min-width:32px;padding:0;display:inline-grid}.device-tag{background:var(--accent-soft);color:var(--accent);border-radius:4px;margin-left:8px;padding:1px 6px;font-size:10px;font-style:normal}.device-meta{color:var(--ink-muted);font-size:11px;display:block}.crash-report{z-index:100;color:#7a1f1a;max-height:60dvh;font:11px/1.4 var(--font-mono);white-space:pre-wrap;word-break:break-word;background:#fff0ef;margin:0;padding:12px 16px;position:fixed;inset:0 0 auto;overflow:auto}.pairing-invite .theme-control{flex-wrap:wrap;justify-content:center;margin:0 0 12px}.pairing-invite .theme-option{white-space:nowrap}.settings-subtitle{color:var(--ink-soft);margin:16px 0 4px;font-size:11px;font-weight:500}.activity-row{color:var(--ink-muted);justify-content:space-between;gap:12px;margin:0;padding:4px 0;font-size:11px;display:flex}.activity-row time{flex:none}.perf-overlay{z-index:40;color:#d7ffd7;font-variant-numeric:tabular-nums;pointer-events:none;background:#000000c7;border-radius:6px;padding:4px 8px;font:11px/1.5 ui-monospace,monospace;position:fixed;bottom:8px;right:8px}.tooltip-positioner{z-index:40}.tooltip-popup{border:1px solid var(--line);background:var(--surface-raised);box-shadow:var(--card-shadow);color:var(--ink);white-space:nowrap;border-radius:6px;padding:5px 8px;font-size:11px;font-weight:500;transition:opacity .16s,transform .16s}.tooltip-popup[data-starting-style],.tooltip-popup[data-ending-style]{opacity:0;transform:translateY(3px)}::view-transition-group(root),::view-transition-old(root),::view-transition-new(root){mix-blend-mode:normal;animation:none}.astro-route-announcer{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;top:0;left:0;overflow:hidden}.xterm{cursor:text;-webkit-user-select:none;-ms-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:underline double}.xterm-underline-3{text-decoration:underline wavy}.xterm-underline-4{text-decoration:underline dotted}.xterm-underline-5{text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}