webmux 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/webmux.js CHANGED
@@ -2604,6 +2604,9 @@ function assertTmuxOk(args, action) {
2604
2604
  }
2605
2605
  return result.stdout;
2606
2606
  }
2607
+ function isIgnorableKillWindowError(stderr) {
2608
+ return stderr.includes("can't find window") || stderr.includes("can't find session") || stderr.includes("no server running") || stderr.includes("error connecting to") && stderr.includes("No such file or directory");
2609
+ }
2607
2610
  function sanitizeTmuxNameSegment(value, maxLength = 24) {
2608
2611
  const sanitized = value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[.-]+|[.-]+$/g, "");
2609
2612
  const trimmed = sanitized.slice(0, maxLength);
@@ -2637,8 +2640,10 @@ class BunTmuxGateway {
2637
2640
  ensureSession(sessionName, cwd) {
2638
2641
  const check = runTmux(["has-session", "-t", sessionName]);
2639
2642
  if (check.exitCode !== 0) {
2640
- assertTmuxOk(["new-session", "-d", "-s", sessionName, "-c", cwd], `create tmux session ${sessionName}`);
2643
+ assertTmuxOk(["new-session", "-d", "-s", sessionName, "-c", cwd, ";", "set-option", "-t", sessionName, "destroy-unattached", "off"], `create tmux session ${sessionName}`);
2644
+ return;
2641
2645
  }
2646
+ assertTmuxOk(["set-option", "-t", sessionName, "destroy-unattached", "off"], `set destroy-unattached off for ${sessionName}`);
2642
2647
  }
2643
2648
  hasWindow(sessionName, windowName) {
2644
2649
  const result = runTmux(["list-windows", "-t", sessionName, "-F", "#{window_name}"]);
@@ -2649,7 +2654,7 @@ class BunTmuxGateway {
2649
2654
  }
2650
2655
  killWindow(sessionName, windowName) {
2651
2656
  const result = runTmux(["kill-window", "-t", `${sessionName}:${windowName}`]);
2652
- if (result.exitCode !== 0 && !result.stderr.includes("can't find window")) {
2657
+ if (result.exitCode !== 0 && !isIgnorableKillWindowError(result.stderr)) {
2653
2658
  throw new Error(`kill tmux window ${sessionName}:${windowName} failed: ${result.stderr}`);
2654
2659
  }
2655
2660
  }
@@ -9685,10 +9690,27 @@ var init_dist3 = __esm(() => {
9685
9690
 
9686
9691
  // backend/src/adapters/config.ts
9687
9692
  import { readFileSync as readFileSync3 } from "fs";
9688
- import { join as join6 } from "path";
9693
+ import { dirname as dirname2, join as join6, resolve as resolve4 } from "path";
9689
9694
  function clonePanes(panes) {
9690
9695
  return panes.map((pane) => ({ ...pane }));
9691
9696
  }
9697
+ function cloneMounts(mounts) {
9698
+ return mounts?.map((mount) => ({ ...mount }));
9699
+ }
9700
+ function cloneProfile(profile) {
9701
+ return {
9702
+ ...profile,
9703
+ envPassthrough: [...profile.envPassthrough],
9704
+ panes: clonePanes(profile.panes),
9705
+ ...profile.mounts ? { mounts: cloneMounts(profile.mounts) } : {}
9706
+ };
9707
+ }
9708
+ function cloneProfiles(profiles) {
9709
+ return Object.fromEntries(Object.entries(profiles).map(([name, profile]) => [name, cloneProfile(profile)]));
9710
+ }
9711
+ function defaultProfiles() {
9712
+ return { default: cloneProfile(DEFAULT_CONFIG.profiles.default) };
9713
+ }
9692
9714
  function isRecord3(value) {
9693
9715
  return typeof value === "object" && value !== null && !Array.isArray(value);
9694
9716
  }
@@ -9761,16 +9783,16 @@ function parseProfile(raw, fallbackRuntime) {
9761
9783
  ...mounts ? { mounts } : {}
9762
9784
  };
9763
9785
  }
9764
- function parseProfiles(raw) {
9786
+ function parseProfiles(raw, includeDefaultProfile) {
9765
9787
  if (!isRecord3(raw))
9766
- return { default: { ...DEFAULT_CONFIG.profiles.default, panes: clonePanes(DEFAULT_CONFIG.profiles.default.panes) } };
9788
+ return includeDefaultProfile ? defaultProfiles() : {};
9767
9789
  const profiles = Object.entries(raw).reduce((acc, [name, value]) => {
9768
9790
  const fallbackRuntime = name === "sandbox" ? "docker" : "host";
9769
9791
  acc[name] = parseProfile(value, fallbackRuntime);
9770
9792
  return acc;
9771
9793
  }, {});
9772
9794
  if (Object.keys(profiles).length === 0) {
9773
- return { default: { ...DEFAULT_CONFIG.profiles.default, panes: clonePanes(DEFAULT_CONFIG.profiles.default.panes) } };
9795
+ return includeDefaultProfile ? defaultProfiles() : {};
9774
9796
  }
9775
9797
  return profiles;
9776
9798
  }
@@ -9842,6 +9864,69 @@ function getDefaultProfileName(config) {
9842
9864
  function readConfigFile(root) {
9843
9865
  return readFileSync3(join6(root, ".webmux.yaml"), "utf8");
9844
9866
  }
9867
+ function readLocalConfigFile(root) {
9868
+ return readFileSync3(join6(root, ".webmux.local.yaml"), "utf8");
9869
+ }
9870
+ function parseConfigDocument(text) {
9871
+ const parsed = $parse(text);
9872
+ return isRecord3(parsed) ? parsed : {};
9873
+ }
9874
+ function parseProjectConfig(parsed) {
9875
+ return {
9876
+ name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : DEFAULT_CONFIG.name,
9877
+ workspace: {
9878
+ mainBranch: isRecord3(parsed.workspace) && typeof parsed.workspace.mainBranch === "string" ? parsed.workspace.mainBranch : DEFAULT_CONFIG.workspace.mainBranch,
9879
+ worktreeRoot: isRecord3(parsed.workspace) && typeof parsed.workspace.worktreeRoot === "string" ? parsed.workspace.worktreeRoot : DEFAULT_CONFIG.workspace.worktreeRoot,
9880
+ defaultAgent: isRecord3(parsed.workspace) ? parseAgentKind(parsed.workspace.defaultAgent) : DEFAULT_CONFIG.workspace.defaultAgent
9881
+ },
9882
+ profiles: parseProfiles(parsed.profiles, true),
9883
+ services: parseServices(parsed.services),
9884
+ startupEnvs: parseStartupEnvs(parsed.startupEnvs),
9885
+ integrations: {
9886
+ github: {
9887
+ linkedRepos: isRecord3(parsed.integrations) && isRecord3(parsed.integrations.github) ? parseLinkedRepos(parsed.integrations.github.linkedRepos) : isRecord3(parsed.integrations) && Array.isArray(parsed.integrations.github) ? parseLinkedRepos(parsed.integrations.github) : []
9888
+ },
9889
+ linear: {
9890
+ enabled: isRecord3(parsed.integrations) && isRecord3(parsed.integrations.linear) && typeof parsed.integrations.linear.enabled === "boolean" ? parsed.integrations.linear.enabled : DEFAULT_CONFIG.integrations.linear.enabled
9891
+ }
9892
+ },
9893
+ lifecycleHooks: parseLifecycleHooks(parsed.lifecycleHooks),
9894
+ autoName: parseAutoName(parsed.auto_name)
9895
+ };
9896
+ }
9897
+ function defaultConfig() {
9898
+ return parseProjectConfig({});
9899
+ }
9900
+ function loadLocalProjectConfigOverlay(root) {
9901
+ try {
9902
+ const text = readLocalConfigFile(root).trim();
9903
+ if (!text) {
9904
+ return { profiles: {}, lifecycleHooks: {} };
9905
+ }
9906
+ const parsed = parseConfigDocument(text);
9907
+ return {
9908
+ profiles: parseProfiles(parsed.profiles, false),
9909
+ lifecycleHooks: parseLifecycleHooks(parsed.lifecycleHooks)
9910
+ };
9911
+ } catch {
9912
+ return { profiles: {}, lifecycleHooks: {} };
9913
+ }
9914
+ }
9915
+ function mergeHookCommand(projectCommand, localCommand) {
9916
+ if (projectCommand && localCommand) {
9917
+ return ["set -e", projectCommand, localCommand].join(`
9918
+ `);
9919
+ }
9920
+ return localCommand ?? projectCommand;
9921
+ }
9922
+ function mergeLifecycleHooks(projectHooks, localHooks) {
9923
+ const postCreate = mergeHookCommand(projectHooks.postCreate, localHooks.postCreate);
9924
+ const preRemove = mergeHookCommand(projectHooks.preRemove, localHooks.preRemove);
9925
+ return {
9926
+ ...postCreate ? { postCreate } : {},
9927
+ ...preRemove ? { preRemove } : {}
9928
+ };
9929
+ }
9845
9930
  function gitRoot2(dir) {
9846
9931
  const result = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], { stdout: "pipe", stderr: "pipe", cwd: dir });
9847
9932
  if (result.exitCode !== 0)
@@ -9849,37 +9934,31 @@ function gitRoot2(dir) {
9849
9934
  const root = new TextDecoder().decode(result.stdout).trim();
9850
9935
  return root || dir;
9851
9936
  }
9852
- function loadConfig(dir) {
9937
+ function projectRoot(dir) {
9938
+ const result = Bun.spawnSync(["git", "rev-parse", "--git-common-dir"], { stdout: "pipe", stderr: "pipe", cwd: dir });
9939
+ if (result.exitCode !== 0)
9940
+ return gitRoot2(dir);
9941
+ const commonDir = new TextDecoder().decode(result.stdout).trim();
9942
+ return commonDir ? dirname2(resolve4(dir, commonDir)) : gitRoot2(dir);
9943
+ }
9944
+ function loadConfig(dir, options = {}) {
9945
+ const root = options.resolvedRoot ? dir : projectRoot(dir);
9946
+ let projectConfig;
9853
9947
  try {
9854
- const root = gitRoot2(dir);
9855
9948
  const text = readConfigFile(root).trim();
9856
- if (!text)
9857
- return DEFAULT_CONFIG;
9858
- const parsed = $parse(text);
9859
- return {
9860
- name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : DEFAULT_CONFIG.name,
9861
- workspace: {
9862
- mainBranch: isRecord3(parsed.workspace) && typeof parsed.workspace.mainBranch === "string" ? parsed.workspace.mainBranch : DEFAULT_CONFIG.workspace.mainBranch,
9863
- worktreeRoot: isRecord3(parsed.workspace) && typeof parsed.workspace.worktreeRoot === "string" ? parsed.workspace.worktreeRoot : DEFAULT_CONFIG.workspace.worktreeRoot,
9864
- defaultAgent: isRecord3(parsed.workspace) ? parseAgentKind(parsed.workspace.defaultAgent) : DEFAULT_CONFIG.workspace.defaultAgent
9865
- },
9866
- profiles: parseProfiles(parsed.profiles),
9867
- services: parseServices(parsed.services),
9868
- startupEnvs: parseStartupEnvs(parsed.startupEnvs),
9869
- integrations: {
9870
- github: {
9871
- linkedRepos: isRecord3(parsed.integrations) && isRecord3(parsed.integrations.github) ? parseLinkedRepos(parsed.integrations.github.linkedRepos) : isRecord3(parsed.integrations) && Array.isArray(parsed.integrations.github) ? parseLinkedRepos(parsed.integrations.github) : []
9872
- },
9873
- linear: {
9874
- enabled: isRecord3(parsed.integrations) && isRecord3(parsed.integrations.linear) && typeof parsed.integrations.linear.enabled === "boolean" ? parsed.integrations.linear.enabled : DEFAULT_CONFIG.integrations.linear.enabled
9875
- }
9876
- },
9877
- lifecycleHooks: parseLifecycleHooks(parsed.lifecycleHooks),
9878
- autoName: parseAutoName(parsed.auto_name)
9879
- };
9949
+ projectConfig = text ? parseProjectConfig(parseConfigDocument(text)) : defaultConfig();
9880
9950
  } catch {
9881
- return DEFAULT_CONFIG;
9951
+ projectConfig = defaultConfig();
9882
9952
  }
9953
+ const localOverlay = loadLocalProjectConfigOverlay(root);
9954
+ return {
9955
+ ...projectConfig,
9956
+ profiles: {
9957
+ ...cloneProfiles(projectConfig.profiles),
9958
+ ...cloneProfiles(localOverlay.profiles)
9959
+ },
9960
+ lifecycleHooks: mergeLifecycleHooks(projectConfig.lifecycleHooks, localOverlay.lifecycleHooks)
9961
+ };
9883
9962
  }
9884
9963
  function expandTemplate(template, env) {
9885
9964
  return template.replace(/\$\{(\w+)\}/g, (_2, key) => env[key] ?? "");
@@ -9918,7 +9997,7 @@ var init_config = __esm(() => {
9918
9997
 
9919
9998
  // backend/src/adapters/control-token.ts
9920
9999
  import { chmod, mkdir as mkdir2 } from "fs/promises";
9921
- import { dirname as dirname2 } from "path";
10000
+ import { dirname as dirname3 } from "path";
9922
10001
  async function loadControlToken() {
9923
10002
  if (cachedToken)
9924
10003
  return cachedToken;
@@ -9928,7 +10007,7 @@ async function loadControlToken() {
9928
10007
  return cachedToken;
9929
10008
  }
9930
10009
  const controlToken = crypto.randomUUID();
9931
- await mkdir2(dirname2(CONTROL_TOKEN_PATH), { recursive: true });
10010
+ await mkdir2(dirname3(CONTROL_TOKEN_PATH), { recursive: true });
9932
10011
  await Bun.write(CONTROL_TOKEN_PATH, controlToken);
9933
10012
  await chmod(CONTROL_TOKEN_PATH, 384);
9934
10013
  cachedToken = controlToken;
@@ -10290,7 +10369,7 @@ class BunPortProbe {
10290
10369
  this.hostnames = hostnames;
10291
10370
  }
10292
10371
  isListening(port) {
10293
- return new Promise((resolve4) => {
10372
+ return new Promise((resolve5) => {
10294
10373
  let settled = false;
10295
10374
  let pending = this.hostnames.length;
10296
10375
  const settle = (result) => {
@@ -10299,20 +10378,20 @@ class BunPortProbe {
10299
10378
  if (result) {
10300
10379
  settled = true;
10301
10380
  clearTimeout(timer);
10302
- resolve4(true);
10381
+ resolve5(true);
10303
10382
  return;
10304
10383
  }
10305
10384
  pending--;
10306
10385
  if (pending === 0) {
10307
10386
  settled = true;
10308
10387
  clearTimeout(timer);
10309
- resolve4(false);
10388
+ resolve5(false);
10310
10389
  }
10311
10390
  };
10312
10391
  const timer = setTimeout(() => {
10313
10392
  if (!settled) {
10314
10393
  settled = true;
10315
- resolve4(false);
10394
+ resolve5(false);
10316
10395
  }
10317
10396
  }, this.timeoutMs);
10318
10397
  for (const hostname of this.hostnames) {
@@ -10453,7 +10532,7 @@ var init_auto_name_service = __esm(() => {
10453
10532
 
10454
10533
  // backend/src/adapters/agent-runtime.ts
10455
10534
  import { chmod as chmod2, mkdir as mkdir3 } from "fs/promises";
10456
- import { dirname as dirname3, join as join8 } from "path";
10535
+ import { dirname as dirname4, join as join8 } from "path";
10457
10536
  function shellQuote(value) {
10458
10537
  return `'${value.replaceAll("'", "'\\''")}'`;
10459
10538
  }
@@ -10720,7 +10799,7 @@ async function ensureAgentRuntimeArtifacts(input) {
10720
10799
  agentCtlPath: join8(storagePaths.webmuxDir, "webmux-agentctl"),
10721
10800
  claudeSettingsPath: join8(input.worktreePath, ".claude", "settings.local.json")
10722
10801
  };
10723
- await mkdir3(dirname3(artifacts.claudeSettingsPath), { recursive: true });
10802
+ await mkdir3(dirname4(artifacts.claudeSettingsPath), { recursive: true });
10724
10803
  await Bun.write(artifacts.agentCtlPath, buildAgentCtlScript());
10725
10804
  await chmod2(artifacts.agentCtlPath, 493);
10726
10805
  const hookSettings = buildClaudeHookSettings(artifacts);
@@ -10800,7 +10879,7 @@ function resolvePaneStartupCommand(template, commands) {
10800
10879
  return template.command;
10801
10880
  }
10802
10881
  }
10803
- function planSessionLayout(projectRoot, branch, templates, ctx) {
10882
+ function planSessionLayout(projectRoot2, branch, templates, ctx) {
10804
10883
  if (templates.length === 0) {
10805
10884
  throw new Error("At least one pane template is required");
10806
10885
  }
@@ -10821,7 +10900,7 @@ function planSessionLayout(projectRoot, branch, templates, ctx) {
10821
10900
  });
10822
10901
  const focusPaneIndex = panes.find((pane) => pane.focus)?.index ?? 0;
10823
10902
  return {
10824
- sessionName: buildProjectSessionName(projectRoot),
10903
+ sessionName: buildProjectSessionName(projectRoot2),
10825
10904
  windowName: buildWorktreeWindowName(branch),
10826
10905
  shellCommand: ctx.paneCommands.shell,
10827
10906
  panes,
@@ -11018,7 +11097,7 @@ var init_worktree_service = __esm(() => {
11018
11097
  // backend/src/services/lifecycle-service.ts
11019
11098
  import { randomUUID as randomUUID2 } from "crypto";
11020
11099
  import { mkdir as mkdir4 } from "fs/promises";
11021
- import { dirname as dirname4, resolve as resolve4 } from "path";
11100
+ import { dirname as dirname5, resolve as resolve5 } from "path";
11022
11101
  function generateBranchName() {
11023
11102
  return `change-${randomUUID2().slice(0, 8)}`;
11024
11103
  }
@@ -11051,7 +11130,7 @@ class LifecycleService {
11051
11130
  agent,
11052
11131
  phase: "creating_worktree"
11053
11132
  });
11054
- await mkdir4(dirname4(worktreePath), { recursive: true });
11133
+ await mkdir4(dirname5(worktreePath), { recursive: true });
11055
11134
  initialized = await createManagedWorktree({
11056
11135
  repoRoot: this.deps.projectRoot,
11057
11136
  worktreePath,
@@ -11286,17 +11365,17 @@ class LifecycleService {
11286
11365
  return allocateServicePorts(metas, this.deps.config.services);
11287
11366
  }
11288
11367
  resolveWorktreePath(branch) {
11289
- return resolve4(this.deps.projectRoot, this.deps.config.workspace.worktreeRoot, branch);
11368
+ return resolve5(this.deps.projectRoot, this.deps.config.workspace.worktreeRoot, branch);
11290
11369
  }
11291
11370
  listLocalBranches() {
11292
- return this.deps.git.listLocalBranches(resolve4(this.deps.projectRoot));
11371
+ return this.deps.git.listLocalBranches(resolve5(this.deps.projectRoot));
11293
11372
  }
11294
11373
  listCheckedOutBranches() {
11295
- return new Set(this.deps.git.listWorktrees(resolve4(this.deps.projectRoot)).filter((entry) => !entry.bare && entry.branch !== null).map((entry) => entry.branch));
11374
+ return new Set(this.deps.git.listWorktrees(resolve5(this.deps.projectRoot)).filter((entry) => !entry.bare && entry.branch !== null).map((entry) => entry.branch));
11296
11375
  }
11297
11376
  listProjectWorktrees() {
11298
- const projectRoot = resolve4(this.deps.projectRoot);
11299
- return this.deps.git.listWorktrees(projectRoot).filter((entry) => !entry.bare && resolve4(entry.path) !== projectRoot);
11377
+ const projectRoot2 = resolve5(this.deps.projectRoot);
11378
+ return this.deps.git.listWorktrees(projectRoot2).filter((entry) => !entry.bare && resolve5(entry.path) !== projectRoot2);
11300
11379
  }
11301
11380
  async readManagedMetas() {
11302
11381
  const metas = await Promise.all(this.listProjectWorktrees().map(async (entry) => {
@@ -11819,9 +11898,9 @@ var init_project_runtime = __esm(() => {
11819
11898
  });
11820
11899
 
11821
11900
  // backend/src/services/reconciliation-service.ts
11822
- import { basename as basename4, resolve as resolve5 } from "path";
11901
+ import { basename as basename4, resolve as resolve6 } from "path";
11823
11902
  function makeUnmanagedWorktreeId(path) {
11824
- return `unmanaged:${resolve5(path)}`;
11903
+ return `unmanaged:${resolve6(path)}`;
11825
11904
  }
11826
11905
  function isValidPort2(port) {
11827
11906
  return port !== null && Number.isInteger(port) && port >= 1 && port <= 65535;
@@ -11864,7 +11943,7 @@ class ReconciliationService {
11864
11943
  this.deps = deps2;
11865
11944
  }
11866
11945
  async reconcile(repoRoot) {
11867
- const normalizedRepoRoot = resolve5(repoRoot);
11946
+ const normalizedRepoRoot = resolve6(repoRoot);
11868
11947
  const worktrees = this.deps.git.listWorktrees(normalizedRepoRoot);
11869
11948
  const sessionName = buildProjectSessionName(normalizedRepoRoot);
11870
11949
  let windows = [];
@@ -11877,7 +11956,7 @@ class ReconciliationService {
11877
11956
  for (const entry of worktrees) {
11878
11957
  if (entry.bare)
11879
11958
  continue;
11880
- if (resolve5(entry.path) === normalizedRepoRoot)
11959
+ if (resolve6(entry.path) === normalizedRepoRoot)
11881
11960
  continue;
11882
11961
  const gitDir = this.deps.git.resolveWorktreeGitDir(entry.path);
11883
11962
  const meta = await readWorktreeMeta(gitDir);
@@ -11961,8 +12040,8 @@ class WorktreeCreationTracker {
11961
12040
  // backend/src/runtime.ts
11962
12041
  function createWebmuxRuntime(options = {}) {
11963
12042
  const port = options.port ?? parseInt(Bun.env.PORT || "5111", 10);
11964
- const projectDir = gitRoot2(options.projectDir ?? Bun.env.WEBMUX_PROJECT_DIR ?? process.cwd());
11965
- const config = loadConfig(projectDir);
12043
+ const projectDir = projectRoot(options.projectDir ?? Bun.env.WEBMUX_PROJECT_DIR ?? process.cwd());
12044
+ const config = loadConfig(projectDir, { resolvedRoot: true });
11966
12045
  const git = new BunGitGateway;
11967
12046
  const portProbe = new BunPortProbe;
11968
12047
  const tmux = new BunTmuxGateway;
@@ -12035,7 +12114,7 @@ __export(exports_worktree_commands, {
12035
12114
  parseAddCommandArgs: () => parseAddCommandArgs,
12036
12115
  getWorktreeCommandUsage: () => getWorktreeCommandUsage
12037
12116
  });
12038
- import { basename as basename5, resolve as resolve6 } from "path";
12117
+ import { basename as basename5, resolve as resolve7 } from "path";
12039
12118
  function getWorktreeCommandUsage(command) {
12040
12119
  switch (command) {
12041
12120
  case "add":
@@ -12184,8 +12263,8 @@ function parsePruneCommandArgs(args) {
12184
12263
  return true;
12185
12264
  }
12186
12265
  function listProjectWorktrees(runtime) {
12187
- const projectDir = resolve6(runtime.projectDir);
12188
- return runtime.git.listWorktrees(projectDir).filter((entry) => !entry.bare && resolve6(entry.path) !== projectDir);
12266
+ const projectDir = resolve7(runtime.projectDir);
12267
+ return runtime.git.listWorktrees(projectDir).filter((entry) => !entry.bare && resolve7(entry.path) !== projectDir);
12189
12268
  }
12190
12269
  async function defaultConfirmPrune(worktreeCount) {
12191
12270
  const response = await Rt({
@@ -12195,7 +12274,7 @@ async function defaultConfirmPrune(worktreeCount) {
12195
12274
  return !Ct(response) && response;
12196
12275
  }
12197
12276
  function defaultSwitchToTmuxWindow(projectDir, branch) {
12198
- const sessionName = buildProjectSessionName(resolve6(projectDir));
12277
+ const sessionName = buildProjectSessionName(resolve7(projectDir));
12199
12278
  const windowName = buildWorktreeWindowName(branch);
12200
12279
  const target = `${sessionName}:${windowName}`;
12201
12280
  const selectResult = Bun.spawnSync(["tmux", "select-window", "-t", target], {
@@ -12224,7 +12303,7 @@ function defaultSwitchToTmuxWindow(projectDir, branch) {
12224
12303
  }
12225
12304
  }
12226
12305
  async function listWorktrees(runtime, stdout) {
12227
- const projectDir = resolve6(runtime.projectDir);
12306
+ const projectDir = resolve7(runtime.projectDir);
12228
12307
  const entries = listProjectWorktrees(runtime);
12229
12308
  if (entries.length === 0) {
12230
12309
  stdout("No worktrees found.");
@@ -12327,6 +12406,7 @@ async function runWorktreeCommand(context, deps2 = {}) {
12327
12406
  case "open":
12328
12407
  await runtime.lifecycleService.openWorktree(branch);
12329
12408
  stdout(`Opened worktree ${branch}`);
12409
+ switchToTmuxWindow(runtime.projectDir, branch);
12330
12410
  return 0;
12331
12411
  case "close":
12332
12412
  await runtime.lifecycleService.closeWorktree(branch);
@@ -12358,13 +12438,13 @@ var init_worktree_commands = __esm(() => {
12358
12438
  });
12359
12439
 
12360
12440
  // bin/src/webmux.ts
12361
- import { resolve as resolve7, dirname as dirname5, join as join10 } from "path";
12441
+ import { resolve as resolve8, dirname as dirname6, join as join10 } from "path";
12362
12442
  import { existsSync as existsSync5 } from "fs";
12363
12443
  import { fileURLToPath } from "url";
12364
12444
  // package.json
12365
12445
  var package_default = {
12366
12446
  name: "webmux",
12367
- version: "0.13.0",
12447
+ version: "0.15.0",
12368
12448
  description: "Web dashboard for workmux \u2014 browser UI with embedded terminals, PR monitoring, and CI integration",
12369
12449
  type: "module",
12370
12450
  repository: {
@@ -12418,7 +12498,7 @@ var package_default = {
12418
12498
  };
12419
12499
 
12420
12500
  // bin/src/webmux.ts
12421
- var PKG_ROOT = resolve7(dirname5(fileURLToPath(import.meta.url)), "..");
12501
+ var PKG_ROOT = resolve8(dirname6(fileURLToPath(import.meta.url)), "..");
12422
12502
  function usage2() {
12423
12503
  console.log(`
12424
12504
  webmux \u2014 Dev dashboard for managing Git worktrees
@@ -12586,8 +12666,8 @@ async function main(args = process.argv.slice(2)) {
12586
12666
  const code = await proc.exited;
12587
12667
  process.exit(code);
12588
12668
  }
12589
- await loadEnvFile(resolve7(process.cwd(), ".env.local"));
12590
- await loadEnvFile(resolve7(process.cwd(), ".env"));
12669
+ await loadEnvFile(resolve8(process.cwd(), ".env.local"));
12670
+ await loadEnvFile(resolve8(process.cwd(), ".env"));
12591
12671
  if (isWorktreeCommand(parsed.command)) {
12592
12672
  const { runWorktreeCommand: runWorktreeCommand2 } = await Promise.resolve().then(() => (init_worktree_commands(), exports_worktree_commands));
12593
12673
  const exitCode = await runWorktreeCommand2({
@@ -12602,7 +12682,7 @@ async function main(args = process.argv.slice(2)) {
12602
12682
  usage2();
12603
12683
  process.exit(0);
12604
12684
  }
12605
- if (!existsSync5(resolve7(process.cwd(), ".webmux.yaml"))) {
12685
+ if (!existsSync5(resolve8(process.cwd(), ".webmux.yaml"))) {
12606
12686
  console.error("No .webmux.yaml found in this directory.\nRun `webmux init` to set up your project.");
12607
12687
  process.exit(1);
12608
12688
  }
@@ -0,0 +1,32 @@
1
+ /*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#0d1117;--color-sidebar:#161b22;--color-topbar:#1c2128;--color-hover:#21262d;--color-active:#1f6feb33;--color-edge:#30363d;--color-primary:#e6edf3;--color-muted:#8b949e;--color-accent:#58a6ff;--color-merged:#a78bfa;--color-danger:#f85149;--color-success:#3fb950;--color-warning:#d29922}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.-top-0\.5{top:calc(var(--spacing) * -.5)}.top-2{top:calc(var(--spacing) * 2)}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.bottom-4{bottom:calc(var(--spacing) * 4)}.z-40{z-index:40}.z-50{z-index:50}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.-mt-2{margin-top:calc(var(--spacing) * -2)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-3{margin-left:calc(var(--spacing) * 3)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-dvh{height:100dvh}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[300px\]{max-height:300px}.max-h-\[400px\]{max-height:400px}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-12{min-height:calc(var(--spacing) * 12)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-\[90\%\]{width:90%}.w-\[220px\]{width:220px}.w-full{width:100%}.max-w-\[90\%\]{max-width:90%}.max-w-\[380px\]{max-width:380px}.max-w-\[560px\]{max-width:560px}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[220px\]{min-width:220px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-y{resize:vertical}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-accent{border-color:var(--color-accent)}.border-accent\/40{border-color:#58a6ff66}@supports (color:color-mix(in lab,red,red)){.border-accent\/40{border-color:color-mix(in oklab,var(--color-accent) 40%,transparent)}}.border-danger{border-color:var(--color-danger)}.border-danger\/35{border-color:#f8514959}@supports (color:color-mix(in lab,red,red)){.border-danger\/35{border-color:color-mix(in oklab,var(--color-danger) 35%,transparent)}}.border-danger\/40{border-color:#f8514966}@supports (color:color-mix(in lab,red,red)){.border-danger\/40{border-color:color-mix(in oklab,var(--color-danger) 40%,transparent)}}.border-edge{border-color:var(--color-edge)}.border-merged\/35{border-color:#a78bfa59}@supports (color:color-mix(in lab,red,red)){.border-merged\/35{border-color:color-mix(in oklab,var(--color-merged) 35%,transparent)}}.border-success\/30{border-color:#3fb9504d}@supports (color:color-mix(in lab,red,red)){.border-success\/30{border-color:color-mix(in oklab,var(--color-success) 30%,transparent)}}.border-success\/40{border-color:#3fb95066}@supports (color:color-mix(in lab,red,red)){.border-success\/40{border-color:color-mix(in oklab,var(--color-success) 40%,transparent)}}.border-transparent{border-color:#0000}.border-warning\/40{border-color:#d2992266}@supports (color:color-mix(in lab,red,red)){.border-warning\/40{border-color:color-mix(in oklab,var(--color-warning) 40%,transparent)}}.bg-accent{background-color:var(--color-accent)}.bg-accent\/10{background-color:#58a6ff1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-active{background-color:var(--color-active)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-danger{background-color:var(--color-danger)}.bg-danger\/5{background-color:#f851490d}@supports (color:color-mix(in lab,red,red)){.bg-danger\/5{background-color:color-mix(in oklab,var(--color-danger) 5%,transparent)}}.bg-danger\/15{background-color:#f8514926}@supports (color:color-mix(in lab,red,red)){.bg-danger\/15{background-color:color-mix(in oklab,var(--color-danger) 15%,transparent)}}.bg-danger\/20{background-color:#f8514933}@supports (color:color-mix(in lab,red,red)){.bg-danger\/20{background-color:color-mix(in oklab,var(--color-danger) 20%,transparent)}}.bg-hover{background-color:var(--color-hover)}.bg-merged\/8{background-color:#a78bfa14}@supports (color:color-mix(in lab,red,red)){.bg-merged\/8{background-color:color-mix(in oklab,var(--color-merged) 8%,transparent)}}.bg-merged\/20{background-color:#a78bfa33}@supports (color:color-mix(in lab,red,red)){.bg-merged\/20{background-color:color-mix(in oklab,var(--color-merged) 20%,transparent)}}.bg-muted{background-color:var(--color-muted)}.bg-muted\/20{background-color:#8b949e33}@supports (color:color-mix(in lab,red,red)){.bg-muted\/20{background-color:color-mix(in oklab,var(--color-muted) 20%,transparent)}}.bg-sidebar{background-color:var(--color-sidebar)}.bg-success{background-color:var(--color-success)}.bg-success\/5{background-color:#3fb9500d}@supports (color:color-mix(in lab,red,red)){.bg-success\/5{background-color:color-mix(in oklab,var(--color-success) 5%,transparent)}}.bg-success\/15{background-color:#3fb95026}@supports (color:color-mix(in lab,red,red)){.bg-success\/15{background-color:color-mix(in oklab,var(--color-success) 15%,transparent)}}.bg-success\/20{background-color:#3fb95033}@supports (color:color-mix(in lab,red,red)){.bg-success\/20{background-color:color-mix(in oklab,var(--color-success) 20%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface\/60{background-color:#0d111799}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-topbar{background-color:var(--color-topbar)}.bg-transparent{background-color:#0000}.bg-warning{background-color:var(--color-warning)}.bg-warning\/5{background-color:#d299220d}@supports (color:color-mix(in lab,red,red)){.bg-warning\/5{background-color:color-mix(in oklab,var(--color-warning) 5%,transparent)}}.bg-warning\/15{background-color:#d2992226}@supports (color:color-mix(in lab,red,red)){.bg-warning\/15{background-color:color-mix(in oklab,var(--color-warning) 15%,transparent)}}.p-0{padding:calc(var(--spacing) * 0)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.pr-5{padding-right:calc(var(--spacing) * 5)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-accent\/60{color:#58a6ff99}@supports (color:color-mix(in lab,red,red)){.text-accent\/60{color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.text-accent\/80{color:#58a6ffcc}@supports (color:color-mix(in lab,red,red)){.text-accent\/80{color:color-mix(in oklab,var(--color-accent) 80%,transparent)}}.text-danger{color:var(--color-danger)}.text-inherit{color:inherit}.text-merged{color:var(--color-merged)}.text-muted{color:var(--color-muted)}.text-muted\/50{color:#8b949e80}@supports (color:color-mix(in lab,red,red)){.text-muted\/50{color:color-mix(in oklab,var(--color-muted) 50%,transparent)}}.text-primary{color:var(--color-primary)}.text-primary\/80{color:#e6edf3cc}@supports (color:color-mix(in lab,red,red)){.text-primary\/80{color:color-mix(in oklab,var(--color-primary) 80%,transparent)}}.text-success{color:var(--color-success)}.text-warning{color:var(--color-warning)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.no-underline{text-decoration-line:none}.accent-\[var\(--accent\)\]{accent-color:var(--accent)}.accent-accent{accent-color:var(--color-accent)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-muted::placeholder{color:var(--color-muted)}.placeholder\:text-muted\/50::placeholder{color:#8b949e80}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-muted\/50::placeholder{color:color-mix(in oklab,var(--color-muted) 50%,transparent)}}@media(hover:hover){.hover\:border-edge:hover{border-color:var(--color-edge)}.hover\:bg-accent\/10:hover{background-color:#58a6ff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/10:hover{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.hover\:bg-danger\/10:hover{background-color:#f851491a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-danger\/10:hover{background-color:color-mix(in oklab,var(--color-danger) 10%,transparent)}}.hover\:bg-hover:hover{background-color:var(--color-hover)}.hover\:text-danger:hover{color:var(--color-danger)}.hover\:text-primary:hover{color:var(--color-primary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus-visible\:z-10:focus-visible{z-index:10}.focus-visible\:bg-hover:focus-visible{background-color:var(--color-hover)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent:focus-visible{--tw-ring-color:var(--color-accent)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-inset:focus-visible{--tw-ring-inset:inset}.active\:bg-active:active{background-color:var(--color-active)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:max-w-\[440px\]{max-width:440px}}}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif}dialog[open]{margin:auto}dialog::backdrop{background:#0009}dialog textarea{font-family:inherit}dialog textarea:focus{border-color:var(--color-accent);outline:none}@keyframes spin{to{transform:rotate(360deg)}}.spinner{border:1.5px solid;border-top-color:#0000;border-radius:50%;width:12px;height:12px;animation:.6s linear infinite spin;display:inline-block}.xterm{width:100%;height:100%}.xterm .xterm-viewport{overscroll-behavior-y:contain}@media(max-width:768px){html,body{width:100%;height:100dvh;position:fixed;overflow:hidden}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@keyframes pulse{50%{opacity:.5}}.more-dropdown.svelte-gwjq7z{position:absolute;top:100%;right:0;margin-top:.25rem;width:max-content;max-width:80vw;border-radius:.5rem;border:1px solid var(--color-edge);background:var(--color-topbar);box-shadow:0 4px 12px #0006;z-index:50}.bell-dropdown.svelte-gwjq7z{position:absolute;top:100%;right:0;margin-top:.25rem;width:18rem;border-radius:.5rem;border:1px solid var(--color-edge);background:var(--color-topbar);box-shadow:0 4px 12px #0006;z-index:50}/**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.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{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.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;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.pane-bar.svelte-19vlwaa{padding-bottom:env(safe-area-inset-bottom,0px)}.pane-active.svelte-19vlwaa{box-shadow:inset 0 2px 0 0 var(--color-accent)}.toast.svelte-1974bvt{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;border-radius:.5rem;border:1px solid var(--color-edge);background:var(--color-topbar);box-shadow:0 4px 12px #0006;animation:svelte-1974bvt-slide-in .2s ease-out}@keyframes svelte-1974bvt-slide-in{0%{opacity:0;transform:translate(1rem)}to{opacity:1;transform:translate(0)}}