babavoss 0.12.6 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/gui/babavoss-web.js +4 -1
  2. package/gui/{chunk-9gmmm54v.js → chunk-qgzmajan.js} +619 -574
  3. package/gui/gui.js +1538 -277
  4. package/gui/theme.css +2007 -1128
  5. package/index.ts +1 -1
  6. package/package.json +5 -5
  7. package/src/{promptware → agents}/compile.ts +23 -31
  8. package/src/{promptware → agents}/define.ts +2 -2
  9. package/src/{promptware → agents}/disk.ts +7 -4
  10. package/src/{promptware → agents}/sync.ts +2 -2
  11. package/src/{promptware → agents}/system.ts +17 -17
  12. package/src/baba/check.ts +3 -3
  13. package/src/baba/config.ts +5 -2
  14. package/src/baba/init.ts +13 -18
  15. package/src/baba/node.ts +2 -2
  16. package/src/baba/project.ts +2 -2
  17. package/src/baba/requirements.ts +39 -0
  18. package/src/baba/setup.ts +30 -0
  19. package/src/baba/worker.ts +13 -5
  20. package/src/build/mdx.ts +4 -4
  21. package/src/build/project.ts +6 -1
  22. package/src/build/views.ts +3 -3
  23. package/src/desktop/desktop.css +35 -39
  24. package/src/desktop/index.ts +9 -7
  25. package/src/desktop/keys.ts +153 -0
  26. package/src/desktop/view.tsx +174 -130
  27. package/src/door/core.ts +2 -2
  28. package/src/ecs/baba.ts +27 -17
  29. package/src/generated/build.ts +1 -1
  30. package/src/gui/gui.tsx +21 -31
  31. package/src/gui/index.ts +6 -2
  32. package/src/gui/{promptware.tsx → instructions.tsx} +12 -12
  33. package/src/gui/levels.tsx +241 -59
  34. package/src/gui/lockup.ts +11 -0
  35. package/src/gui/settings.tsx +241 -0
  36. package/src/gui/setup.tsx +77 -0
  37. package/src/gui/theme.css +205 -58
  38. package/src/gui/theme.ts +9 -5
  39. package/src/gui/voss-settings.ts +48 -0
  40. package/src/gui/wizard.tsx +17 -40
  41. package/src/guide/add-a-desktop.mdx +7 -7
  42. package/src/guide/index.ts +4 -4
  43. package/src/guide/write-a-system.mdx +3 -3
  44. package/src/guide/{write-promptware.mdx → write-agentic-instructions.mdx} +17 -24
  45. package/src/http/server.ts +140 -17
  46. package/src/{prompt → instructions}/evals.ts +1 -1
  47. package/src/{prompt → instructions}/index.ts +9 -8
  48. package/src/{prompt → instructions}/jsx-runtime.ts +3 -3
  49. package/src/mcp/main.ts +3 -3
  50. package/src/mcp/tools.ts +4 -4
  51. package/src/runtime/harness.ts +2 -2
  52. package/src/server/edge.ts +31 -3
  53. package/src/server/main.ts +2 -1
  54. package/src/server/messages.ts +47 -7
  55. package/src/shell/run.ts +18 -18
  56. package/src/spec/index.ts +1 -1
  57. package/src/web/core.tsx +2 -2
  58. package/src/web/index.tsx +1 -1
  59. package/src/desktop/bob.ts +0 -76
  60. /package/src/{promptware → agents}/markdown.d.ts +0 -0
  61. /package/src/{prompt → instructions}/jsx-dev-runtime.ts +0 -0
  62. /package/src/{prompt → instructions}/mdx.d.ts +0 -0
@@ -2783,7 +2783,7 @@ function dataOf(v) {
2783
2783
  var nameOf = (v) => dataOf(v).name;
2784
2784
  var NAME2 = /^[a-z][a-zA-Z0-9-]*$/;
2785
2785
  var SYSTEM_NAME = /^[a-z][a-z0-9-]*$/;
2786
- var RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "promptware", "init", "babas", "service", "gui"]);
2786
+ var RESERVED = new Set(["start", "stop", "status", "state", "follow", "run", "raw", "secret", "help", "mcp", "check", "install", "add-system", "serve", "agents", "init", "babas", "service", "gui"]);
2787
2787
  function declareModel(name, model) {
2788
2788
  if (typeof name !== "string" || !SYSTEM_NAME.test(name))
2789
2789
  throw new Error(`system ${JSON.stringify(name)}: lowercase letters, digits and dashes`);
@@ -2883,6 +2883,8 @@ function closureOf(systems) {
2883
2883
  return out;
2884
2884
  }
2885
2885
  function baba(def) {
2886
+ if ("promptware" in def)
2887
+ throw new Error("baba({ promptware }) is gone: the agentic instructions are the .mdx files under .baba/agents/, which voss reads on its own; see the write-agentic-instructions skill");
2886
2888
  const harness = harnessOf(def.harness);
2887
2889
  if (def.tick !== undefined && !(Number.isInteger(def.tick) && def.tick > 0))
2888
2890
  throw new Error("baba: tick is whole milliseconds, above zero");
@@ -2942,7 +2944,7 @@ function baba(def) {
2942
2944
  if (e)
2943
2945
  throw new Error(`app ${v.key}: ${e}`);
2944
2946
  }
2945
- return Object.freeze({ kind: "baba", systems: def.systems, apps: apps.map((v) => ({ ...v, ...v.systems ? { systems: [...v.systems] } : {}, ...v.routes ? { routes: [...v.routes] } : {} })), $app: undefined, $routes: undefined, tick: def.tick ?? null, promptware: [...def.promptware ?? []], harness, model, actions, queries });
2947
+ return Object.freeze({ kind: "baba", systems: def.systems, apps: apps.map((v) => ({ ...v, ...v.systems ? { systems: [...v.systems] } : {}, ...v.routes ? { routes: [...v.routes] } : {} })), $app: undefined, $routes: undefined, tick: def.tick ?? null, instructions: [...def.instructions ?? []], harness, model, actions, queries });
2946
2948
  }
2947
2949
  function isSystem(v) {
2948
2950
  return typeof v === "object" && v !== null && SYSTEM in v;
@@ -5028,93 +5030,151 @@ function useBabaState(v) {
5028
5030
  // src/desktop/view.tsx
5029
5031
  var import_react7 = __toESM(require_react(), 1);
5030
5032
 
5031
- // src/desktop/bob.ts
5032
- var gravity = 9000;
5033
- var catchSpring = { stiffness: 1400, damping: 28 };
5034
- var glideSpring = { stiffness: 1100, damping: 40 };
5035
- var swingSpring = { stiffness: 1600, damping: 56 };
5036
- var axis = (at, v, to, s2, dt) => {
5037
- const a = s2.stiffness * (to - at) - s2.damping * v;
5038
- const nv = v + a * dt;
5039
- return [at + nv * dt, nv];
5033
+ // src/desktop/keys.ts
5034
+ var keyCommands = [
5035
+ { id: "launcher", title: "Quick launcher", summary: "Open the launcher over the app in front, and close it again.", default: "Mod+P" },
5036
+ { id: "previousTab", title: "Previous tab", summary: "Go to the tab on the left; from the first, the launcher's.", default: "Mod+J" },
5037
+ { id: "nextTab", title: "Next tab", summary: "Go to the tab on the right; from the last, the launcher's.", default: "Mod+K" },
5038
+ { id: "closeTab", title: "Close tab", summary: "Close the window in front; its × and a middle click do too.", default: null }
5039
+ ];
5040
+ var modifiers = ["Mod", "Ctrl", "Alt", "Shift", "Meta"];
5041
+ var named = {
5042
+ Minus: "-",
5043
+ Equal: "=",
5044
+ BracketLeft: "[",
5045
+ BracketRight: "]",
5046
+ Backslash: "\\",
5047
+ Semicolon: ";",
5048
+ Quote: "'",
5049
+ Backquote: "`",
5050
+ Comma: ",",
5051
+ Period: ".",
5052
+ Slash: "/",
5053
+ ArrowLeft: "Left",
5054
+ ArrowRight: "Right",
5055
+ ArrowUp: "Up",
5056
+ ArrowDown: "Down",
5057
+ Space: "Space",
5058
+ Tab: "Tab",
5059
+ Enter: "Enter",
5060
+ Backspace: "Backspace",
5061
+ Delete: "Delete",
5062
+ Home: "Home",
5063
+ End: "End",
5064
+ PageUp: "PageUp",
5065
+ PageDown: "PageDown"
5040
5066
  };
5041
- function step(s2, aim, dt) {
5042
- const [x, vx] = axis(s2.at.x, s2.v.x, aim.x, s2.phase === "gliding" ? glideSpring : swingSpring, dt);
5043
- if (s2.phase === "falling") {
5044
- const vy2 = s2.v.y + gravity * dt, y2 = s2.at.y + vy2 * dt;
5045
- return y2 < aim.y ? { at: { x, y: y2 }, v: { x: vx, y: vy2 }, phase: "falling" } : { at: { x, y: aim.y }, v: { x: vx, y: vy2 }, phase: "caught" };
5067
+ var keyOfCode = (code) => {
5068
+ const m = /^(?:Key([A-Z])|Digit([0-9])|Numpad([0-9])|(F(?:[1-9]|1[0-2])))$/.exec(code);
5069
+ if (m)
5070
+ return m[1] ?? m[2] ?? m[3] ?? m[4];
5071
+ return named[code] ?? null;
5072
+ };
5073
+ var keys = new Set([..."ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", ...Object.values(named), ...Array.from({ length: 12 }, (_, i) => `F${i + 1}`)]);
5074
+ function parseBinding(text) {
5075
+ const parts = text.split("+").map((p) => p.trim());
5076
+ if (parts.some((p) => !p))
5077
+ return null;
5078
+ const key = parts.pop();
5079
+ const k = key.length === 1 ? key.toUpperCase() : key;
5080
+ const mods = [];
5081
+ for (const p of parts) {
5082
+ const m = modifiers.find((x) => x.toLowerCase() === p.toLowerCase());
5083
+ if (!m || mods.includes(m))
5084
+ return null;
5085
+ mods.push(m);
5046
5086
  }
5047
- const [y, vy] = axis(s2.at.y, s2.v.y, aim.y, s2.phase === "caught" ? catchSpring : glideSpring, dt);
5048
- return { at: { x, y }, v: { x: vx, y: vy }, phase: s2.phase };
5087
+ if (!keys.has(k))
5088
+ return null;
5089
+ if (!mods.some((m) => m !== "Shift") && !/^F\d+$/.test(k))
5090
+ return null;
5091
+ return { mods: modifiers.filter((m) => mods.includes(m)), key: k };
5049
5092
  }
5050
- var resting = (s2, aim) => s2.phase !== "falling" && Math.abs(s2.at.x - aim.x) < 0.05 && Math.abs(s2.at.y - aim.y) < 0.05 && Math.hypot(s2.v.x, s2.v.y) < 2;
5051
-
5052
- class Bob {
5053
- draw;
5054
- still;
5055
- state = null;
5056
- aim = { x: 0, y: 0 };
5057
- frame = 0;
5058
- last = 0;
5059
- constructor(draw, still = () => false) {
5060
- this.draw = draw;
5061
- this.still = still;
5062
- }
5063
- drop(from) {
5064
- this.state = { at: { ...from }, v: { x: 0, y: 0 }, phase: "falling" };
5065
- this.draw(from);
5066
- this.wake();
5067
- }
5068
- to(aim, now = false) {
5069
- const moved = Math.abs(aim.y - this.aim.y) > 0.5;
5070
- this.aim = aim;
5071
- if (!this.state || now || this.still()) {
5072
- this.state = { at: { ...aim }, v: { x: 0, y: 0 }, phase: "gliding" };
5073
- this.draw(aim);
5074
- return;
5075
- }
5076
- if (this.state.phase === "caught" && moved)
5077
- this.state = { ...this.state, phase: "gliding" };
5078
- this.wake();
5079
- }
5080
- stop() {
5081
- cancelAnimationFrame(this.frame);
5082
- this.frame = 0;
5083
- this.state = null;
5084
- }
5085
- wake() {
5086
- if (this.frame)
5087
- return;
5088
- this.last = performance.now();
5089
- const tick = (now) => {
5090
- let left = Math.min(0.05, (now - this.last) / 1000);
5091
- this.last = now;
5092
- while (left > 0 && this.state) {
5093
- const dt = Math.min(left, 1 / 240);
5094
- this.state = step(this.state, this.aim, dt);
5095
- left -= dt;
5096
- }
5097
- if (!this.state) {
5098
- this.frame = 0;
5099
- return;
5100
- }
5101
- if (resting(this.state, this.aim)) {
5102
- this.state = { at: { ...this.aim }, v: { x: 0, y: 0 }, phase: "gliding" };
5103
- this.draw(this.aim);
5104
- this.frame = 0;
5105
- return;
5106
- }
5107
- this.draw(this.state.at);
5108
- this.frame = requestAnimationFrame(tick);
5109
- };
5110
- this.frame = requestAnimationFrame(tick);
5111
- }
5093
+ function normalBinding(text) {
5094
+ const b = parseBinding(text);
5095
+ return b ? [...b.mods, b.key].join("+") : null;
5096
+ }
5097
+ var isMac = () => {
5098
+ const nav = globalThis.navigator;
5099
+ return !!nav && /mac|iphone|ipad/i.test(nav.userAgentData?.platform ?? nav.platform ?? nav.userAgent ?? "");
5100
+ };
5101
+ function bindingOf(e, mac = isMac()) {
5102
+ const key = keyOfCode(e.code);
5103
+ if (!key)
5104
+ return null;
5105
+ const mods = [];
5106
+ if (mac ? e.metaKey : e.ctrlKey)
5107
+ mods.push("Mod");
5108
+ if (mac && e.ctrlKey)
5109
+ mods.push("Ctrl");
5110
+ if (e.altKey)
5111
+ mods.push("Alt");
5112
+ if (e.shiftKey)
5113
+ mods.push("Shift");
5114
+ if (!mac && e.metaKey)
5115
+ mods.push("Meta");
5116
+ return [...mods, key].join("+");
5117
+ }
5118
+ function keymapOf(overrides) {
5119
+ return Object.fromEntries(keyCommands.map((c) => [c.id, overrides?.[c.id] ?? c.default]));
5120
+ }
5121
+ function bindingOn(binding, mac = isMac()) {
5122
+ const b = parseBinding(binding);
5123
+ if (!b)
5124
+ return null;
5125
+ const same2 = mac ? "Meta" : "Ctrl";
5126
+ const mods = new Set(b.mods.map((m) => m === same2 ? "Mod" : m));
5127
+ return [...modifiers.filter((m) => mods.has(m)), b.key].join("+");
5128
+ }
5129
+ function commandOf(keymap, e, mac = isMac()) {
5130
+ const b = bindingOf(e, mac);
5131
+ if (!b)
5132
+ return null;
5133
+ return keyCommands.find((c) => keymap[c.id] !== null && bindingOn(keymap[c.id], mac) === b)?.id ?? null;
5134
+ }
5135
+ function formatBinding(text, mac = isMac()) {
5136
+ const b = parseBinding(text);
5137
+ if (!b)
5138
+ return text;
5139
+ const arrows = { Left: "←", Right: "→", Up: "↑", Down: "↓" };
5140
+ if (mac) {
5141
+ const sign = { Mod: "⌘", Ctrl: "⌃", Alt: "⌥", Shift: "⇧", Meta: "⌘" };
5142
+ return [...b.mods.map((m) => sign[m]), arrows[b.key] ?? b.key].join("");
5143
+ }
5144
+ const word = { Mod: "Ctrl", Ctrl: "Ctrl", Alt: "Alt", Shift: "Shift", Meta: "Win" };
5145
+ return [...b.mods.map((m) => word[m]), arrows[b.key] ?? b.key].join("+");
5146
+ }
5147
+ var browserKeys = {
5148
+ "Mod+L": "the browser's address bar",
5149
+ "Mod+T": "a new browser tab",
5150
+ "Mod+N": "a new browser window",
5151
+ "Mod+Shift+N": "a private window",
5152
+ "Mod+Shift+T": "reopening a closed tab",
5153
+ "Mod+Q": "quitting the browser",
5154
+ "Mod+R": "reloading the page",
5155
+ "Mod+F": "finding in the page",
5156
+ "Mod+W": "closing the browser tab, or the app the page runs in",
5157
+ "Mod+Shift+W": "closing the browser window",
5158
+ "Mod+Tab": "switching browser tabs",
5159
+ "Ctrl+Tab": "switching browser tabs",
5160
+ "Mod+C": "copy",
5161
+ "Mod+V": "paste",
5162
+ "Mod+X": "cut",
5163
+ "Mod+Z": "undo",
5164
+ "Mod+A": "select all",
5165
+ ...Object.fromEntries(Array.from({ length: 9 }, (_, i) => [`Mod+${i + 1}`, "switching browser tabs"]))
5166
+ };
5167
+ function bindingNote(binding) {
5168
+ const n = normalBinding(binding);
5169
+ const why = n && browserKeys[n];
5170
+ return why ? `Also ${why}: the browser may keep it.` : null;
5112
5171
  }
5113
5172
 
5114
5173
  // src/desktop/view.tsx
5115
5174
  var jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
5116
5175
  var plusIcon = [{ tag: "path", attrs: { d: "M5 12h14" } }, { tag: "path", attrs: { d: "M12 5v14" } }];
5117
- var leaveIcon = [{ tag: "path", attrs: { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" } }, { tag: "polyline", attrs: { points: "16 17 21 12 16 7" } }, { tag: "line", attrs: { x1: "21", x2: "9", y1: "12", y2: "12" } }];
5176
+ var leaveIcon = [{ tag: "path", attrs: { d: "M15 21h4a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2h-4" } }, { tag: "polyline", attrs: { points: "8 17 3 12 8 7" } }, { tag: "line", attrs: { x1: "3", x2: "15", y1: "12", y2: "12" } }];
5177
+ var searchIcon = [{ tag: "circle", attrs: { cx: "11", cy: "11", r: "8" } }, { tag: "path", attrs: { d: "m21 21-4.3-4.3" } }];
5118
5178
  var sessionIcon = [{ tag: "rect", attrs: { x: "3", y: "5", width: "18", height: "14", rx: "2" } }, { tag: "path", attrs: { d: "M3 9.5h18" } }];
5119
5179
  var OWN = "~";
5120
5180
  var sessionKey = (scope) => `voss.session.${scope}`;
@@ -5172,7 +5232,7 @@ function addressedSession() {
5172
5232
  const old = parseDesktopHash(location.hash)?.session;
5173
5233
  return old === OWN ? undefined : old;
5174
5234
  }
5175
- function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", address = true, base: givenBase, root }) {
5235
+ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", address = true, base: givenBase, root, settings: person }) {
5176
5236
  const renderers = apps ?? surfaces ?? {};
5177
5237
  const scope = useScope();
5178
5238
  const base = givenBase ?? `/${encodeURIComponent(scope)}`;
@@ -5181,7 +5241,9 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5181
5241
  const iconRows = useColumn(selectors.icons, system.desktopIcon);
5182
5242
  const sessions = useColumn(selectors.sessions, system.desktopSession);
5183
5243
  const windows = useColumn(selectors.windows, system.desktopWindow);
5184
- const theme = useResource(system.desktopSettings)?.theme ?? "system";
5244
+ const babaTheme = useResource(system.desktopSettings)?.theme;
5245
+ const remembered = address ? document.documentElement.dataset.theme : undefined;
5246
+ const theme = person?.theme ?? (address ? remembered ?? "system" : babaTheme ?? "system");
5185
5247
  const { fire, ready, connected } = useBaba();
5186
5248
  const [error, setError] = import_react7.useState(null);
5187
5249
  const [key, setKey] = import_react7.useState(() => fixed ?? (address ? addressedSession() : undefined) ?? rememberedSession(scope));
@@ -5212,7 +5274,8 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5212
5274
  const s2 = front && appRows.get(front.app);
5213
5275
  return { entity, key: value.key, touched: value.touched ?? 0, windows: wins.length, front: front ? front.title || (front.path === "/" ? s2?.title ?? null : front.path) : null };
5214
5276
  }).sort((a, b) => b.touched - a.touched || b.entity - a.entity),
5215
- theme
5277
+ theme,
5278
+ ...person?.keys ? { keys: person.keys } : {}
5216
5279
  } : null;
5217
5280
  const hand = { fire: (name, args) => {
5218
5281
  setError(null);
@@ -5260,7 +5323,15 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5260
5323
  const agreed = import_react7.useRef(null);
5261
5324
  const pending = import_react7.useRef(null);
5262
5325
  const initialized = import_react7.useRef(false);
5263
- const [launch, setLaunch] = import_react7.useState(0);
5326
+ const first = import_react7.useMemo(() => address ? parseDesktopAddress(base, location.pathname) : null, []);
5327
+ const [launch, setLaunch] = import_react7.useState(first && !first.app ? 1 : 0);
5328
+ const [arriving, setArriving] = import_react7.useState(!!first?.app);
5329
+ import_react7.useEffect(() => {
5330
+ if (!arriving)
5331
+ return;
5332
+ const t = setTimeout(() => setArriving(false), 2000);
5333
+ return () => clearTimeout(t);
5334
+ }, [arriving]);
5264
5335
  const [dismiss, setDismiss] = import_react7.useState(0);
5265
5336
  const go = (next) => {
5266
5337
  setError(null);
@@ -5304,10 +5375,12 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5304
5375
  return;
5305
5376
  }
5306
5377
  pending.current = expected;
5378
+ setArriving(true);
5307
5379
  fire("desktop-open", { session: key, app: route2.app, path }).catch((e) => {
5308
5380
  if (!live)
5309
5381
  return;
5310
5382
  pending.current = null;
5383
+ setArriving(false);
5311
5384
  setError(String(e));
5312
5385
  if (current.current)
5313
5386
  history.replaceState(null, "", current.current);
@@ -5327,8 +5400,11 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5327
5400
  import_react7.useEffect(() => {
5328
5401
  if (!address || !initialized.current || !here)
5329
5402
  return;
5403
+ if (pending.current && here === desktopAddress(base))
5404
+ return;
5330
5405
  const asked = pending.current === here;
5331
5406
  pending.current = null;
5407
+ setArriving(false);
5332
5408
  if (here === agreed.current)
5333
5409
  return;
5334
5410
  agreed.current = here;
@@ -5339,10 +5415,21 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5339
5415
  history.pushState(null, "", here);
5340
5416
  }
5341
5417
  }, [address, here, base]);
5342
- const launcherShown = (open) => {
5343
- if (open || !address || !current.current || current.current !== agreed.current)
5418
+ const launcherShown = (mode) => {
5419
+ if (!address || !current.current)
5420
+ return;
5421
+ const at = location.pathname === `${base}/`;
5422
+ if (mode === "tab") {
5423
+ if (!at)
5424
+ history.pushState({ launcher: true, over: current.current }, "", `${base}/`);
5425
+ return;
5426
+ }
5427
+ if (current.current !== agreed.current || !at || current.current === `${base}/`)
5344
5428
  return;
5345
- if (location.pathname === `${base}/` && current.current !== `${base}/`)
5429
+ const entry = history.state;
5430
+ if (entry?.launcher && entry.over === current.current)
5431
+ history.back();
5432
+ else
5346
5433
  history.replaceState(null, "", current.current);
5347
5434
  };
5348
5435
  return /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
@@ -5368,9 +5455,12 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5368
5455
  title,
5369
5456
  hand,
5370
5457
  root,
5458
+ documentTitle: address,
5459
+ launcherHref: address ? `${base}/` : undefined,
5371
5460
  go: fixed ? undefined : go,
5372
5461
  launch,
5373
5462
  dismiss,
5463
+ arriving,
5374
5464
  onLauncher: launcherShown,
5375
5465
  render: (app, props2) => {
5376
5466
  const View = renderers[app.key];
@@ -5385,17 +5475,42 @@ function Desktop({ system, apps, surfaces, session: fixed, title = "Desktop", ad
5385
5475
  ]
5386
5476
  });
5387
5477
  }
5388
- }) : /* @__PURE__ */ jsx_runtime4.jsx("p", {
5389
- className: "desktop-empty",
5390
- role: "status",
5391
- children: "Opening desktop…"
5478
+ }) : /* @__PURE__ */ jsx_runtime4.jsx(DesktopSkeleton, {
5479
+ title
5392
5480
  })
5393
5481
  ]
5394
5482
  });
5395
5483
  }
5396
- function Eyes({ size = 16, large = true, settle = false }) {
5484
+ function DesktopSkeleton({ title }) {
5485
+ return /* @__PURE__ */ jsx_runtime4.jsx("div", {
5486
+ className: "desktop",
5487
+ "aria-busy": "true",
5488
+ "aria-label": "Opening the desktop",
5489
+ children: /* @__PURE__ */ jsx_runtime4.jsx("header", {
5490
+ className: "desktop-bar",
5491
+ children: /* @__PURE__ */ jsx_runtime4.jsx("span", {
5492
+ className: "desktop-pill desktop-pill-mark",
5493
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("span", {
5494
+ className: "desktop-pill-label",
5495
+ children: [
5496
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
5497
+ className: "desktop-pill-icon",
5498
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Eyes, {
5499
+ size: 18
5500
+ })
5501
+ }),
5502
+ title && /* @__PURE__ */ jsx_runtime4.jsx(PillText, {
5503
+ text: title
5504
+ })
5505
+ ]
5506
+ })
5507
+ })
5508
+ })
5509
+ });
5510
+ }
5511
+ function Eyes({ size = 16, large = true }) {
5397
5512
  return /* @__PURE__ */ jsx_runtime4.jsxs("svg", {
5398
- className: `desktop-eyes${large ? "" : " is-alone"}${settle ? " is-settling" : ""}`,
5513
+ className: `desktop-eyes${large ? "" : " is-alone"}`,
5399
5514
  viewBox: "0 0 100 100",
5400
5515
  width: size,
5401
5516
  height: size,
@@ -5515,21 +5630,24 @@ function WindowPill({ label, active, hand, icon, shortcut, enter, onSelect, onCl
5515
5630
  type: "button",
5516
5631
  className: "desktop-pill-close",
5517
5632
  "aria-label": `Close ${label}`,
5518
- title: `Close ${label}${shortcut ? " (⌘/Ctrl W)" : ""}`,
5633
+ title: `Close ${label}${shortcut ? ` (${shortcut})` : ""}`,
5519
5634
  onClick: onClose,
5520
5635
  children: /* @__PURE__ */ jsx_runtime4.jsx(Cross, {})
5521
5636
  })
5522
5637
  ]
5523
5638
  });
5524
5639
  }
5525
- function Pills({ children, label }) {
5640
+ function useScrollFade(axis) {
5526
5641
  const ref = import_react7.useRef(null);
5527
5642
  const [fade, setFade] = import_react7.useState("");
5528
5643
  import_react7.useEffect(() => {
5529
5644
  const el = ref.current;
5530
5645
  if (!el || typeof ResizeObserver === "undefined")
5531
5646
  return;
5532
- const measure = () => setFade(`${el.scrollLeft > 1 ? " is-faded-left" : ""}${el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? " is-faded-right" : ""}`);
5647
+ const measure = () => {
5648
+ const [at, size, whole, start, end] = axis === "x" ? [el.scrollLeft, el.clientWidth, el.scrollWidth, "left", "right"] : [el.scrollTop, el.clientHeight, el.scrollHeight, "top", "bottom"];
5649
+ setFade(`${at > 1 ? ` is-faded-${start}` : ""}${at + size < whole - 1 ? ` is-faded-${end}` : ""}`);
5650
+ };
5533
5651
  measure();
5534
5652
  const ro = new ResizeObserver(measure);
5535
5653
  ro.observe(el);
@@ -5541,6 +5659,10 @@ function Pills({ children, label }) {
5541
5659
  el.removeEventListener("scroll", measure);
5542
5660
  };
5543
5661
  });
5662
+ return { ref, fade };
5663
+ }
5664
+ function Pills({ children, label }) {
5665
+ const { ref, fade } = useScrollFade("x");
5544
5666
  return /* @__PURE__ */ jsx_runtime4.jsx("div", {
5545
5667
  ref,
5546
5668
  className: `desktop-pills${fade}`,
@@ -5596,14 +5718,19 @@ function rank2(texts, words2) {
5596
5718
  }
5597
5719
  return total;
5598
5720
  }
5599
- function DesktopFrame({ picture, title, hand, render: render2, root, go, launch, dismiss, onLauncher }) {
5721
+ function DesktopFrame({ picture, title, hand, render: render2, root, documentTitle = false, launcherHref, go, launch, dismiss, arriving = false, onLauncher }) {
5600
5722
  const { session: w, apps, windows } = picture;
5723
+ const keymap = keymapOf(picture.keys);
5724
+ const keyOf2 = (c) => {
5725
+ const b = keymap[c];
5726
+ return b === null ? undefined : formatBinding(b);
5727
+ };
5601
5728
  const sessions = go ? picture.sessions ?? [] : [];
5602
5729
  const frame = import_react7.useRef(null), input = import_react7.useRef(null);
5603
5730
  const resultsId = import_react7.useId();
5604
5731
  const fire = (name, args) => hand?.fire(name, { ...args, session: w.key });
5605
5732
  const open = (app, path) => fire("desktop-open", { app, ...path === undefined ? {} : { path } });
5606
- const [focused, setFocused] = import_react7.useState(false);
5733
+ const [mode, setMode] = import_react7.useState(() => launch ? "tab" : null);
5607
5734
  const [awaiting, setAwaiting] = import_react7.useState(null);
5608
5735
  import_react7.useEffect(() => {
5609
5736
  if (awaiting && w.selected !== awaiting.from)
@@ -5615,43 +5742,63 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5615
5742
  const t = setTimeout(() => setAwaiting(null), 800);
5616
5743
  return () => clearTimeout(t);
5617
5744
  }, [awaiting]);
5618
- const launcher = focused || w.selected === null || awaiting !== null;
5745
+ const shownMode = w.selected === null ? arriving && mode === null ? null : "tab" : mode ?? awaiting?.mode ?? null;
5746
+ const launcher = shownMode !== null, quick = shownMode === "quick";
5747
+ const before = import_react7.useRef(null);
5748
+ const openQuick = () => {
5749
+ before.current = document.activeElement instanceof HTMLElement && document.activeElement !== document.body ? document.activeElement : null;
5750
+ setMode("quick");
5751
+ };
5752
+ const leave = () => {
5753
+ const back = quick ? before.current : null;
5754
+ setMode(null);
5755
+ input.current?.blur();
5756
+ before.current = null;
5757
+ if (back?.isConnected)
5758
+ back.focus({ preventScroll: true });
5759
+ };
5619
5760
  import_react7.useEffect(() => {
5620
5761
  if (launch)
5621
- setFocused(true);
5762
+ setMode("tab");
5622
5763
  }, [launch]);
5623
5764
  import_react7.useEffect(() => {
5624
5765
  if (dismiss) {
5625
- setFocused(false);
5766
+ setMode(null);
5626
5767
  input.current?.blur();
5627
5768
  }
5628
5769
  }, [dismiss]);
5770
+ import_react7.useEffect(() => {
5771
+ if (w.selected === null)
5772
+ setMode((m) => m === "quick" ? null : m);
5773
+ }, [w.selected]);
5629
5774
  const told = import_react7.useRef(onLauncher);
5630
5775
  told.current = onLauncher;
5631
- const shown = import_react7.useRef(launcher);
5776
+ const shown = import_react7.useRef(shownMode);
5632
5777
  import_react7.useEffect(() => {
5633
- if (shown.current !== launcher) {
5634
- shown.current = launcher;
5635
- told.current?.(launcher);
5778
+ if (shown.current !== shownMode) {
5779
+ shown.current = shownMode;
5780
+ told.current?.(shownMode);
5636
5781
  }
5637
- }, [launcher]);
5638
- const [settles, setSettles] = import_react7.useState(0);
5639
- const [was, setWas] = import_react7.useState(launcher);
5640
- if (was !== launcher) {
5641
- setWas(launcher);
5642
- if (!launcher)
5643
- setSettles(settles + 1);
5644
- }
5782
+ }, [shownMode]);
5645
5783
  const firstDrawn = import_react7.useRef(false);
5646
5784
  import_react7.useEffect(() => {
5647
5785
  firstDrawn.current = true;
5648
5786
  }, []);
5649
- const toggle = () => {
5650
- if (focused) {
5651
- setFocused(false);
5652
- input.current?.blur();
5653
- } else
5654
- setFocused(true);
5787
+ const chooseMark = (e) => {
5788
+ if (launcherHref && (e.metaKey || e.ctrlKey || e.button === 1)) {
5789
+ window.open(launcherHref, "_blank");
5790
+ return;
5791
+ }
5792
+ setMode("tab");
5793
+ input.current?.focus();
5794
+ };
5795
+ const selectWindow = (o) => {
5796
+ if (o !== w.selected) {
5797
+ fire("desktop-select", { window: o });
5798
+ if (launcher)
5799
+ setAwaiting({ from: w.selected, mode: shownMode });
5800
+ }
5801
+ leave();
5655
5802
  };
5656
5803
  const selectedWindow = windows.find((o) => o.entity === w.selected);
5657
5804
  const selectedApp = apps.find((s2) => s2.entity === selectedWindow?.app);
@@ -5666,17 +5813,18 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5666
5813
  search("");
5667
5814
  }, [launcher]);
5668
5815
  const label = (o, s2) => o.title || (o.path === "/" ? s2.title : o.path);
5816
+ const tabTitle = launcher && !quick || !selectedWindow || !selectedApp ? null : label(selectedWindow, selectedApp);
5817
+ import_react7.useEffect(() => {
5818
+ if (documentTitle)
5819
+ document.title = tabTitle ? `${tabTitle} · ${title}` : title;
5820
+ }, [documentTitle, tabTitle, title]);
5669
5821
  const words2 = text.toLowerCase().trim().split(/\s+/).filter(Boolean);
5670
5822
  const ranked = (items, texts) => items.map((item2, order) => ({ item: item2, order, rank: rank2(texts(item2), words2) })).filter((r) => r.rank !== null).sort((a, b) => a.rank - b.rank || a.order - b.order).map((r) => r.item);
5671
- const openRows = ranked(windows.flatMap((o) => {
5672
- const s2 = apps.find((s3) => s3.entity === o.app);
5673
- return s2 ? [{ o, s: s2 }] : [];
5674
- }), ({ o, s: s2 }) => [label(o, s2), s2.title, s2.group]);
5675
5823
  const found = ranked(apps, (s2) => [s2.title, s2.key, s2.group]);
5676
5824
  const sessionRows = ranked(sessions, (x) => [x.key, x.front ?? ""]);
5677
5825
  const newSession = go && rank2(["new session"], words2) !== null;
5678
5826
  const leaving = root && rank2([`leave ${title}`, root.label], words2) !== null;
5679
- const S0 = openRows.length + found.length;
5827
+ const S0 = found.length;
5680
5828
  const L = S0 + sessionRows.length + (newSession ? 1 : 0);
5681
5829
  const count = L + (leaving ? 1 : 0);
5682
5830
  const cursor = Math.min(at, Math.max(0, count - 1));
@@ -5688,86 +5836,53 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5688
5836
  if (i >= S0) {
5689
5837
  const x = sessionRows[i - S0];
5690
5838
  go?.(x ? x.key : null);
5691
- setFocused(false);
5839
+ setMode(null);
5692
5840
  input.current?.blur();
5693
5841
  return;
5694
5842
  }
5695
- const row = openRows[i], s2 = row ? undefined : found[i - openRows.length];
5696
- const target = row ? row.o.entity : s2 && !s2.many ? windows.filter((o) => o.app === s2.entity).at(-1)?.entity : undefined;
5697
- if (row)
5698
- fire("desktop-select", { window: row.o.entity });
5699
- else if (s2)
5843
+ const s2 = found[i];
5844
+ const target = s2 && !s2.many ? windows.filter((o) => o.app === s2.entity).at(-1)?.entity : undefined;
5845
+ if (s2)
5700
5846
  fire("desktop-open", { app: s2.key, fresh: true });
5701
- if ((row || s2) && target !== w.selected)
5702
- setAwaiting({ from: w.selected });
5703
- setFocused(false);
5847
+ if (s2 && target !== w.selected)
5848
+ setAwaiting({ from: w.selected, mode: shownMode ?? "tab" });
5849
+ setMode(null);
5704
5850
  input.current?.blur();
5705
5851
  };
5706
5852
  import_react7.useEffect(() => {
5707
- if (focused && hand)
5853
+ if (mode && hand)
5708
5854
  input.current?.focus();
5709
- }, [focused, !!hand]);
5855
+ }, [mode, !!hand]);
5710
5856
  import_react7.useEffect(() => {
5711
5857
  frame.current?.querySelector(`[data-result="${cursor}"]`)?.scrollIntoView({ block: "nearest" });
5712
5858
  }, [cursor]);
5713
- const dot = import_react7.useRef(null), string = import_react7.useRef(null), overlay = import_react7.useRef(null);
5714
- const tie = import_react7.useRef({ x: 0, y: 0, r: 0 });
5715
- const bob = import_react7.useRef(null);
5716
- bob.current ??= new Bob((at2) => {
5717
- const el = dot.current, line = string.current, t = tie.current;
5718
- if (!el || !line)
5719
- return;
5720
- el.style.transform = `translate(${at2.x}px, ${at2.y}px)`;
5721
- const dx = at2.x - t.x, dy = at2.y - t.y, reach = Math.hypot(dx, dy), length = Math.max(0, reach - t.r);
5722
- line.style.opacity = length > 1 ? "1" : "0";
5723
- line.style.transform = `translate(${t.x}px, ${t.y}px) rotate(${reach ? -Math.asin(dx / reach) : 0}rad) scaleY(${length})`;
5724
- }, () => typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches);
5725
- import_react7.useEffect(() => () => bob.current?.stop(), []);
5726
- const place = (now = false) => {
5727
- const o = overlay.current, el = dot.current;
5728
- const row = o?.querySelector(`[data-result="${cursor}"]`)?.closest(".desktop-row");
5729
- if (!el)
5730
- return;
5731
- el.style.opacity = row ? "1" : "0";
5732
- if (!row) {
5733
- if (string.current)
5734
- string.current.style.opacity = "0";
5735
- return;
5736
- }
5737
- const body = row.closest(".desktop-launcher-body");
5738
- bob.current.to({ x: tie.current.x, y: row.offsetTop - (body?.scrollTop ?? 0) + row.offsetHeight / 2 }, now);
5739
- };
5740
- import_react7.useLayoutEffect(() => {
5741
- if (!launcher) {
5742
- bob.current.stop();
5743
- return;
5744
- }
5745
- const o = overlay.current, mark = o?.querySelector(".desktop-logo"), eye = o?.querySelector(".desktop-eye-large"), el = dot.current;
5746
- if (!o || !mark || !eye || !el)
5747
- return;
5748
- const O = o.getBoundingClientRect(), M = mark.getBoundingClientRect(), E = eye.getBoundingClientRect();
5749
- tie.current = { x: M.left + M.width / 2 - O.left, y: M.bottom - O.top, r: E.width / 2 };
5750
- el.style.width = el.style.height = `${E.width}px`;
5751
- el.style.margin = `${-E.width / 2}px 0 0 ${-E.width / 2}px`;
5752
- bob.current.drop({ x: E.left + E.width / 2 - O.left, y: E.top + E.height / 2 - O.top });
5753
- }, [launcher]);
5754
- import_react7.useEffect(() => {
5755
- if (launcher)
5756
- place();
5757
- });
5859
+ const { ref: body, fade: bodyFade } = useScrollFade("y");
5758
5860
  import_react7.useEffect(() => {
5759
5861
  if (!hand)
5760
5862
  return;
5761
5863
  const key = (e) => {
5762
5864
  if (document.activeElement !== document.body && !frame.current?.contains(document.activeElement))
5763
5865
  return;
5764
- if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k" && !e.altKey) {
5866
+ const command = e.defaultPrevented ? null : commandOf(keymap, e);
5867
+ if (command === "launcher") {
5765
5868
  e.preventDefault();
5766
- if (!focused)
5767
- setFocused(true);
5768
- else if (w.selected !== null)
5769
- toggle();
5770
- } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "w" && !e.altKey && !launcher && w.selected !== null) {
5869
+ if (!launcher)
5870
+ openQuick();
5871
+ else if (quick)
5872
+ leave();
5873
+ else
5874
+ input.current?.focus();
5875
+ } else if ((command === "previousTab" || command === "nextTab") && windows.length) {
5876
+ e.preventDefault();
5877
+ const tabs = [null, ...windows.map((o) => o.entity)];
5878
+ const here = launcher && !quick ? 0 : tabs.indexOf(w.selected);
5879
+ const next = tabs[(here + (command === "nextTab" ? 1 : -1) + tabs.length) % tabs.length];
5880
+ if (next === null) {
5881
+ setMode("tab");
5882
+ input.current?.focus();
5883
+ } else
5884
+ selectWindow(next);
5885
+ } else if (command === "closeTab" && !launcher && w.selected !== null) {
5771
5886
  e.preventDefault();
5772
5887
  fire("desktop-close", {});
5773
5888
  } else if (launcher && e.key === "Escape") {
@@ -5775,7 +5890,7 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5775
5890
  if (text)
5776
5891
  search("");
5777
5892
  else if (w.selected !== null)
5778
- toggle();
5893
+ leave();
5779
5894
  }
5780
5895
  };
5781
5896
  addEventListener("keydown", key);
@@ -5787,17 +5902,17 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5787
5902
  const s2 = apps.find((s3) => s3.entity === o.app);
5788
5903
  if (!s2)
5789
5904
  return null;
5790
- const active = w.selected === o.entity;
5905
+ const active = (!launcher || quick) && w.selected === o.entity;
5791
5906
  return /* @__PURE__ */ jsx_runtime4.jsx(WindowPill, {
5792
5907
  label: label(o, s2),
5793
5908
  active,
5794
5909
  hand,
5795
- shortcut: active,
5910
+ shortcut: active ? keyOf2("closeTab") : undefined,
5796
5911
  icon: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
5797
5912
  drawing: s2.drawing
5798
5913
  }),
5799
5914
  enter: firstDrawn.current,
5800
- onSelect: () => fire("desktop-select", { window: o.entity }),
5915
+ onSelect: () => selectWindow(o.entity),
5801
5916
  onClose: () => fire("desktop-close", { window: o.entity })
5802
5917
  }, o.entity);
5803
5918
  });
@@ -5810,44 +5925,47 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5810
5925
  className: "desktop-bar",
5811
5926
  "aria-label": "Bar",
5812
5927
  children: [
5813
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
5814
- type: "button",
5815
- className: "desktop-logo",
5816
- title: `${title}: launcher (⌘/Ctrl K)`,
5817
- "aria-label": "Open launcher",
5818
- "aria-expanded": launcher,
5819
- disabled: !hand,
5820
- onClick: toggle,
5821
- children: /* @__PURE__ */ jsx_runtime4.jsx(Eyes, {
5822
- size: 18,
5823
- settle: settles > 0
5824
- }, settles)
5825
- }),
5826
- windows.length === 0 && /* @__PURE__ */ jsx_runtime4.jsxs("span", {
5827
- className: "desktop-bar-title",
5928
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
5929
+ className: `desktop-pill desktop-pill-mark${launcher && !quick ? " is-active" : ""}`,
5828
5930
  children: [
5829
- root && /* @__PURE__ */ jsx_runtime4.jsxs(jsx_runtime4.Fragment, {
5931
+ /* @__PURE__ */ jsx_runtime4.jsxs("button", {
5932
+ type: "button",
5933
+ className: "desktop-pill-label",
5934
+ title: `${title}: launcher`,
5935
+ "aria-label": "Launcher",
5936
+ "aria-pressed": launcher && !quick,
5937
+ disabled: !hand,
5938
+ onClick: chooseMark,
5939
+ onMouseDown: (e) => {
5940
+ if (e.button === 1)
5941
+ e.preventDefault();
5942
+ },
5943
+ onAuxClick: (e) => {
5944
+ if (e.button === 1)
5945
+ chooseMark(e);
5946
+ },
5830
5947
  children: [
5831
- /* @__PURE__ */ jsx_runtime4.jsx("a", {
5832
- className: "desktop-bar-root",
5833
- href: root.href,
5834
- title: `Leave ${title}: back to ${root.label}`,
5835
- children: root.label
5836
- }),
5837
5948
  /* @__PURE__ */ jsx_runtime4.jsx("span", {
5838
- className: "desktop-muted",
5839
- children: " / "
5949
+ className: "desktop-pill-icon",
5950
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Eyes, {
5951
+ size: 18
5952
+ })
5953
+ }),
5954
+ /* @__PURE__ */ jsx_runtime4.jsx(PillText, {
5955
+ text: title
5840
5956
  })
5841
5957
  ]
5842
5958
  }),
5843
- title,
5844
- go || w.key !== "main" ? /* @__PURE__ */ jsx_runtime4.jsxs("span", {
5845
- className: "desktop-muted",
5846
- children: [
5847
- " · ",
5848
- w.key
5849
- ]
5850
- }) : ""
5959
+ hand && root && /* @__PURE__ */ jsx_runtime4.jsx("button", {
5960
+ type: "button",
5961
+ className: "desktop-pill-close",
5962
+ "aria-label": `Leave ${title}`,
5963
+ title: `Leave ${title}: back to ${root.label}`,
5964
+ onClick: () => location.assign(root.href),
5965
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
5966
+ drawing: leaveIcon
5967
+ })
5968
+ })
5851
5969
  ]
5852
5970
  }),
5853
5971
  /* @__PURE__ */ jsx_runtime4.jsx(Pills, {
@@ -5864,7 +5982,7 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5864
5982
  return null;
5865
5983
  const active = w.selected === o.entity;
5866
5984
  return /* @__PURE__ */ jsx_runtime4.jsx("section", {
5867
- className: "desktop-app",
5985
+ className: `desktop-app${active && launcher && !quick ? " is-behind" : ""}`,
5868
5986
  hidden: !active,
5869
5987
  "aria-label": label(o, s2),
5870
5988
  children: /* @__PURE__ */ jsx_runtime4.jsx(AppBoundary, {
@@ -5892,359 +6010,285 @@ function DesktopFrame({ picture, title, hand, render: render2, root, go, launch,
5892
6010
  }, o.entity);
5893
6011
  })
5894
6012
  }),
5895
- launcher && /* @__PURE__ */ jsx_runtime4.jsxs("div", {
5896
- ref: overlay,
5897
- className: "desktop-launcher",
6013
+ launcher && /* @__PURE__ */ jsx_runtime4.jsx("div", {
6014
+ className: `desktop-launcher${quick ? " is-quick" : ""}`,
5898
6015
  "aria-label": "Launcher",
5899
6016
  onPointerDown: (e) => {
5900
6017
  if (!e.target.closest("input"))
5901
6018
  e.preventDefault();
6019
+ if (quick && !e.target.closest(".desktop-palette"))
6020
+ leave();
5902
6021
  },
5903
- children: [
5904
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
5905
- className: "desktop-bar desktop-launcher-search",
5906
- children: [
5907
- /* @__PURE__ */ jsx_runtime4.jsx("button", {
5908
- type: "button",
5909
- className: "desktop-logo is-selected",
5910
- title: "Close launcher (Esc)",
5911
- "aria-label": "Close launcher",
5912
- "aria-expanded": "true",
5913
- disabled: !hand,
5914
- onClick: toggle,
5915
- children: /* @__PURE__ */ jsx_runtime4.jsx(Eyes, {
5916
- size: 18,
5917
- large: false
5918
- })
5919
- }),
5920
- /* @__PURE__ */ jsx_runtime4.jsx("input", {
5921
- ref: input,
5922
- className: "desktop-search",
5923
- autoFocus: focused,
5924
- "aria-label": "Search",
5925
- placeholder: "Type an app",
5926
- value: text,
5927
- disabled: !hand,
5928
- onFocus: () => setFocused(true),
5929
- onBlur: () => setFocused(false),
5930
- onChange: (e) => search(e.target.value),
5931
- role: "combobox",
5932
- "aria-expanded": "true",
5933
- "aria-controls": resultsId,
5934
- "aria-activedescendant": count ? `${resultsId}-${cursor}` : undefined,
5935
- onKeyDown: (e) => {
5936
- if (["ArrowDown", "ArrowUp"].includes(e.key) && count) {
5937
- e.preventDefault();
5938
- setAt((cursor + (e.key === "ArrowDown" ? 1 : -1) + count) % count);
5939
- } else if (e.key === "Enter" && count) {
5940
- e.preventDefault();
5941
- choose(cursor);
6022
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6023
+ className: "desktop-palette",
6024
+ children: [
6025
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6026
+ className: "desktop-launcher-search",
6027
+ children: [
6028
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6029
+ className: "desktop-pill-icon",
6030
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6031
+ drawing: searchIcon
6032
+ })
6033
+ }),
6034
+ /* @__PURE__ */ jsx_runtime4.jsx("input", {
6035
+ ref: input,
6036
+ className: "desktop-search",
6037
+ autoFocus: mode !== null,
6038
+ "aria-label": "Search",
6039
+ placeholder: "Type an app",
6040
+ value: text,
6041
+ disabled: !hand,
6042
+ onChange: (e) => search(e.target.value),
6043
+ role: "combobox",
6044
+ "aria-expanded": "true",
6045
+ "aria-controls": resultsId,
6046
+ "aria-activedescendant": count ? `${resultsId}-${cursor}` : undefined,
6047
+ onKeyDown: (e) => {
6048
+ if (["ArrowDown", "ArrowUp"].includes(e.key) && count) {
6049
+ e.preventDefault();
6050
+ setAt((cursor + (e.key === "ArrowDown" ? 1 : -1) + count) % count);
6051
+ } else if (e.key === "Enter" && count) {
6052
+ e.preventDefault();
6053
+ choose(cursor);
6054
+ }
5942
6055
  }
5943
- }
5944
- })
5945
- ]
5946
- }),
5947
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
5948
- ref: string,
5949
- className: "desktop-string",
5950
- "aria-hidden": "true"
5951
- }),
5952
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
5953
- ref: dot,
5954
- className: "desktop-dot",
5955
- "aria-hidden": "true"
5956
- }),
5957
- /* @__PURE__ */ jsx_runtime4.jsxs("div", {
5958
- id: resultsId,
5959
- className: "desktop-launcher-body",
5960
- role: "listbox",
5961
- "aria-label": "Open windows and apps",
5962
- onScroll: () => place(true),
5963
- children: [
5964
- openRows.length > 0 && /* @__PURE__ */ jsx_runtime4.jsxs("div", {
5965
- className: "desktop-group",
5966
- children: [
5967
- /* @__PURE__ */ jsx_runtime4.jsx("h2", {
5968
- className: "desktop-group-title",
5969
- style: { "--i": 0 },
5970
- children: "Open"
5971
- }),
5972
- openRows.map(({ o, s: s2 }, k) => {
5973
- const i = k, front = w.selected === o.entity;
5974
- return /* @__PURE__ */ jsx_runtime4.jsxs("span", {
5975
- className: `desktop-row desktop-row-window${cursor === i ? " is-cursor" : ""}${front ? " is-active" : ""}`,
5976
- style: { "--i": i + 1 },
5977
- ...middleCloses(hand && (() => fire("desktop-close", { window: o.entity }))),
5978
- children: [
5979
- /* @__PURE__ */ jsx_runtime4.jsxs("button", {
5980
- type: "button",
5981
- id: `${resultsId}-${i}`,
5982
- "data-result": i,
5983
- role: "option",
5984
- "aria-selected": cursor === i,
5985
- className: "desktop-row-main",
5986
- disabled: !hand,
5987
- title: label(o, s2),
5988
- onMouseEnter: () => setAt(i),
5989
- onClick: () => choose(i),
5990
- children: [
5991
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
5992
- className: "desktop-pill-icon",
5993
- children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
5994
- drawing: s2.drawing
5995
- })
5996
- }),
5997
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
5998
- className: "desktop-row-label",
5999
- children: label(o, s2)
6000
- }),
6001
- label(o, s2) !== s2.title && /* @__PURE__ */ jsx_runtime4.jsx("span", {
6002
- className: "desktop-row-note",
6003
- children: s2.title
6056
+ })
6057
+ ]
6058
+ }),
6059
+ /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6060
+ ref: body,
6061
+ id: resultsId,
6062
+ className: `desktop-launcher-body${bodyFade}`,
6063
+ role: "listbox",
6064
+ "aria-label": "Apps",
6065
+ children: [
6066
+ groups.map((group) => /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6067
+ className: "desktop-group",
6068
+ children: [
6069
+ group && /* @__PURE__ */ jsx_runtime4.jsx("h2", {
6070
+ className: "desktop-group-title",
6071
+ children: group
6072
+ }),
6073
+ appsOf(group).map((s2) => {
6074
+ const i = found.indexOf(s2);
6075
+ const open2 = windows.filter((o) => o.app === s2.entity).length;
6076
+ return /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6077
+ type: "button",
6078
+ id: `${resultsId}-${i}`,
6079
+ "data-result": i,
6080
+ role: "option",
6081
+ "aria-selected": cursor === i,
6082
+ className: `desktop-row desktop-row-app${cursor === i ? " is-cursor" : ""}`,
6083
+ disabled: !hand,
6084
+ title: s2.many ? `Open another ${s2.title}` : open2 ? `Bring ${s2.title} to the front` : `Open ${s2.title}`,
6085
+ onMouseEnter: () => setAt(i),
6086
+ onClick: () => choose(i),
6087
+ children: [
6088
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6089
+ className: "desktop-pill-icon",
6090
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6091
+ drawing: s2.drawing
6004
6092
  })
6005
- ]
6006
- }),
6007
- hand && /* @__PURE__ */ jsx_runtime4.jsx("button", {
6008
- type: "button",
6009
- className: "desktop-pill-close",
6010
- "aria-label": `Close ${label(o, s2)}`,
6011
- title: `Close ${label(o, s2)}`,
6012
- onClick: () => fire("desktop-close", { window: o.entity }),
6013
- children: /* @__PURE__ */ jsx_runtime4.jsx(Cross, {})
6014
- })
6015
- ]
6016
- }, o.entity);
6017
- })
6018
- ]
6019
- }),
6020
- groups.map((group) => /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6021
- className: "desktop-group",
6022
- children: [
6023
- group && /* @__PURE__ */ jsx_runtime4.jsx("h2", {
6024
- className: "desktop-group-title",
6025
- style: { "--i": openRows.length + found.indexOf(appsOf(group)[0]) + (openRows.length ? 1 : 0) },
6026
- children: group
6027
- }),
6028
- appsOf(group).map((s2) => {
6029
- const i = openRows.length + found.indexOf(s2);
6030
- const open2 = windows.filter((o) => o.app === s2.entity).length;
6031
- return /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6032
- type: "button",
6033
- id: `${resultsId}-${i}`,
6034
- "data-result": i,
6035
- role: "option",
6036
- "aria-selected": cursor === i,
6037
- className: `desktop-row desktop-row-app${cursor === i ? " is-cursor" : ""}`,
6038
- style: { "--i": i + (openRows.length ? 2 : 1) },
6039
- disabled: !hand,
6040
- title: s2.many ? `Open another ${s2.title}` : open2 ? `Bring ${s2.title} to the front` : `Open ${s2.title}`,
6041
- onMouseEnter: () => setAt(i),
6042
- onClick: () => choose(i),
6043
- children: [
6044
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6045
- className: "desktop-pill-icon",
6046
- children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6047
- drawing: s2.drawing
6093
+ }),
6094
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6095
+ className: "desktop-row-label",
6096
+ children: s2.title
6097
+ }),
6098
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6099
+ className: "desktop-row-note",
6100
+ children: [
6101
+ words2.length && s2.group ? s2.group : "",
6102
+ open2 > 1 ? `${words2.length && s2.group ? " · " : ""}${open2} open` : ""
6103
+ ]
6048
6104
  })
6049
- }),
6050
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6051
- className: "desktop-row-label",
6052
- children: s2.title
6053
- }),
6054
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6055
- className: "desktop-row-note",
6056
- children: [
6057
- words2.length && s2.group ? s2.group : "",
6058
- open2 > 1 ? `${words2.length && s2.group ? " · " : ""}${open2} open` : ""
6059
- ]
6060
- })
6061
- ]
6062
- }, s2.key);
6063
- })
6064
- ]
6065
- }, group)),
6066
- (sessionRows.length > 0 || newSession) && /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6067
- className: "desktop-group desktop-sessions",
6068
- children: [
6069
- /* @__PURE__ */ jsx_runtime4.jsx("h2", {
6070
- className: "desktop-group-title",
6071
- style: { "--i": S0 + (openRows.length ? 1 : 0) + (found.length ? 1 : 0) },
6072
- children: "Sessions"
6073
- }),
6074
- sessionRows.map((x, k) => {
6075
- const i = S0 + k, note = `${x.windows ? `${x.windows} open` : "empty"}${x.front ? ` · ${x.front}` : ""}`;
6076
- return /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6077
- className: `desktop-row desktop-row-window desktop-row-session${cursor === i ? " is-cursor" : ""}`,
6078
- style: { "--i": i + 1 + (openRows.length ? 1 : 0) + (found.length ? 1 : 0) },
6079
- ...middleCloses(hand && (() => hand.fire("desktop-leave", { session: x.key }))),
6080
- children: [
6081
- /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6082
- type: "button",
6083
- id: `${resultsId}-${i}`,
6084
- "data-result": i,
6085
- role: "option",
6086
- "aria-selected": cursor === i,
6087
- className: "desktop-row-main",
6088
- disabled: !hand,
6089
- title: `Go to the session ${x.key}`,
6090
- onMouseEnter: () => setAt(i),
6091
- onClick: () => choose(i),
6092
- children: [
6093
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6094
- className: "desktop-pill-icon",
6095
- children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6096
- drawing: sessionIcon
6105
+ ]
6106
+ }, s2.key);
6107
+ })
6108
+ ]
6109
+ }, group)),
6110
+ (sessionRows.length > 0 || newSession) && /* @__PURE__ */ jsx_runtime4.jsxs("div", {
6111
+ className: "desktop-group desktop-sessions",
6112
+ children: [
6113
+ /* @__PURE__ */ jsx_runtime4.jsx("h2", {
6114
+ className: "desktop-group-title",
6115
+ children: "Sessions"
6116
+ }),
6117
+ sessionRows.map((x, k) => {
6118
+ const i = S0 + k, note = `${x.windows ? `${x.windows} open` : "empty"}${x.front ? ` · ${x.front}` : ""}`;
6119
+ return /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6120
+ className: `desktop-row desktop-row-window desktop-row-session${cursor === i ? " is-cursor" : ""}`,
6121
+ ...middleCloses(hand && (() => hand.fire("desktop-leave", { session: x.key }))),
6122
+ children: [
6123
+ /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6124
+ type: "button",
6125
+ id: `${resultsId}-${i}`,
6126
+ "data-result": i,
6127
+ role: "option",
6128
+ "aria-selected": cursor === i,
6129
+ className: "desktop-row-main",
6130
+ disabled: !hand,
6131
+ title: `Go to the session ${x.key}`,
6132
+ onMouseEnter: () => setAt(i),
6133
+ onClick: () => choose(i),
6134
+ children: [
6135
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6136
+ className: "desktop-pill-icon",
6137
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6138
+ drawing: sessionIcon
6139
+ })
6140
+ }),
6141
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6142
+ className: "desktop-row-label",
6143
+ children: x.key
6144
+ }),
6145
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6146
+ className: "desktop-row-note",
6147
+ children: note
6097
6148
  })
6098
- }),
6099
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6100
- className: "desktop-row-label",
6101
- children: x.key
6102
- }),
6103
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6104
- className: "desktop-row-note",
6105
- children: note
6149
+ ]
6150
+ }),
6151
+ hand && /* @__PURE__ */ jsx_runtime4.jsx("button", {
6152
+ type: "button",
6153
+ className: "desktop-pill-close",
6154
+ "aria-label": `End the session ${x.key}`,
6155
+ title: `End the session ${x.key}`,
6156
+ onClick: () => hand.fire("desktop-leave", { session: x.key }),
6157
+ children: /* @__PURE__ */ jsx_runtime4.jsx(Cross, {})
6158
+ })
6159
+ ]
6160
+ }, x.entity);
6161
+ }),
6162
+ newSession && (() => {
6163
+ const i = S0 + sessionRows.length;
6164
+ return /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6165
+ type: "button",
6166
+ id: `${resultsId}-${i}`,
6167
+ "data-result": i,
6168
+ role: "option",
6169
+ "aria-selected": cursor === i,
6170
+ className: `desktop-row desktop-row-app${cursor === i ? " is-cursor" : ""}`,
6171
+ disabled: !hand,
6172
+ title: "Open a new session in this tab",
6173
+ onMouseEnter: () => setAt(i),
6174
+ onClick: () => choose(i),
6175
+ children: [
6176
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6177
+ className: "desktop-pill-icon",
6178
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6179
+ drawing: plusIcon
6106
6180
  })
6107
- ]
6108
- }),
6109
- hand && /* @__PURE__ */ jsx_runtime4.jsx("button", {
6110
- type: "button",
6111
- className: "desktop-pill-close",
6112
- "aria-label": `End the session ${x.key}`,
6113
- title: `End the session ${x.key}`,
6114
- onClick: () => hand.fire("desktop-leave", { session: x.key }),
6115
- children: /* @__PURE__ */ jsx_runtime4.jsx(Cross, {})
6116
- })
6117
- ]
6118
- }, x.entity);
6119
- }),
6120
- newSession && (() => {
6121
- const i = S0 + sessionRows.length;
6122
- return /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6123
- type: "button",
6124
- id: `${resultsId}-${i}`,
6125
- "data-result": i,
6126
- role: "option",
6127
- "aria-selected": cursor === i,
6128
- className: `desktop-row desktop-row-app${cursor === i ? " is-cursor" : ""}`,
6129
- style: { "--i": i + 1 + (openRows.length ? 1 : 0) + (found.length ? 1 : 0) },
6130
- disabled: !hand,
6131
- title: "Open a new session in this tab",
6132
- onMouseEnter: () => setAt(i),
6133
- onClick: () => choose(i),
6134
- children: [
6135
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6136
- className: "desktop-pill-icon",
6137
- children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6138
- drawing: plusIcon
6181
+ }),
6182
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6183
+ className: "desktop-row-label",
6184
+ children: "New session"
6185
+ }),
6186
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6187
+ className: "desktop-row-note",
6188
+ children: [
6189
+ "this tab is on ",
6190
+ w.key
6191
+ ]
6139
6192
  })
6140
- }),
6141
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6142
- className: "desktop-row-label",
6143
- children: "New session"
6144
- }),
6145
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6146
- className: "desktop-row-note",
6147
- children: [
6148
- "this tab is on ",
6149
- w.key
6150
- ]
6193
+ ]
6194
+ });
6195
+ })()
6196
+ ]
6197
+ }),
6198
+ leaving && /* @__PURE__ */ jsx_runtime4.jsx("div", {
6199
+ className: "desktop-group desktop-leave",
6200
+ children: /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6201
+ type: "button",
6202
+ id: `${resultsId}-${L}`,
6203
+ "data-result": L,
6204
+ role: "option",
6205
+ "aria-selected": cursor === L,
6206
+ className: `desktop-row desktop-row-app${cursor === L ? " is-cursor" : ""}`,
6207
+ title: `Leave ${title}: back to ${root.label}`,
6208
+ onMouseEnter: () => setAt(L),
6209
+ onClick: () => choose(L),
6210
+ children: [
6211
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6212
+ className: "desktop-pill-icon",
6213
+ children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6214
+ drawing: leaveIcon
6151
6215
  })
6152
- ]
6153
- });
6154
- })()
6155
- ]
6156
- }),
6157
- leaving && /* @__PURE__ */ jsx_runtime4.jsx("div", {
6158
- className: "desktop-group desktop-leave",
6159
- children: /* @__PURE__ */ jsx_runtime4.jsxs("button", {
6160
- type: "button",
6161
- id: `${resultsId}-${L}`,
6162
- "data-result": L,
6163
- role: "option",
6164
- "aria-selected": cursor === L,
6165
- className: `desktop-row desktop-row-app${cursor === L ? " is-cursor" : ""}`,
6166
- style: { "--i": L + 2 + (openRows.length ? 1 : 0) + (found.length ? 1 : 0) },
6167
- title: `Leave ${title}: back to ${root.label}`,
6168
- onMouseEnter: () => setAt(L),
6169
- onClick: () => choose(L),
6170
- children: [
6171
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6172
- className: "desktop-pill-icon",
6173
- children: /* @__PURE__ */ jsx_runtime4.jsx(AppIcon, {
6174
- drawing: leaveIcon
6216
+ }),
6217
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6218
+ className: "desktop-row-label",
6219
+ children: [
6220
+ "Leave ",
6221
+ title
6222
+ ]
6223
+ }),
6224
+ /* @__PURE__ */ jsx_runtime4.jsx("span", {
6225
+ className: "desktop-row-note",
6226
+ children: root.label
6175
6227
  })
6228
+ ]
6229
+ })
6230
+ }),
6231
+ !count && /* @__PURE__ */ jsx_runtime4.jsx("p", {
6232
+ className: "desktop-muted",
6233
+ children: text ? "Nothing matches." : "Nothing here yet."
6234
+ })
6235
+ ]
6236
+ }),
6237
+ /* @__PURE__ */ jsx_runtime4.jsxs("footer", {
6238
+ className: "desktop-launcher-legend",
6239
+ "aria-label": "Shortcuts",
6240
+ children: [
6241
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6242
+ children: [
6243
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6244
+ children: "↑"
6176
6245
  }),
6177
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6178
- className: "desktop-row-label",
6179
- children: [
6180
- "Leave ",
6181
- title
6182
- ]
6246
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6247
+ children: "↓"
6183
6248
  }),
6184
- /* @__PURE__ */ jsx_runtime4.jsx("span", {
6185
- className: "desktop-row-note",
6186
- children: root.label
6187
- })
6249
+ " move"
6250
+ ]
6251
+ }),
6252
+ /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6253
+ children: [
6254
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6255
+ children: "↵"
6256
+ }),
6257
+ " open"
6258
+ ]
6259
+ }),
6260
+ (text || w.selected !== null) && /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6261
+ children: [
6262
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6263
+ children: "esc"
6264
+ }),
6265
+ " ",
6266
+ text ? "clear" : "back"
6267
+ ]
6268
+ }),
6269
+ quick && keyOf2("launcher") && /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6270
+ children: [
6271
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6272
+ children: keyOf2("launcher")
6273
+ }),
6274
+ " close"
6275
+ ]
6276
+ }),
6277
+ windows.length > 0 && keyOf2("previousTab") && keyOf2("nextTab") && /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6278
+ children: [
6279
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6280
+ children: keyOf2("previousTab")
6281
+ }),
6282
+ /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6283
+ children: keyOf2("nextTab")
6284
+ }),
6285
+ " tabs"
6188
6286
  ]
6189
6287
  })
6190
- }),
6191
- !count && /* @__PURE__ */ jsx_runtime4.jsx("p", {
6192
- className: "desktop-muted",
6193
- children: text ? "Nothing matches." : "Nothing here yet."
6194
- })
6195
- ]
6196
- }),
6197
- /* @__PURE__ */ jsx_runtime4.jsxs("footer", {
6198
- className: "desktop-launcher-legend",
6199
- "aria-label": "Shortcuts",
6200
- children: [
6201
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6202
- children: [
6203
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6204
- children: "↑"
6205
- }),
6206
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6207
- children: "↓"
6208
- }),
6209
- " move"
6210
- ]
6211
- }),
6212
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6213
- children: [
6214
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6215
- children: "↵"
6216
- }),
6217
- " open"
6218
- ]
6219
- }),
6220
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6221
- children: [
6222
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6223
- children: "esc"
6224
- }),
6225
- " ",
6226
- text ? "clear" : "close"
6227
- ]
6228
- }),
6229
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6230
- children: [
6231
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6232
- children: "⌘K"
6233
- }),
6234
- " launcher"
6235
- ]
6236
- }),
6237
- /* @__PURE__ */ jsx_runtime4.jsxs("span", {
6238
- children: [
6239
- /* @__PURE__ */ jsx_runtime4.jsx("kbd", {
6240
- children: "⌘W"
6241
- }),
6242
- " close window"
6243
- ]
6244
- })
6245
- ]
6246
- })
6247
- ]
6288
+ ]
6289
+ })
6290
+ ]
6291
+ })
6248
6292
  })
6249
6293
  ]
6250
6294
  });
@@ -7912,26 +7956,26 @@ ${hv.failed.join(`
7912
7956
  ]
7913
7957
  });
7914
7958
  }
7915
- function stepWords(step2) {
7916
- switch (step2.kind) {
7959
+ function stepWords(step) {
7960
+ switch (step.kind) {
7917
7961
  case "given":
7918
- return `given ${step2.label}`;
7962
+ return `given ${step.label}`;
7919
7963
  case "fire":
7920
- return `${step2.name} ${Object.keys(step2.args ?? {}).length ? JSON.stringify(step2.args) : ""}`.trim();
7964
+ return `${step.name} ${Object.keys(step.args ?? {}).length ? JSON.stringify(step.args) : ""}`.trim();
7921
7965
  case "tick":
7922
- return step2.at !== undefined ? `time passes to ${(step2.at / 1000).toFixed(1)} s` : `time passes ${step2.after ?? 200} ms`;
7966
+ return step.at !== undefined ? `time passes to ${(step.at / 1000).toFixed(1)} s` : `time passes ${step.after ?? 200} ms`;
7923
7967
  case "ticks":
7924
- return `time passes ${step2.count} × ${step2.every} ms`;
7968
+ return `time passes ${step.count} × ${step.every} ms`;
7925
7969
  case "done":
7926
- return `${step2.effect.name} answers ${JSON.stringify(step2.result)}`;
7970
+ return `${step.effect.name} answers ${JSON.stringify(step.result)}`;
7927
7971
  case "failed":
7928
- return `${step2.effect.name} fails: ${step2.error}`;
7972
+ return `${step.effect.name} fails: ${step.error}`;
7929
7973
  case "list":
7930
- return `${step2.effect.name} lists ${JSON.stringify(step2.items)}`;
7974
+ return `${step.effect.name} lists ${JSON.stringify(step.items)}`;
7931
7975
  case "watch":
7932
- return `${step2.effect.name} sends ${JSON.stringify(step2.events)}`;
7976
+ return `${step.effect.name} sends ${JSON.stringify(step.events)}`;
7933
7977
  case "check":
7934
- return `expect ${step2.label}`;
7978
+ return `expect ${step.label}`;
7935
7979
  }
7936
7980
  }
7937
7981
  function Story({ sim }) {
@@ -7960,7 +8004,7 @@ function Story({ sim }) {
7960
8004
  }),
7961
8005
  /* @__PURE__ */ jsx_runtime5.jsx("ol", {
7962
8006
  className: "bv-lab-story",
7963
- children: r.steps.map((step2, i) => {
8007
+ children: r.steps.map((step, i) => {
7964
8008
  const done = i < r.at || i === r.at && (r.result !== null || r.waitUntil === null);
7965
8009
  const here = i === r.at && !r.result;
7966
8010
  const check = r.checks.get(i), error = r.errors.get(i), ch = r.changes.get(i);
@@ -7973,7 +8017,7 @@ function Story({ sim }) {
7973
8017
  children: check ? check.ok ? "✓" : warn ? "!" : "✗" : error ? warn ? "!" : "✗" : done ? "·" : here ? "▸" : " "
7974
8018
  }),
7975
8019
  /* @__PURE__ */ jsx_runtime5.jsx("span", {
7976
- children: stepWords(step2)
8020
+ children: stepWords(step)
7977
8021
  }),
7978
8022
  check && !check.ok && (check.error ? /* @__PURE__ */ jsx_runtime5.jsxs("span", {
7979
8023
  className: "bv-film-error",
@@ -8566,6 +8610,7 @@ __export(exports_web, {
8566
8610
  CommandList: () => CommandList,
8567
8611
  Desktop: () => Desktop,
8568
8612
  DesktopFrame: () => DesktopFrame,
8613
+ DesktopSkeleton: () => DesktopSkeleton,
8569
8614
  Dialog: () => Dialog,
8570
8615
  Eyes: () => Eyes,
8571
8616
  FieldError: () => FieldError,
@@ -8634,4 +8679,4 @@ __export(exports_web, {
8634
8679
  view: () => view
8635
8680
  });
8636
8681
 
8637
- export { canonicalPath, matchRoute, route, isApps, compose, view, ScopeProvider, useScope, ApiError, configure, apiHeaders, api, useManifest, filterItems, Button, Input, Textarea, Select, Kbd, Panel, Rows, Row, Table, Dialog, CommandList, FieldError, plan, collect, describeType, tableOf, objectsOf, instancesOf, ridersOf, looseOf, ObjectMap, ObjectCards, ObjectCard, Replica, s, not, added, changed, removed, select, accept, resource, effect, asProgram, system, fixedStore, AppStoreContext, AppState, LiveStoreContext, useHand, useQuery, useColumn, useCount, useComponent, useHas, useAlive, useResource, useSettled, useRound, useProbe, useBaba, useBabaState, desktopAddress, parseDesktopAddress, routeOf, parseDesktopHash, Desktop, Eyes, AppIcon, DesktopFrame, SpecDesktop, LocalDesktop, appsFor, Simulation, Maker, placeOf, formatFailure, exports_web };
8682
+ export { canonicalPath, matchRoute, route, isApps, compose, view, ScopeProvider, useScope, ApiError, configure, apiHeaders, api, useManifest, filterItems, Button, Input, Textarea, Select, Kbd, Panel, Rows, Row, Table, Dialog, CommandList, FieldError, plan, collect, describeType, tableOf, objectsOf, instancesOf, ridersOf, looseOf, ObjectMap, ObjectCards, ObjectCard, Replica, s, not, added, changed, removed, select, accept, resource, effect, asProgram, system, fixedStore, AppStoreContext, AppState, LiveStoreContext, useHand, useQuery, useColumn, useCount, useComponent, useHas, useAlive, useResource, useSettled, useRound, useProbe, useBaba, useBabaState, keyCommands, normalBinding, isMac, bindingOf, keymapOf, formatBinding, bindingNote, desktopAddress, parseDesktopAddress, routeOf, parseDesktopHash, Desktop, DesktopSkeleton, Eyes, AppIcon, DesktopFrame, SpecDesktop, LocalDesktop, appsFor, Simulation, Maker, placeOf, formatFailure, exports_web };