opencode-navigator 0.27.1 → 0.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1913,7 +1913,7 @@ function providerQuotaCapability(id, canonical) {
1913
1913
  function quotaStatusMessage(input) {
1914
1914
  const { model, snapshot, state } = input;
1915
1915
  if (state.status === "error") return snapshot ? "Stale · refresh failed" : "Quota read failed · refresh to retry";
1916
- if (!input.showReadyMessage && snapshot?.availability === "ready" && (snapshot.windows.length > 0 || snapshot.balances?.length)) return;
1916
+ if (snapshot?.availability === "ready" && (snapshot.windows.length > 0 || snapshot.balances?.length)) return input.showUpdatedAt ? `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()}` : void 0;
1917
1917
  if (snapshot?.message) {
1918
1918
  if (snapshot.availability === "stale") return `Stale · ${snapshot.message}`;
1919
1919
  return snapshot.availability === "ready" ? `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()} · ${snapshot.message}` : snapshot.message;
@@ -1930,7 +1930,6 @@ function quotaStatusMessage(input) {
1930
1930
  if (snapshot.availability === "unsupported") return "No documented quota bucket for this model";
1931
1931
  if (snapshot.availability === "rate_limited") return "Provider rate-limited this read · try again later";
1932
1932
  if (snapshot.availability === "stale") return "Stale provider data · refresh to update";
1933
- if (snapshot.availability === "ready" && input.showReadyMessage && (snapshot.windows.length > 0 || snapshot.balances?.length)) return;
1934
1933
  return `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()}`;
1935
1934
  }
1936
1935
  // src/entities/preferences/model/codex-account-bindings.ts
@@ -2718,6 +2717,16 @@ function targetForLegacyScope(scope) {
2718
2717
  key: scope
2719
2718
  };
2720
2719
  }
2720
+ // src/shared/lib/reorder.ts
2721
+ function reorderItems(items, from, to, key) {
2722
+ const source = items.findIndex((item) => key(item) === from);
2723
+ const target = items.findIndex((item) => key(item) === to);
2724
+ const next = [...items];
2725
+ if (source === -1 || target === -1 || source === target) return next;
2726
+ const [item] = next.splice(source, 1);
2727
+ next.splice(target, 0, item);
2728
+ return next;
2729
+ }
2721
2730
  // src/entities/preferences/model/built-ins.ts
2722
2731
  function builtInPreferences(defaults) {
2723
2732
  return {
@@ -3354,6 +3363,14 @@ function createPreferencesModel(ports, defaults, store, configuredInput = {}) {
3354
3363
  behavior: { quickActionOrder: order }
3355
3364
  });
3356
3365
  },
3366
+ reorderQuickAction(from, to, scope) {
3367
+ if (scope !== targetKey(selectedTarget())) return;
3368
+ const order = reorderItems(selectedResolved().behavior.quickActionOrder, from, to, (id) => id);
3369
+ update({
3370
+ target: selectedTarget(),
3371
+ behavior: { quickActionOrder: order }
3372
+ });
3373
+ },
3357
3374
  sectionItemLimit: (section) => resolved().behavior.sectionItemLimits[section] ?? 0,
3358
3375
  selectedSectionItemLimit: (section) => selectedResolved().behavior.sectionItemLimits[section] ?? 0,
3359
3376
  setSectionItemLimit(section, value) {
@@ -3579,6 +3596,11 @@ function createPreferencesModel(ports, defaults, store, configuredInput = {}) {
3579
3596
  swap(order, index, destination);
3580
3597
  setSelectedSessionLayout({ order });
3581
3598
  },
3599
+ reorderSelectedSection(from, to, scope) {
3600
+ if (scope !== targetKey(selectedTarget())) return;
3601
+ const layout = resolveTarget(selectedTarget()).layout;
3602
+ setSelectedSessionLayout({ order: reorderItems(layout.order, from, to, (id) => id) });
3603
+ },
3582
3604
  previewLayoutPreset(preset) {
3583
3605
  return layoutPresetPreview(selectedLayoutResolved().layout, preset);
3584
3606
  },
@@ -3876,10 +3898,16 @@ function sourceIdentity(item) {
3876
3898
  version: item.source.version
3877
3899
  } : item?.source);
3878
3900
  }
3879
- function createMainPluginUpdate(context) {
3901
+ function createMainPluginUpdate(context, changed = () => {}) {
3880
3902
  let prepared;
3881
3903
  let busy = false;
3904
+ let phase = "idle";
3905
+ const report = (next) => {
3906
+ phase = next;
3907
+ changed();
3908
+ };
3882
3909
  return {
3910
+ state: () => ({ phase }),
3883
3911
  async check(_input, caller) {
3884
3912
  if (busy) throw new Error("Navigator installation is already in progress");
3885
3913
  prepared = void 0;
@@ -3921,6 +3949,7 @@ function createMainPluginUpdate(context) {
3921
3949
  async install(input, caller) {
3922
3950
  if (busy || !prepared || input.token !== prepared.token) throw new Error("Update confirmation expired. Check again.");
3923
3951
  busy = true;
3952
+ report("preparing");
3924
3953
  const expected = prepared;
3925
3954
  prepared = void 0;
3926
3955
  try {
@@ -3933,12 +3962,14 @@ function createMainPluginUpdate(context) {
3933
3962
  const response = await client.plugin.list({}, { signal });
3934
3963
  const item = response.data.find((entry) => entry.id === context.id);
3935
3964
  if (context.client !== client || sourceIdentity(item) !== expected.source || packageTarget(item) !== expected.target) throw new Error("Navigator installation changed. Check again before installing.");
3965
+ report("installing");
3936
3966
  await client.plugin.update({
3937
3967
  location: response.location,
3938
3968
  targets: [expected.target]
3939
3969
  }, { signal });
3940
3970
  } finally {
3941
3971
  busy = false;
3972
+ report("idle");
3942
3973
  }
3943
3974
  }
3944
3975
  };
@@ -4646,6 +4677,13 @@ function localCodexIpc(pluginID) {
4646
4677
  function pluginUpdateIpc(pluginID) {
4647
4678
  return _opencode_plugin_gui.Ipc.define({
4648
4679
  id: `${pluginID}.package-update`,
4680
+ state: effect.Schema.Struct({ phase: effect.Schema.Literals([
4681
+ "idle",
4682
+ "preparing",
4683
+ "downloading",
4684
+ "installing",
4685
+ "verifying"
4686
+ ]) }),
4649
4687
  methods: {
4650
4688
  check: { output: effect.Schema.Struct({
4651
4689
  status: effect.Schema.Literals([
@@ -4675,14 +4713,15 @@ function preferencesIpc(pluginID) {
4675
4713
  var main = (context) => {
4676
4714
  const store = desktopPreferencesStore();
4677
4715
  const readAccount = createMainAccountReader(context);
4678
- const codex = createNodeCodexClient({ version: "0.27.1" });
4716
+ const codex = createNodeCodexClient({ version: "0.27.3" });
4679
4717
  context.onCleanup(codex.dispose);
4680
4718
  context.provide(preferencesIpc(context.id), createMainPreferencesHandlers(store));
4681
4719
  context.provide(localAccountIpc(context.id), { read: readAccount });
4682
4720
  context.provide(localCodexIpc(context.id), createMainCodexHandlers(context, store, codex));
4683
- context.provide(pluginUpdateIpc(context.id), createMainPluginUpdate(context));
4721
+ const updateImpl = createMainPluginUpdate(context, () => updateProvider.changed());
4722
+ const updateProvider = context.provide(pluginUpdateIpc(context.id), updateImpl);
4684
4723
  context.provide(bugReportIpc(context.id), createMainBugReport(context.signal));
4685
- context.log.write("info", `Navigator 0.27.1 Desktop main ready`);
4724
+ context.log.write("info", `Navigator 0.27.3 Desktop main ready`);
4686
4725
  };
4687
4726
  // src/pages/navigator/ui/gui/context.tsx
4688
4727
  var NavigatorGuiContext = (0, solid_js.createContext)();
@@ -4692,11 +4731,11 @@ function useNavigatorGui() {
4692
4731
  return services;
4693
4732
  }
4694
4733
  // src/pages/navigator/ui/gui/bookmark.tsx
4695
- var _tmpl$$55 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=width:24px;flex-shrink:0><button type=button style=width:24px;height:24px;padding:0;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:4px;color:inherit><svg width=12 height=16 viewBox="0 0 14 18"aria-hidden=true><path d="M3 2H11V15L7 12L3 15Z"stroke=currentColor stroke-width=1.5>`);
4734
+ var _tmpl$$60 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=width:24px;flex-shrink:0><button type=button style=width:24px;height:24px;padding:0;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:4px;color:inherit><svg width=12 height=16 viewBox="0 0 14 18"aria-hidden=true><path d="M3 2H11V15L7 12L3 15Z"stroke=currentColor stroke-width=1.5>`);
4696
4735
  function SidebarBookmark(props) {
4697
4736
  const label = () => `${props.selected ? "Unbookmark" : "Bookmark"} ${props.title}`;
4698
4737
  return (() => {
4699
- var _el$ = _tmpl$$55(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.firstChild;
4738
+ var _el$ = _tmpl$$60(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.firstChild;
4700
4739
  _el$2.$$click = (event) => {
4701
4740
  event.stopPropagation();
4702
4741
  props.toggle();
@@ -4725,7 +4764,7 @@ function SidebarBookmark(props) {
4725
4764
  }
4726
4765
  (0, solid_js_web.delegateEvents)(["click"]);
4727
4766
  // src/pages/navigator/ui/gui/catalog-row.tsx
4728
- var _tmpl$$54 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="display:flex;gap:4px;align-items:center;min-width:0;padding:3px 0"><button type=button style=flex:1;min-width:0;display:flex;gap:6px;align-items:center;text-align:left;padding:4px;color:inherit;border:0;border-radius:6px><span style=min-width:0;flex:1><span style=display:block;overflow-wrap:anywhere></span><span style=display:block;opacity:0.65;font-size:12px;overflow-wrap:anywhere>`);
4767
+ var _tmpl$$59 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="display:flex;gap:4px;align-items:center;min-width:0;padding:3px 0"><button type=button style=flex:1;min-width:0;display:flex;gap:6px;align-items:center;text-align:left;padding:4px;color:inherit;border:0;border-radius:6px><span style=min-width:0;flex:1><span style=display:block;overflow-wrap:anywhere></span><span style=display:block;opacity:0.65;font-size:12px;overflow-wrap:anywhere>`);
4729
4768
  function navigate$1(event) {
4730
4769
  if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
4731
4770
  const buttons = [...event.currentTarget.querySelectorAll("button:not(:disabled)")];
@@ -4740,7 +4779,7 @@ function CatalogRow(props) {
4740
4779
  const [hovered, setHovered] = (0, solid_js.createSignal)(false);
4741
4780
  const [focused, setFocused] = (0, solid_js.createSignal)(false);
4742
4781
  return (() => {
4743
- var _el$ = _tmpl$$54(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling;
4782
+ var _el$ = _tmpl$$59(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling;
4744
4783
  _el$.$$keydown = navigate$1;
4745
4784
  _el$.$$focusout = (event) => {
4746
4785
  if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);
@@ -4842,15 +4881,19 @@ function GuiActionRow(props) {
4842
4881
  });
4843
4882
  }
4844
4883
  // src/pages/navigator/ui/gui/section.tsx
4845
- var _tmpl$$53 = /*#__PURE__*/ (0, solid_js_web.template)(`<section style=min-width:0><div style=display:flex;gap:8px;align-items:center;min-width:0><h2 style=flex:1;min-width:0;font-size:13px;font-weight:600;margin:0><button type=button style="display:flex;gap:6px;align-items:center;width:100%;padding:3px 0;color:inherit;background:transparent;border:0;text-align:left"><span aria-hidden=true></span><span style=overflow:hidden;text-overflow:ellipsis;white-space:nowrap></span><span style=opacity:0.55;font-weight:400>`);
4884
+ var _tmpl$$58 = /*#__PURE__*/ (0, solid_js_web.template)(`<section style=min-width:0><div style=display:flex;gap:8px;align-items:center;min-width:0><h2 style=flex:1;min-width:0;font-size:13px;font-weight:600;margin:0><button type=button style="display:flex;gap:6px;align-items:center;width:100%;padding:3px 0;color:inherit;background:transparent;border:0;text-align:left"><span aria-hidden=true data-navigator-disclosure style=display:inline-flex;width:16px;height:16px;flex-shrink:0;align-items:center;justify-content:center></span><span style=overflow:hidden;text-overflow:ellipsis;white-space:nowrap></span><span style=opacity:0.55;font-weight:400>`);
4846
4885
  function MonitoringSection(props) {
4847
4886
  const services = useNavigatorGui();
4848
4887
  const preferences = () => services.runtime.catalogs?.preferences;
4849
4888
  const expanded = () => preferences()?.expanded()[props.id] ?? true;
4889
+ const Icon = services.ui.Icon;
4850
4890
  return (() => {
4851
- var _el$ = _tmpl$$53(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling;
4891
+ var _el$ = _tmpl$$58(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling;
4852
4892
  _el$4.$$click = () => preferences()?.toggleSectionExpanded(props.id);
4853
- (0, solid_js_web.insert)(_el$5, () => expanded() ? "⌄" : "›");
4893
+ (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(Icon, {
4894
+ name: "chevron-down",
4895
+ size: "small"
4896
+ }));
4854
4897
  (0, solid_js_web.insert)(_el$6, () => props.title);
4855
4898
  (0, solid_js_web.insert)(_el$7, () => props.count);
4856
4899
  (0, solid_js_web.insert)(_el$2, () => props.actions, null);
@@ -4863,24 +4906,26 @@ function MonitoringSection(props) {
4863
4906
  }
4864
4907
  }), null);
4865
4908
  (0, solid_js_web.effect)((_p$) => {
4866
- var _v$ = props.title, _v$2 = expanded();
4909
+ var _v$ = props.title, _v$2 = expanded(), _v$3 = expanded() ? "rotate(0deg)" : "rotate(-90deg)";
4867
4910
  _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
4868
4911
  _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$4, "aria-expanded", _p$.t = _v$2);
4912
+ _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$5, "transform", _p$.a = _v$3);
4869
4913
  return _p$;
4870
4914
  }, {
4871
4915
  e: void 0,
4872
- t: void 0
4916
+ t: void 0,
4917
+ a: void 0
4873
4918
  });
4874
4919
  return _el$;
4875
4920
  })();
4876
4921
  }
4877
4922
  (0, solid_js_web.delegateEvents)(["click"]);
4878
4923
  // src/pages/navigator/ui/gui/section-filter.tsx
4879
- var _tmpl$$52 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="margin:8px 0">`);
4924
+ var _tmpl$$57 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="margin:8px 0">`);
4880
4925
  function SectionFilter(props) {
4881
4926
  const TextInput = useNavigatorGui().ui.TextInput;
4882
4927
  return (() => {
4883
- var _el$ = _tmpl$$52();
4928
+ var _el$ = _tmpl$$57();
4884
4929
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(TextInput, {
4885
4930
  get label() {
4886
4931
  return props.label;
@@ -4900,9 +4945,9 @@ function SectionFilter(props) {
4900
4945
  })();
4901
4946
  }
4902
4947
  // src/pages/navigator/ui/gui/actions-section.tsx
4903
- var _tmpl$$51 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching actions.`);
4904
- var _tmpl$2$34 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
4905
- var _tmpl$3$21 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style=margin:0;padding:0;list-style:none>`);
4948
+ var _tmpl$$56 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching actions.`);
4949
+ var _tmpl$2$38 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
4950
+ var _tmpl$3$24 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style=margin:0;padding:0;list-style:none>`);
4906
4951
  function ActionsSection() {
4907
4952
  const services = useNavigatorGui();
4908
4953
  const catalogs = () => services.runtime.catalogs;
@@ -4927,13 +4972,13 @@ function ActionsSection() {
4927
4972
  }
4928
4973
  }),
4929
4974
  (() => {
4930
- var _el$ = _tmpl$2$34();
4975
+ var _el$ = _tmpl$2$38();
4931
4976
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
4932
4977
  get each() {
4933
4978
  return [true, false].filter((selected) => view().visibility.visible().some((id) => catalogs().preferences.favoriteQuickActions().has(id) === selected));
4934
4979
  },
4935
4980
  children: (selected) => (() => {
4936
- var _el$3 = _tmpl$3$21();
4981
+ var _el$3 = _tmpl$3$24();
4937
4982
  (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(solid_js.For, {
4938
4983
  get each() {
4939
4984
  return view().visibility.visible().filter((id) => catalogs().preferences.favoriteQuickActions().has(id) === selected);
@@ -4948,7 +4993,7 @@ function ActionsSection() {
4948
4993
  return view().filtered().length === 0;
4949
4994
  },
4950
4995
  get children() {
4951
- return _tmpl$$51();
4996
+ return _tmpl$$56();
4952
4997
  }
4953
4998
  }), null);
4954
4999
  return _el$;
@@ -4972,52 +5017,690 @@ function ActionsSection() {
4972
5017
  }
4973
5018
  });
4974
5019
  }
4975
- // src/pages/navigator/ui/gui/quota-window.tsx
4976
- var _tmpl$$50 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="min-width:0;margin:12px 0"><div role=meter aria-valuemin=0 aria-valuemax=100 style="height:2px;background:color-mix(in srgb, currentColor 10%, transparent)"><div style=height:100%;background:currentColor;opacity:0.5></div></div><div style=display:flex;gap:8px;font-size:12px;margin-top:5px><span style=opacity:0.6;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1></span><span style=flex-shrink:0>`);
4977
- function GuiQuotaWindow(props) {
4978
- const view = () => quotaPresentation(props.window);
5020
+ // src/shared/ui/gui/tabs.tsx
5021
+ var _tmpl$$55 = /*#__PURE__*/ (0, solid_js_web.template)(`<nav style=display:flex;gap:4px;flex-wrap:wrap>`);
5022
+ var _tmpl$2$37 = /*#__PURE__*/ (0, solid_js_web.template)(`<span>`);
5023
+ var _tmpl$3$23 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=opacity:0.6;font-weight:400>`);
5024
+ function GuiTabs(props) {
5025
+ const Button = props.ui.Button;
5026
+ function navigate(event) {
5027
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
5028
+ const enabled = props.options.filter((option) => !option.disabled);
5029
+ if (enabled.length === 0) return;
5030
+ event.preventDefault();
5031
+ event.stopPropagation();
5032
+ const next = (enabled.findIndex((option) => option.id === props.selected) + (event.key === "ArrowRight" ? 1 : -1) + enabled.length) % enabled.length;
5033
+ props.onSelect(enabled[next].id);
5034
+ event.currentTarget.querySelectorAll("button:not(:disabled)")[next]?.focus();
5035
+ }
4979
5036
  return (() => {
4980
- var _el$ = _tmpl$$50(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$5 = _el$2.nextSibling.firstChild, _el$6 = _el$5.nextSibling;
4981
- (0, solid_js_web.insert)(_el$5, () => view().reset);
4982
- (0, solid_js_web.insert)(_el$6, () => view().value);
5037
+ var _el$ = _tmpl$$55();
5038
+ _el$.$$keydown = navigate;
5039
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
5040
+ get each() {
5041
+ return props.options.map((option) => option.id);
5042
+ },
5043
+ children: (id) => {
5044
+ const option = () => props.options.find((item) => item.id === id);
5045
+ return (0, solid_js_web.createComponent)(Button, {
5046
+ size: "small",
5047
+ get disabled() {
5048
+ return option().disabled;
5049
+ },
5050
+ get variant() {
5051
+ return props.selected === id ? "neutral" : "ghost-muted";
5052
+ },
5053
+ onClick: () => props.onSelect(id),
5054
+ get children() {
5055
+ return [(() => {
5056
+ var _el$2 = _tmpl$2$37();
5057
+ (0, solid_js_web.insert)(_el$2, () => option().label);
5058
+ (0, solid_js_web.effect)((_$p) => (0, solid_js_web.setStyleProperty)(_el$2, "font-weight", props.selected === id ? "600" : "400"));
5059
+ return _el$2;
5060
+ })(), (0, solid_js_web.createComponent)(solid_js.Show, {
5061
+ get when() {
5062
+ return option().count !== void 0;
5063
+ },
5064
+ get children() {
5065
+ var _el$3 = _tmpl$3$23();
5066
+ (0, solid_js_web.insert)(_el$3, () => option().count);
5067
+ return _el$3;
5068
+ }
5069
+ })];
5070
+ }
5071
+ });
5072
+ }
5073
+ }));
5074
+ (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", props.label));
5075
+ return _el$;
5076
+ })();
5077
+ }
5078
+ (0, solid_js_web.delegateEvents)(["keydown"]);
5079
+ // src/shared/lib/icon-choices/index.ts
5080
+ var ICON_CHOICE_IDS = [
5081
+ "plus",
5082
+ "check",
5083
+ "circle",
5084
+ "star",
5085
+ "pin",
5086
+ "bolt",
5087
+ "book",
5088
+ "search",
5089
+ "code",
5090
+ "terminal",
5091
+ "gear",
5092
+ "shield",
5093
+ "lock",
5094
+ "unlock",
5095
+ "eye",
5096
+ "clock",
5097
+ "refresh",
5098
+ "history",
5099
+ "copy",
5100
+ "export",
5101
+ "edit",
5102
+ "fork",
5103
+ "list",
5104
+ "play",
5105
+ "pause",
5106
+ "stop",
5107
+ "rocket",
5108
+ "bug",
5109
+ "beaker",
5110
+ "wand",
5111
+ "cloud",
5112
+ "server",
5113
+ "folder",
5114
+ "file",
5115
+ "heart",
5116
+ "lightbulb",
5117
+ "arrow-up",
5118
+ "arrow-down",
5119
+ "warning",
5120
+ "close"
5121
+ ];
5122
+ var choices = new Set(ICON_CHOICE_IDS);
5123
+ function isIconChoiceId(value) {
5124
+ return choices.has(value);
5125
+ }
5126
+ function iconChoiceLabel(id) {
5127
+ return id.replaceAll("-", " ");
5128
+ }
5129
+ // src/shared/ui/gui/icon.tsx
5130
+ var _tmpl$$54 = /*#__PURE__*/ (0, solid_js_web.template)(`<span aria-hidden=true style=display:inline-flex;width:18px;height:18px;flex-shrink:0;align-items:center;justify-content:center>`);
5131
+ var _tmpl$2$36 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg width=16 height=16 viewBox="0 0 24 26"fill=none stroke=currentColor stroke-width=1.7 stroke-linecap=round stroke-linejoin=round>`);
5132
+ var _tmpl$3$22 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><path></svg>`, false, true, false);
5133
+ var native = {
5134
+ plus: "plus",
5135
+ check: "check",
5136
+ search: "magnifying-glass",
5137
+ code: "code",
5138
+ terminal: "terminal",
5139
+ gear: "settings-gear",
5140
+ shield: "shield",
5141
+ lock: "lock",
5142
+ eye: "eye",
5143
+ refresh: "refresh",
5144
+ copy: "copy",
5145
+ export: "share",
5146
+ edit: "edit",
5147
+ fork: "fork",
5148
+ list: "bullet-list",
5149
+ stop: "stop",
5150
+ beaker: "flask",
5151
+ server: "server",
5152
+ folder: "folder",
5153
+ file: "file-document",
5154
+ "arrow-up": "arrow-up",
5155
+ warning: "warning",
5156
+ close: "close"
5157
+ };
5158
+ var paths = {
5159
+ circle: ["M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0Z"],
5160
+ star: ["m12 3 2.7 5.5 6.1.9-4.4 4.3 1 6.1-5.4-2.9-5.4 2.9 1-6.1-4.4-4.3 6.1-.9Z"],
5161
+ pin: ["m9 3 6 0-1 6 4 4H6l4-4Z", "M12 13v8"],
5162
+ bolt: ["m13 2-8 12h6l-1 8 9-13h-7Z"],
5163
+ book: ["M3 4h7l2 2 2-2h7v15h-7l-2 2-2-2H3Z", "M12 6v15"],
5164
+ unlock: [
5165
+ "M7 10V7a5 5 0 0 1 9-3",
5166
+ "M5 10h14v11H5Z",
5167
+ "M12 14v3"
5168
+ ],
5169
+ clock: ["M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0Z", "M12 7v5l3 2"],
5170
+ history: [
5171
+ "M4 8a9 9 0 1 1-1 7",
5172
+ "M4 3v5h5",
5173
+ "M12 7v5l3 2"
5174
+ ],
5175
+ play: ["m7 4 13 8-13 8Z"],
5176
+ pause: ["M8 4v16", "M16 4v16"],
5177
+ rocket: [
5178
+ "M9 16c0-8 6-13 12-13 0 6-5 12-13 12Z",
5179
+ "m9 8-5 1-2 6 6-1",
5180
+ "m16 15-1 5-6 2 1-6",
5181
+ "m5 17-3 5 5-3"
5182
+ ],
5183
+ bug: [
5184
+ "M8 10h8v6a4 4 0 0 1-8 0Z",
5185
+ "M9 10V8a3 3 0 0 1 6 0v2",
5186
+ "M5 7l3 4M19 7l-3 4M3 14h5m8 0h5M5 21l4-3m6 0 4 3M12 11v9"
5187
+ ],
5188
+ wand: ["m4 20 13-13 3 3L7 23Z", "M5 3v6M2 6h6M18 1v4M16 3h4"],
5189
+ cloud: ["M7 19a5 5 0 1 1 0-10 6 6 0 0 1 11-1 5.5 5.5 0 0 1 0 11Z"],
5190
+ heart: ["M12 21 3.5 12.5a5.5 5.5 0 0 1 8.5-7 5.5 5.5 0 0 1 8.5 7Z"],
5191
+ lightbulb: ["M9 18v-2a7 7 0 1 1 6 0v2Z", "M9 21h6M10 24h4"],
5192
+ "arrow-down": ["M12 3v18", "m5 14 7 7 7-7"]
5193
+ };
5194
+ function GuiIcon(props) {
5195
+ const Icon = props.ui.Icon;
5196
+ const id = () => isIconChoiceId(props.id) ? props.id : "bolt";
5197
+ return (() => {
5198
+ var _el$ = _tmpl$$54();
5199
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
5200
+ get when() {
5201
+ return native[id()];
5202
+ },
5203
+ get fallback() {
5204
+ return (() => {
5205
+ var _el$2 = _tmpl$2$36();
5206
+ (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
5207
+ get each() {
5208
+ return paths[id()] ?? paths.bolt;
5209
+ },
5210
+ children: (path) => (() => {
5211
+ var _el$3 = _tmpl$3$22();
5212
+ (0, solid_js_web.setAttribute)(_el$3, "d", path);
5213
+ return _el$3;
5214
+ })()
5215
+ }));
5216
+ return _el$2;
5217
+ })();
5218
+ },
5219
+ children: (name) => (0, solid_js_web.createComponent)(Icon, {
5220
+ get name() {
5221
+ return name();
5222
+ },
5223
+ size: "small"
5224
+ })
5225
+ }));
5226
+ return _el$;
5227
+ })();
5228
+ }
5229
+ // src/shared/ui/gui/multiline-field.tsx
5230
+ var _tmpl$$53 = /*#__PURE__*/ (0, solid_js_web.template)(`<label style=display:grid;gap:8px><span></span><textarea style="width:100%;box-sizing:border-box;min-height:160px;max-height:40vh;resize:vertical;color:var(--v2-text-text-base, inherit);background:var(--v2-background-bg-base, transparent);border:0;caret-color:var(--v2-text-text-base, auto);border-radius:6px;padding:8px;font:inherit">`);
5231
+ function GuiMultilineField(props) {
5232
+ const id = (0, solid_js.createUniqueId)();
5233
+ const [focused, setFocused] = (0, solid_js.createSignal)(false);
5234
+ return (() => {
5235
+ var _el$ = _tmpl$$53(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
5236
+ (0, solid_js_web.setAttribute)(_el$, "for", id);
5237
+ (0, solid_js_web.insert)(_el$2, () => props.label);
5238
+ _el$3.$$input = (event) => props.onInput(event.currentTarget.value);
5239
+ _el$3.addEventListener("blur", () => setFocused(false));
5240
+ _el$3.addEventListener("focus", () => setFocused(true));
5241
+ (0, solid_js_web.setAttribute)(_el$3, "id", id);
4983
5242
  (0, solid_js_web.effect)((_p$) => {
4984
- var _v$ = props.window.label, _v$2 = props.window.label, _v$3 = view().ratio === void 0 ? void 0 : Math.round(view().ratio * 100), _v$4 = view().value, _v$5 = `${(view().ratio ?? 0) * 100}%`;
4985
- _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
4986
- _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$2, "aria-label", _p$.t = _v$2);
4987
- _v$3 !== _p$.a && (0, solid_js_web.setAttribute)(_el$2, "aria-valuenow", _p$.a = _v$3);
4988
- _v$4 !== _p$.o && (0, solid_js_web.setAttribute)(_el$2, "aria-valuetext", _p$.o = _v$4);
4989
- _v$5 !== _p$.i && (0, solid_js_web.setStyleProperty)(_el$3, "width", _p$.i = _v$5);
5243
+ var _v$ = props.disabled, _v$2 = focused() ? "1px solid var(--v2-border-border-focus)" : "1px solid transparent", _v$3 = focused() ? "none" : "var(--v2-elevation-input)", _v$4 = props.disabled ? "0.5" : "1";
5244
+ _v$ !== _p$.e && (_el$3.disabled = _p$.e = _v$);
5245
+ _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$3, "outline", _p$.t = _v$2);
5246
+ _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$3, "box-shadow", _p$.a = _v$3);
5247
+ _v$4 !== _p$.o && (0, solid_js_web.setStyleProperty)(_el$3, "opacity", _p$.o = _v$4);
4990
5248
  return _p$;
4991
5249
  }, {
4992
5250
  e: void 0,
4993
5251
  t: void 0,
4994
5252
  a: void 0,
4995
- o: void 0,
4996
- i: void 0
5253
+ o: void 0
4997
5254
  });
5255
+ (0, solid_js_web.effect)(() => _el$3.value = props.value);
4998
5256
  return _el$;
4999
5257
  })();
5000
5258
  }
5001
- // src/pages/navigator/ui/gui/limits-section.tsx
5002
- var _tmpl$$49 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:4px;align-items:center;min-width:0>`);
5003
- var _tmpl$2$33 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
5004
- var _tmpl$3$20 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>`);
5005
- var _tmpl$4$12 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>: `);
5006
- function LimitsSection() {
5007
- const props = useNavigatorGui();
5008
- const limits = () => props.runtime.limits;
5009
- const snapshot = () => limits()?.controller.snapshot();
5010
- const state = () => limits()?.controller.state();
5011
- const message = () => limits()?.selected() ? quotaStatusMessage({
5012
- model: limits()?.selected(),
5013
- snapshot: snapshot(),
5014
- state: state() ?? { status: "idle" },
5015
- hasAdapter: Boolean(limits()?.controller.current().adapter),
5016
- showReadyMessage: true
5017
- }) : "Select a model in chat to view its provider limits.";
5018
- const canLink = () => {
5019
- const model = limits()?.selected();
5020
- return model && props.onLinkAccount && limits()?.codex && model.hostConnection?.status === "ready" && canLinkCodexAccount(model) && (!model.accountID || snapshot()?.availability === "unauthenticated" && model.accountSource === "binding");
5259
+ (0, solid_js_web.delegateEvents)(["input"]);
5260
+ // src/shared/ui/gui/group-control.tsx
5261
+ var _tmpl$$52 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=group style=display:flex;gap:8px;flex-wrap:wrap>`);
5262
+ function GuiGroupControl(props) {
5263
+ const Select = props.ui.Select;
5264
+ const TextInput = props.ui.TextInput;
5265
+ const Button = props.ui.Button;
5266
+ const [draft, setDraft] = (0, solid_js.createSignal)("");
5267
+ const [creating, setCreating] = (0, solid_js.createSignal)(false);
5268
+ (0, solid_js.createEffect)(() => setDraft(props.value ?? ""));
5269
+ return (() => {
5270
+ var _el$ = _tmpl$$52();
5271
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Select, {
5272
+ get label() {
5273
+ return `${props.label} existing group`;
5274
+ },
5275
+ get value() {
5276
+ return (0, solid_js_web.memo)(() => !!props.groups.includes(draft()))() ? draft() : "";
5277
+ },
5278
+ get options() {
5279
+ return [{
5280
+ value: "",
5281
+ label: "Ungrouped"
5282
+ }, ...props.groups.map((value) => ({
5283
+ value,
5284
+ label: value
5285
+ }))];
5286
+ },
5287
+ onChange: (value) => {
5288
+ setDraft(value);
5289
+ setCreating(false);
5290
+ },
5291
+ get disabled() {
5292
+ return props.disabled;
5293
+ }
5294
+ }), null);
5295
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
5296
+ size: "small",
5297
+ variant: "ghost",
5298
+ get disabled() {
5299
+ return props.disabled;
5300
+ },
5301
+ onClick: () => {
5302
+ setCreating(true);
5303
+ setDraft("");
5304
+ },
5305
+ children: "New group"
5306
+ }), null);
5307
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
5308
+ get when() {
5309
+ return creating();
5310
+ },
5311
+ get children() {
5312
+ return (0, solid_js_web.createComponent)(TextInput, {
5313
+ get label() {
5314
+ return `${props.label} new group`;
5315
+ },
5316
+ get value() {
5317
+ return draft();
5318
+ },
5319
+ onInput: setDraft,
5320
+ get disabled() {
5321
+ return props.disabled;
5322
+ },
5323
+ get invalid() {
5324
+ return draft().length > 64;
5325
+ }
5326
+ });
5327
+ }
5328
+ }), null);
5329
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
5330
+ get when() {
5331
+ return creating() || draft() !== (props.value ?? "");
5332
+ },
5333
+ get children() {
5334
+ return [(0, solid_js_web.createComponent)(Button, {
5335
+ size: "small",
5336
+ get disabled() {
5337
+ return props.disabled || draft().length > 64 || creating() && !draft().trim();
5338
+ },
5339
+ onClick: () => {
5340
+ props.save(draft().trim() || void 0);
5341
+ setCreating(false);
5342
+ },
5343
+ children: "Save group"
5344
+ }), (0, solid_js_web.createComponent)(Button, {
5345
+ size: "small",
5346
+ variant: "ghost",
5347
+ onClick: () => {
5348
+ setDraft(props.value ?? "");
5349
+ setCreating(false);
5350
+ },
5351
+ children: "Cancel"
5352
+ })];
5353
+ }
5354
+ }), null);
5355
+ (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", `${props.label} grouping`));
5356
+ return _el$;
5357
+ })();
5358
+ }
5359
+ // src/shared/ui/gui/settings-surface.tsx
5360
+ var _tmpl$$51 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-settings><style>\n[data-navigator-settings] \{ display: grid; gap: 32px; min-width: 0; container-type: inline-size; }\n[data-navigator-settings] [data-component="settings-row"] \{ display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 20px; }\n[data-navigator-settings] [data-slot="settings-row-copy"] \{ min-width: 0; overflow-wrap: anywhere; }\n[data-navigator-settings] :is(button, input, textarea, [role="switch"]) \{ scroll-margin-block: 96px; }\n[data-navigator-settings] [data-slot="settings-row-control"] \{ min-width: 0; max-width: 100%; width: 100%; flex-wrap: wrap; gap: 8px; }\n[data-navigator-settings] [data-slot="settings-row-control"] > * \{ min-width: 0; max-width: 100%; }\n[data-navigator-settings] [data-slot="settings-row-control"] > div:not([data-component]) \{ width: 100%; flex-wrap: wrap; }\n[data-navigator-settings] [data-component="button-v2"] \{ white-space: normal; height: auto; min-height: 24px; overflow-wrap: anywhere; }\n[data-navigator-settings] [data-component="text-input-v2"] \{ min-width: 0; max-width: 100%; }\n[data-navigator-settings] [data-sortable-item]:not(:last-of-type) > [data-component="settings-row"] \{ border-bottom: 0.5px solid var(--v2-border-border-base); }\n@container (max-width: 640px) \{\n [data-navigator-settings] [data-component="settings-row"] \{ grid-template-columns: minmax(0, 1fr); gap: 12px; }\n [data-navigator-settings] [data-slot="settings-row-control"] \{ justify-content: flex-start; }\n}\n`);
5361
+ function GuiSettingsSurface(props) {
5362
+ return (() => {
5363
+ var _el$ = _tmpl$$51();
5364
+ _el$.firstChild;
5365
+ (0, solid_js_web.insert)(_el$, () => props.children, null);
5366
+ return _el$;
5367
+ })();
5368
+ }
5369
+ // src/shared/ui/gui/sortable-focus.ts
5370
+ function createSortableFocus(props) {
5371
+ let focused;
5372
+ (0, solid_js.createEffect)(() => {
5373
+ const keys = props.keys();
5374
+ const disabled = props.disabled();
5375
+ const key = focused;
5376
+ if (!key || disabled || !keys.includes(key)) return;
5377
+ queueMicrotask(() => {
5378
+ if (focused !== key || props.disabled() || document.activeElement !== document.body) return;
5379
+ [...props.root().querySelectorAll("[data-sortable-item]")].find((node) => node.dataset.sortableItem === key)?.querySelector("button")?.focus({ preventScroll: true });
5380
+ });
5381
+ });
5382
+ return {
5383
+ enter(event) {
5384
+ const target = event.target;
5385
+ focused = target.getAttribute("aria-label")?.startsWith("Reorder ") ? target.closest("[data-sortable-item]")?.dataset.sortableItem : void 0;
5386
+ },
5387
+ leave(event) {
5388
+ if (event.relatedTarget && !props.root().contains(event.relatedTarget)) focused = void 0;
5389
+ }
5390
+ };
5391
+ }
5392
+ // src/shared/ui/gui/sortable-interaction.ts
5393
+ function createSortableInteraction(props) {
5394
+ const [source, setSource] = (0, solid_js.createSignal)();
5395
+ const [target, setTarget] = (0, solid_js.createSignal)();
5396
+ const [announcement, setAnnouncement] = (0, solid_js.createSignal)("");
5397
+ let drag;
5398
+ const signature = () => JSON.stringify(props.keys());
5399
+ function cancel() {
5400
+ const previous = drag;
5401
+ drag = void 0;
5402
+ setSource(void 0);
5403
+ setTarget(void 0);
5404
+ if (previous && previous.pointer >= 0 && props.root().hasPointerCapture(previous.pointer)) props.root().releasePointerCapture(previous.pointer);
5405
+ }
5406
+ (0, solid_js.createEffect)(() => {
5407
+ source();
5408
+ if (drag && (props.disabled() || props.scope() !== drag.scope || signature() !== drag.keys)) cancel();
5409
+ });
5410
+ function escape(event) {
5411
+ if (event.key !== "Escape") return;
5412
+ event.preventDefault();
5413
+ event.stopPropagation();
5414
+ cancel();
5415
+ }
5416
+ (0, solid_js.createEffect)(() => {
5417
+ if (!source()) return;
5418
+ globalThis.window.addEventListener("keydown", escape, { capture: true });
5419
+ (0, solid_js.onCleanup)(() => globalThis.window.removeEventListener("keydown", escape, true));
5420
+ });
5421
+ (0, solid_js.onCleanup)(cancel);
5422
+ function hover(event) {
5423
+ if (!drag || event.pointerId !== drag.pointer) return;
5424
+ const node = document.elementFromPoint(event.clientX, event.clientY)?.closest("[data-sortable-item]");
5425
+ setTarget(node && props.root().contains(node) ? node.dataset.sortableItem : void 0);
5426
+ }
5427
+ function start(event, id) {
5428
+ if (event.button !== 0 || props.disabled() || drag) return;
5429
+ event.preventDefault();
5430
+ event.stopPropagation();
5431
+ drag = {
5432
+ id,
5433
+ pointer: event.pointerId,
5434
+ scope: props.scope(),
5435
+ keys: signature()
5436
+ };
5437
+ props.root().setPointerCapture(event.pointerId);
5438
+ setSource(id);
5439
+ setTarget(id);
5440
+ }
5441
+ function end(event) {
5442
+ if (!drag || event.pointerId !== drag.pointer) return;
5443
+ hover(event);
5444
+ const current = drag;
5445
+ const destination = target();
5446
+ const valid = !props.disabled() && props.scope() === current.scope && signature() === current.keys;
5447
+ cancel();
5448
+ if (valid && destination && current.id !== destination) props.move(current.id, destination);
5449
+ }
5450
+ function key(event, id) {
5451
+ if (event.key === "Escape") {
5452
+ if (drag) {
5453
+ event.preventDefault();
5454
+ event.stopPropagation();
5455
+ cancel();
5456
+ }
5457
+ return;
5458
+ }
5459
+ if (props.disabled()) return;
5460
+ if ([" ", "Enter"].includes(event.key) && !event.repeat) {
5461
+ event.preventDefault();
5462
+ event.stopPropagation();
5463
+ if (!drag) {
5464
+ drag = {
5465
+ id,
5466
+ pointer: -1,
5467
+ scope: props.scope(),
5468
+ keys: signature()
5469
+ };
5470
+ setSource(id);
5471
+ setTarget(id);
5472
+ setAnnouncement("Picked up. Use arrow keys, Space to drop, or Escape to cancel.");
5473
+ } else if (drag.pointer === -1 && drag.id === id) {
5474
+ const destination = target();
5475
+ const valid = props.scope() === drag.scope && signature() === drag.keys;
5476
+ cancel();
5477
+ if (valid && destination && destination !== id) props.move(id, destination);
5478
+ }
5479
+ return;
5480
+ }
5481
+ if (drag?.pointer !== -1 || drag.id !== id || !["ArrowUp", "ArrowDown"].includes(event.key)) return;
5482
+ const keys = props.keys();
5483
+ const index = keys.indexOf(target() ?? id);
5484
+ const destination = keys[index + (event.key === "ArrowUp" ? -1 : 1)];
5485
+ if (!destination || index === -1) return;
5486
+ event.preventDefault();
5487
+ event.stopPropagation();
5488
+ setTarget(destination);
5489
+ setAnnouncement(`Drop at position ${keys.indexOf(destination) + 1} of ${keys.length}`);
5490
+ }
5491
+ return {
5492
+ source,
5493
+ target,
5494
+ announcement,
5495
+ start,
5496
+ hover,
5497
+ end,
5498
+ key,
5499
+ cancel
5500
+ };
5501
+ }
5502
+ // src/shared/ui/gui/sortable-list.tsx
5503
+ var _tmpl$$50 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=list><span role=status aria-live=polite style=position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)>Drag a handle, or press Space, use arrow keys, then Space to drop. Escape cancels. `);
5504
+ var _tmpl$2$35 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=listitem>`);
5505
+ var _tmpl$3$21 = /*#__PURE__*/ (0, solid_js_web.template)(`<span title="Drag to reorder, or Space, arrow keys, Space. Escape cancels."style=display:inline-flex;touch-action:none;cursor:grab>`);
5506
+ function GuiSortableList(props) {
5507
+ let root;
5508
+ const help = (0, solid_js.createUniqueId)();
5509
+ const IconButton = props.ui.IconButton;
5510
+ const keys = () => props.items.map((item) => props.itemKey(item));
5511
+ const focus = createSortableFocus({
5512
+ root: () => root,
5513
+ keys,
5514
+ disabled: () => Boolean(props.disabled)
5515
+ });
5516
+ const interaction = createSortableInteraction({
5517
+ keys,
5518
+ scope: () => props.scope,
5519
+ disabled: () => Boolean(props.disabled),
5520
+ root: () => root,
5521
+ move: (from, to) => props.move(from, to)
5522
+ });
5523
+ return (() => {
5524
+ var _el$ = _tmpl$$50(), _el$2 = _el$.firstChild;
5525
+ _el$2.firstChild;
5526
+ (0, solid_js_web.addEventListener)(_el$, "lostpointercapture", interaction.cancel);
5527
+ (0, solid_js_web.addEventListener)(_el$, "pointercancel", interaction.cancel);
5528
+ (0, solid_js_web.addEventListener)(_el$, "pointerup", interaction.end, true);
5529
+ (0, solid_js_web.addEventListener)(_el$, "pointermove", interaction.hover, true);
5530
+ (0, solid_js_web.addEventListener)(_el$, "focusout", focus.leave, true);
5531
+ (0, solid_js_web.addEventListener)(_el$, "focusin", focus.enter, true);
5532
+ (0, solid_js_web.use)((node) => {
5533
+ root = node;
5534
+ }, _el$);
5535
+ (0, solid_js_web.setAttribute)(_el$, "aria-describedby", help);
5536
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
5537
+ get each() {
5538
+ return keys();
5539
+ },
5540
+ children: (key) => {
5541
+ const item = () => props.items.find((value) => props.itemKey(value) === key);
5542
+ return (() => {
5543
+ var _el$4 = _tmpl$2$35();
5544
+ (0, solid_js_web.setAttribute)(_el$4, "data-sortable-item", key);
5545
+ (0, solid_js_web.insert)(_el$4, () => props.children(item, (() => {
5546
+ var _el$5 = _tmpl$3$21();
5547
+ _el$5.$$keydown = (event) => interaction.key(event, key);
5548
+ _el$5.$$pointerdown = (event) => interaction.start(event, key);
5549
+ (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(IconButton, {
5550
+ icon: "dot-grid",
5551
+ get label() {
5552
+ return `Reorder ${props.itemLabel(item())}`;
5553
+ },
5554
+ get disabled() {
5555
+ return props.disabled || keys().length < 2;
5556
+ }
5557
+ }));
5558
+ return _el$5;
5559
+ })()));
5560
+ (0, solid_js_web.effect)((_p$) => {
5561
+ var _v$ = keys().indexOf(key) + 1, _v$2 = keys().length, _v$3 = interaction.source() === key ? "0.65" : "1", _v$4 = interaction.target() === key && interaction.source() !== key ? "1px dashed var(--v2-border-border-focus)" : void 0;
5562
+ _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$4, "aria-posinset", _p$.e = _v$);
5563
+ _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$4, "aria-setsize", _p$.t = _v$2);
5564
+ _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$4, "opacity", _p$.a = _v$3);
5565
+ _v$4 !== _p$.o && (0, solid_js_web.setStyleProperty)(_el$4, "outline", _p$.o = _v$4);
5566
+ return _p$;
5567
+ }, {
5568
+ e: void 0,
5569
+ t: void 0,
5570
+ a: void 0,
5571
+ o: void 0
5572
+ });
5573
+ return _el$4;
5574
+ })();
5575
+ }
5576
+ }), _el$2);
5577
+ (0, solid_js_web.setAttribute)(_el$2, "id", help);
5578
+ (0, solid_js_web.insert)(_el$2, () => interaction.announcement(), null);
5579
+ (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", props.label));
5580
+ return _el$;
5581
+ })();
5582
+ }
5583
+ (0, solid_js_web.delegateEvents)([
5584
+ "focusin",
5585
+ "focusout",
5586
+ "pointermove",
5587
+ "pointerup",
5588
+ "pointerdown",
5589
+ "keydown"
5590
+ ]);
5591
+ // src/shared/ui/gui/progress.tsx
5592
+ var _tmpl$$49 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=progressbar aria-valuemin=0 aria-valuemax=100 style=display:grid;gap:6px;min-width:0><style>@keyframes navigator-install-pulse \{ from \{ transform: translateX(-100%); } to \{ transform: translateX(400%); } }\n@media (prefers-reduced-motion: reduce) \{ [data-navigator-install-pulse] \{ animation: none !important; } }</style><span style=display:flex;gap:6px;align-items:center></span><span style=display:block;height:3px;overflow:hidden;background:var(--v2-border-border-base)><span style=display:block;height:100%;background:var(--v2-text-text-base)>`);
5593
+ function GuiProgress(props) {
5594
+ const Loader = props.ui.Loader;
5595
+ return (() => {
5596
+ var _el$ = _tmpl$$49(), _el$3 = _el$.firstChild.nextSibling, _el$5 = _el$3.nextSibling.firstChild;
5597
+ (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(solid_js.Show, {
5598
+ get when() {
5599
+ return props.value === void 0;
5600
+ },
5601
+ get children() {
5602
+ return (0, solid_js_web.createComponent)(Loader, { size: "small" });
5603
+ }
5604
+ }), null);
5605
+ (0, solid_js_web.insert)(_el$3, () => props.label, null);
5606
+ (0, solid_js_web.effect)((_p$) => {
5607
+ var _v$ = props.label, _v$2 = props.value === void 0 ? void 0 : Math.floor(props.value * 100), _v$3 = props.label, _v$4 = props.value === void 0 ? "" : void 0, _v$5 = props.value === void 0 ? "25%" : `${Math.min(1, Math.max(0, props.value)) * 100}%`, _v$6 = props.value === void 0 ? "navigator-install-pulse 1.2s linear infinite" : void 0;
5608
+ _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
5609
+ _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$, "aria-valuenow", _p$.t = _v$2);
5610
+ _v$3 !== _p$.a && (0, solid_js_web.setAttribute)(_el$, "aria-valuetext", _p$.a = _v$3);
5611
+ _v$4 !== _p$.o && (0, solid_js_web.setAttribute)(_el$5, "data-navigator-install-pulse", _p$.o = _v$4);
5612
+ _v$5 !== _p$.i && (0, solid_js_web.setStyleProperty)(_el$5, "width", _p$.i = _v$5);
5613
+ _v$6 !== _p$.n && (0, solid_js_web.setStyleProperty)(_el$5, "animation", _p$.n = _v$6);
5614
+ return _p$;
5615
+ }, {
5616
+ e: void 0,
5617
+ t: void 0,
5618
+ a: void 0,
5619
+ o: void 0,
5620
+ i: void 0,
5621
+ n: void 0
5622
+ });
5623
+ return _el$;
5624
+ })();
5625
+ }
5626
+ // src/shared/ui/gui/icon-action.tsx
5627
+ function GuiIconAction(props) {
5628
+ const IconButton = props.ui.IconButton;
5629
+ const Tooltip = props.ui.Tooltip;
5630
+ return (0, solid_js_web.createComponent)(Tooltip, {
5631
+ get value() {
5632
+ return props.label;
5633
+ },
5634
+ get children() {
5635
+ return (0, solid_js_web.createComponent)(IconButton, {
5636
+ get icon() {
5637
+ return props.icon;
5638
+ },
5639
+ get label() {
5640
+ return props.label;
5641
+ },
5642
+ size: "small",
5643
+ get variant() {
5644
+ return props.variant ?? "ghost-muted";
5645
+ },
5646
+ get disabled() {
5647
+ return props.disabled;
5648
+ },
5649
+ get busy() {
5650
+ return props.busy;
5651
+ },
5652
+ get onClick() {
5653
+ return props.onClick;
5654
+ }
5655
+ });
5656
+ }
5657
+ });
5658
+ }
5659
+ // src/pages/navigator/ui/gui/quota-window.tsx
5660
+ var _tmpl$$48 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="min-width:0;margin:12px 0"><div role=meter aria-valuemin=0 aria-valuemax=100 style="height:2px;background:color-mix(in srgb, currentColor 10%, transparent)"><div style=height:100%;background:currentColor;opacity:0.5></div></div><div style=display:flex;gap:8px;font-size:12px;margin-top:5px><span style=opacity:0.6;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1></span><span style=flex-shrink:0>`);
5661
+ function GuiQuotaWindow(props) {
5662
+ const view = () => quotaPresentation(props.window);
5663
+ return (() => {
5664
+ var _el$ = _tmpl$$48(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$5 = _el$2.nextSibling.firstChild, _el$6 = _el$5.nextSibling;
5665
+ (0, solid_js_web.insert)(_el$5, () => view().reset);
5666
+ (0, solid_js_web.insert)(_el$6, () => view().value);
5667
+ (0, solid_js_web.effect)((_p$) => {
5668
+ var _v$ = props.window.label, _v$2 = props.window.label, _v$3 = view().ratio === void 0 ? void 0 : Math.round(view().ratio * 100), _v$4 = view().value, _v$5 = `${(view().ratio ?? 0) * 100}%`;
5669
+ _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
5670
+ _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$2, "aria-label", _p$.t = _v$2);
5671
+ _v$3 !== _p$.a && (0, solid_js_web.setAttribute)(_el$2, "aria-valuenow", _p$.a = _v$3);
5672
+ _v$4 !== _p$.o && (0, solid_js_web.setAttribute)(_el$2, "aria-valuetext", _p$.o = _v$4);
5673
+ _v$5 !== _p$.i && (0, solid_js_web.setStyleProperty)(_el$3, "width", _p$.i = _v$5);
5674
+ return _p$;
5675
+ }, {
5676
+ e: void 0,
5677
+ t: void 0,
5678
+ a: void 0,
5679
+ o: void 0,
5680
+ i: void 0
5681
+ });
5682
+ return _el$;
5683
+ })();
5684
+ }
5685
+ // src/pages/navigator/ui/gui/limits-section.tsx
5686
+ var _tmpl$$47 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
5687
+ var _tmpl$2$34 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>`);
5688
+ var _tmpl$3$20 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>: `);
5689
+ function LimitsSection() {
5690
+ const props = useNavigatorGui();
5691
+ const limits = () => props.runtime.limits;
5692
+ const snapshot = () => limits()?.controller.snapshot();
5693
+ const state = () => limits()?.controller.state();
5694
+ const message = () => limits()?.selected() ? quotaStatusMessage({
5695
+ model: limits()?.selected(),
5696
+ snapshot: snapshot(),
5697
+ state: state() ?? { status: "idle" },
5698
+ hasAdapter: Boolean(limits()?.controller.current().adapter),
5699
+ showUpdatedAt: true
5700
+ }) : "Select a model in chat to view its provider limits.";
5701
+ const canLink = () => {
5702
+ const model = limits()?.selected();
5703
+ return model && props.onLinkAccount && limits()?.codex && model.hostConnection?.status === "ready" && canLinkCodexAccount(model) && (!model.accountID || snapshot()?.availability === "unauthenticated" && model.accountSource === "binding");
5021
5704
  };
5022
5705
  const Button = props.ui.Button;
5023
5706
  return (0, solid_js_web.createComponent)(MonitoringSection, {
@@ -5038,40 +5721,17 @@ function LimitsSection() {
5038
5721
  children: "Banked Resets"
5039
5722
  });
5040
5723
  }
5041
- }), (0, solid_js_web.createComponent)(Button, {
5042
- size: "small",
5043
- variant: "ghost-muted",
5044
- onClick: () => void limits()?.controller.refresh(true),
5045
- children: "Refresh"
5724
+ }), (0, solid_js_web.createComponent)(GuiIconAction, {
5725
+ get ui() {
5726
+ return props.ui;
5727
+ },
5728
+ icon: "refresh",
5729
+ label: "Refresh",
5730
+ onClick: () => void limits()?.controller.refresh(true)
5046
5731
  })];
5047
5732
  },
5048
5733
  get children() {
5049
5734
  return [
5050
- (() => {
5051
- var _el$ = _tmpl$$49();
5052
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
5053
- size: "small",
5054
- variant: "ghost",
5055
- onClick: () => limits()?.chooseModel(),
5056
- get children() {
5057
- return limits()?.label() ?? "Select model";
5058
- }
5059
- }), null);
5060
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
5061
- get when() {
5062
- return limits()?.selected()?.variant;
5063
- },
5064
- children: (variant) => (0, solid_js_web.createComponent)(Button, {
5065
- size: "small",
5066
- variant: "ghost-muted",
5067
- onClick: () => limits()?.chooseVariant(),
5068
- get children() {
5069
- return variant();
5070
- }
5071
- })
5072
- }), null);
5073
- return _el$;
5074
- })(),
5075
5735
  (0, solid_js_web.createComponent)(solid_js.Show, {
5076
5736
  get when() {
5077
5737
  return canLink();
@@ -5092,9 +5752,9 @@ function LimitsSection() {
5092
5752
  return state()?.error;
5093
5753
  },
5094
5754
  get children() {
5095
- var _el$2 = _tmpl$2$33();
5096
- (0, solid_js_web.insert)(_el$2, () => state()?.error?.message);
5097
- return _el$2;
5755
+ var _el$ = _tmpl$$47();
5756
+ (0, solid_js_web.insert)(_el$, () => state()?.error?.message);
5757
+ return _el$;
5098
5758
  }
5099
5759
  }),
5100
5760
  (0, solid_js_web.createComponent)(solid_js.Show, {
@@ -5102,9 +5762,9 @@ function LimitsSection() {
5102
5762
  return message();
5103
5763
  },
5104
5764
  get children() {
5105
- var _el$3 = _tmpl$3$20();
5106
- (0, solid_js_web.insert)(_el$3, message);
5107
- return _el$3;
5765
+ var _el$2 = _tmpl$2$34();
5766
+ (0, solid_js_web.insert)(_el$2, message);
5767
+ return _el$2;
5108
5768
  }
5109
5769
  }),
5110
5770
  (0, solid_js_web.createComponent)(solid_js.Show, {
@@ -5121,13 +5781,13 @@ function LimitsSection() {
5121
5781
  return value().balances;
5122
5782
  },
5123
5783
  children: (balance) => (() => {
5124
- var _el$4 = _tmpl$4$12(), _el$5 = _el$4.firstChild;
5125
- (0, solid_js_web.insert)(_el$4, () => balance.label, _el$5);
5126
- (0, solid_js_web.insert)(_el$4, (() => {
5784
+ var _el$3 = _tmpl$3$20(), _el$4 = _el$3.firstChild;
5785
+ (0, solid_js_web.insert)(_el$3, () => balance.label, _el$4);
5786
+ (0, solid_js_web.insert)(_el$3, (() => {
5127
5787
  var _c$ = (0, solid_js_web.memo)(() => !!balance.unlimited);
5128
5788
  return () => _c$() ? "Unlimited" : `${balance.amount} ${balance.unit ?? ""}`;
5129
5789
  })(), null);
5130
- return _el$4;
5790
+ return _el$3;
5131
5791
  })()
5132
5792
  })]
5133
5793
  })
@@ -5590,23 +6250,34 @@ function createMcpModel(options, persist, preferences) {
5590
6250
  };
5591
6251
  }
5592
6252
  // src/pages/navigator/ui/gui/mcp-indicator.tsx
5593
- var _tmpl$$48 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style="display:inline-flex;width:12px;height:12px;flex-shrink:0;border-radius:50%;border:1px solid currentColor">`);
6253
+ var _tmpl$$46 = /*#__PURE__*/ (0, solid_js_web.template)(`<span aria-hidden=true style="display:inline-flex;width:12px;height:12px;flex-shrink:0;border-radius:50%;border:1px solid currentColor">`);
6254
+ var _tmpl$2$33 = /*#__PURE__*/ (0, solid_js_web.template)(`<span role=img style=display:inline-flex;width:16px;height:16px;flex-shrink:0;align-items:center;justify-content:center>`);
5594
6255
  function McpIndicator(props) {
6256
+ const Loader = useNavigatorGui().ui.Loader;
5595
6257
  return (() => {
5596
- var _el$ = _tmpl$$48();
5597
- (0, solid_js_web.effect)((_p$) => {
5598
- var _v$ = props.status, _v$2 = props.status === "pending" ? "dotted" : "solid", _v$3 = props.status === "connected" ? "currentColor" : "transparent", _v$4 = props.status === "failed" ? 1 : .65;
5599
- _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
5600
- _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$, "border-style", _p$.t = _v$2);
5601
- _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$, "background", _p$.a = _v$3);
5602
- _v$4 !== _p$.o && (0, solid_js_web.setStyleProperty)(_el$, "opacity", _p$.o = _v$4);
5603
- return _p$;
5604
- }, {
5605
- e: void 0,
5606
- t: void 0,
5607
- a: void 0,
5608
- o: void 0
5609
- });
6258
+ var _el$ = _tmpl$2$33();
6259
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
6260
+ get when() {
6261
+ return props.status !== "pending";
6262
+ },
6263
+ get fallback() {
6264
+ return (0, solid_js_web.createComponent)(Loader, { size: "small" });
6265
+ },
6266
+ get children() {
6267
+ var _el$2 = _tmpl$$46();
6268
+ (0, solid_js_web.effect)((_p$) => {
6269
+ var _v$ = props.status === "connected" ? "currentColor" : "transparent", _v$2 = props.status === "failed" ? 1 : .65;
6270
+ _v$ !== _p$.e && (0, solid_js_web.setStyleProperty)(_el$2, "background", _p$.e = _v$);
6271
+ _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$2, "opacity", _p$.t = _v$2);
6272
+ return _p$;
6273
+ }, {
6274
+ e: void 0,
6275
+ t: void 0
6276
+ });
6277
+ return _el$2;
6278
+ }
6279
+ }));
6280
+ (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", props.status));
5610
6281
  return _el$;
5611
6282
  })();
5612
6283
  }
@@ -5616,7 +6287,6 @@ function GuiMcpRow(props) {
5616
6287
  const catalogs = () => services.runtime.catalogs;
5617
6288
  const state = () => catalogs().mcp.serverState(props.item.name);
5618
6289
  const error = () => props.item.error || state().error?.message;
5619
- const Button = services.ui.Button;
5620
6290
  return (0, solid_js_web.createComponent)(CatalogRow, {
5621
6291
  get title() {
5622
6292
  return props.item.name;
@@ -5646,14 +6316,18 @@ function GuiMcpRow(props) {
5646
6316
  return error();
5647
6317
  },
5648
6318
  get children() {
5649
- return (0, solid_js_web.createComponent)(Button, {
5650
- size: "small",
5651
- variant: "ghost",
6319
+ return (0, solid_js_web.createComponent)(GuiIconAction, {
6320
+ get ui() {
6321
+ return services.ui;
6322
+ },
6323
+ icon: "info",
6324
+ get label() {
6325
+ return `Information about ${props.item.name}`;
6326
+ },
5652
6327
  onClick: () => services.onMcpInformation({
5653
6328
  ...props.item,
5654
6329
  error: error()
5655
- }),
5656
- children: "Info"
6330
+ })
5657
6331
  });
5658
6332
  }
5659
6333
  });
@@ -5661,22 +6335,44 @@ function GuiMcpRow(props) {
5661
6335
  });
5662
6336
  }
5663
6337
  // src/pages/navigator/ui/gui/mcp-group.tsx
5664
- var _tmpl$$47 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><button type=button style="display:flex;gap:6px;align-items:center;width:100%;padding:4px 0;border:0;background:transparent;color:inherit;text-align:left"><strong style=overflow:hidden;text-overflow:ellipsis;white-space:nowrap></strong><span style=opacity:0.6;font-weight:400></span></button><ul style="margin:0;padding:0 0 0 10px;list-style:none">`);
6338
+ var _tmpl$$45 = /*#__PURE__*/ (0, solid_js_web.template)(`<strong style=overflow:hidden;text-overflow:ellipsis;white-space:nowrap>`);
6339
+ var _tmpl$2$32 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=opacity:0.6;font-weight:400>`);
6340
+ var _tmpl$3$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><ul style="margin:0;padding:0 0 0 10px;list-style:none">`);
5665
6341
  function GuiMcpGroup(props) {
5666
6342
  const services = useNavigatorGui();
5667
6343
  const catalogs = () => services.runtime.catalogs;
5668
6344
  const view = () => catalogs().views.mcp;
5669
6345
  const bucket = () => view().bucket(props.group);
5670
6346
  const busy = () => catalogs().mcp.bulkState().group === props.group && catalogs().mcp.bulkState().status === "running";
6347
+ const Button = services.ui.Button;
5671
6348
  return (() => {
5672
- var _el$ = _tmpl$$47(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$2.nextSibling;
5673
- _el$2.$$click = () => void catalogs().mcp.toggleGroup(props.group, bucket().map((item) => item.name))?.catch(() => {});
5674
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(McpIndicator, { get status() {
5675
- return mcpGroupStatus(bucket(), busy());
5676
- } }), _el$3);
5677
- (0, solid_js_web.insert)(_el$3, () => props.group);
5678
- (0, solid_js_web.insert)(_el$4, () => bucket().length);
5679
- (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(solid_js.For, {
6349
+ var _el$ = _tmpl$3$19(), _el$4 = _el$.firstChild;
6350
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
6351
+ variant: "ghost",
6352
+ size: "small",
6353
+ get disabled() {
6354
+ return catalogs().mcp.mutating();
6355
+ },
6356
+ onClick: () => void catalogs().mcp.toggleGroup(props.group, bucket().map((item) => item.name))?.catch(() => {}),
6357
+ get children() {
6358
+ return [
6359
+ (0, solid_js_web.createComponent)(McpIndicator, { get status() {
6360
+ return mcpGroupStatus(bucket(), busy());
6361
+ } }),
6362
+ (() => {
6363
+ var _el$2 = _tmpl$$45();
6364
+ (0, solid_js_web.insert)(_el$2, () => props.group);
6365
+ return _el$2;
6366
+ })(),
6367
+ (() => {
6368
+ var _el$3 = _tmpl$2$32();
6369
+ (0, solid_js_web.insert)(_el$3, () => bucket().length);
6370
+ return _el$3;
6371
+ })()
6372
+ ];
6373
+ }
6374
+ }), _el$4);
6375
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(solid_js.For, {
5680
6376
  get each() {
5681
6377
  return view().visibility.visible().filter((item) => item.bucket === props.group).map((item) => item.name);
5682
6378
  },
@@ -5689,16 +6385,14 @@ function GuiMcpGroup(props) {
5689
6385
  } })
5690
6386
  })
5691
6387
  }));
5692
- (0, solid_js_web.effect)(() => _el$2.disabled = catalogs().mcp.mutating());
5693
6388
  return _el$;
5694
6389
  })();
5695
6390
  }
5696
- (0, solid_js_web.delegateEvents)(["click"]);
5697
6391
  // src/pages/navigator/ui/gui/mcp-section.tsx
5698
- var _tmpl$$46 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
5699
- var _tmpl$2$32 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>/<!> servers updated`);
5700
- var _tmpl$3$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
5701
- var _tmpl$4$11 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching MCP servers.`);
6392
+ var _tmpl$$44 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
6393
+ var _tmpl$2$31 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>/<!> servers updated`);
6394
+ var _tmpl$3$18 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
6395
+ var _tmpl$4$12 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching MCP servers.`);
5702
6396
  function McpSection() {
5703
6397
  const services = useNavigatorGui();
5704
6398
  const catalogs = () => services.runtime.catalogs;
@@ -5750,13 +6444,16 @@ function McpSection() {
5750
6444
  },
5751
6445
  get children() {
5752
6446
  return [(() => {
5753
- var _el$ = _tmpl$$46();
6447
+ var _el$ = _tmpl$$44();
5754
6448
  (0, solid_js_web.insert)(_el$, () => state().error?.message);
5755
6449
  return _el$;
5756
- })(), (0, solid_js_web.createComponent)(Button, {
5757
- size: "small",
5758
- onClick: () => void catalogs().mcp.retry().catch(() => {}),
5759
- children: "Retry"
6450
+ })(), (0, solid_js_web.createComponent)(GuiIconAction, {
6451
+ get ui() {
6452
+ return services.ui;
6453
+ },
6454
+ icon: "refresh",
6455
+ label: "Retry",
6456
+ onClick: () => void catalogs().mcp.retry().catch(() => {})
5760
6457
  })];
5761
6458
  }
5762
6459
  }),
@@ -5766,7 +6463,7 @@ function McpSection() {
5766
6463
  },
5767
6464
  get children() {
5768
6465
  return [(() => {
5769
- var _el$2 = _tmpl$2$32(), _el$3 = _el$2.firstChild, _el$5 = _el$3.nextSibling;
6466
+ var _el$2 = _tmpl$2$31(), _el$3 = _el$2.firstChild, _el$5 = _el$3.nextSibling;
5770
6467
  _el$5.nextSibling;
5771
6468
  (0, solid_js_web.insert)(_el$2, () => bulk().completed, _el$3);
5772
6469
  (0, solid_js_web.insert)(_el$2, () => bulk().total, _el$5);
@@ -5786,13 +6483,13 @@ function McpSection() {
5786
6483
  }
5787
6484
  }),
5788
6485
  (() => {
5789
- var _el$6 = _tmpl$3$19();
6486
+ var _el$6 = _tmpl$3$18();
5790
6487
  (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(solid_js.For, {
5791
6488
  get each() {
5792
6489
  return view().groups();
5793
6490
  },
5794
6491
  get fallback() {
5795
- return _tmpl$4$11();
6492
+ return _tmpl$4$12();
5796
6493
  },
5797
6494
  children: (group) => (0, solid_js_web.createComponent)(GuiMcpGroup, { group })
5798
6495
  }));
@@ -5861,10 +6558,10 @@ function GuiSkillRow(props) {
5861
6558
  });
5862
6559
  }
5863
6560
  // src/pages/navigator/ui/gui/skills-section.tsx
5864
- var _tmpl$$45 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
5865
- var _tmpl$2$31 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
5866
- var _tmpl$3$18 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching Skills.`);
5867
- var _tmpl$4$10 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><h3 style="font-size:12px;opacity:0.65;margin:0 0 4px"></h3><ul style="margin:0;padding:0 0 0 10px;list-style:none">`);
6561
+ var _tmpl$$43 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
6562
+ var _tmpl$2$30 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;flex-direction:column;gap:12px>`);
6563
+ var _tmpl$3$17 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching Skills.`);
6564
+ var _tmpl$4$11 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><h3 style="font-size:12px;opacity:0.65;margin:0 0 4px"></h3><ul style="margin:0;padding:0 0 0 10px;list-style:none">`);
5868
6565
  function SkillsSection() {
5869
6566
  const services = useNavigatorGui();
5870
6567
  const catalogs = () => services.runtime.catalogs;
@@ -5894,27 +6591,30 @@ function SkillsSection() {
5894
6591
  },
5895
6592
  get children() {
5896
6593
  return [(() => {
5897
- var _el$ = _tmpl$$45();
6594
+ var _el$ = _tmpl$$43();
5898
6595
  (0, solid_js_web.insert)(_el$, () => catalogs().skills.error());
5899
6596
  return _el$;
5900
- })(), (0, solid_js_web.createComponent)(Button, {
5901
- size: "small",
5902
- onClick: () => void catalogs().skills.retry().catch(() => {}),
5903
- children: "Retry"
6597
+ })(), (0, solid_js_web.createComponent)(GuiIconAction, {
6598
+ get ui() {
6599
+ return services.ui;
6600
+ },
6601
+ icon: "refresh",
6602
+ label: "Retry",
6603
+ onClick: () => void catalogs().skills.retry().catch(() => {})
5904
6604
  })];
5905
6605
  }
5906
6606
  }),
5907
6607
  (() => {
5908
- var _el$2 = _tmpl$2$31();
6608
+ var _el$2 = _tmpl$2$30();
5909
6609
  (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
5910
6610
  get each() {
5911
6611
  return view().groups();
5912
6612
  },
5913
6613
  get fallback() {
5914
- return _tmpl$3$18();
6614
+ return _tmpl$3$17();
5915
6615
  },
5916
6616
  children: (group) => (() => {
5917
- var _el$4 = _tmpl$4$10(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling;
6617
+ var _el$4 = _tmpl$4$11(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling;
5918
6618
  (0, solid_js_web.insert)(_el$5, group);
5919
6619
  (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(solid_js.For, {
5920
6620
  get each() {
@@ -6227,452 +6927,154 @@ function createSubagentModel(options) {
6227
6927
  status: currentStatuses[session.id] ?? (unavailable()[current.key]?.[session.id] ? void 0 : source.status(session.id))
6228
6928
  })).filter((item) => activeStatus(item.status)).map((item) => ({
6229
6929
  ...item,
6230
- run: history.active(current.key, item.session.id),
6231
- unavailable: unavailable()[current.key]?.[item.session.id]
6232
- }));
6233
- }
6234
- function recent(parentID) {
6235
- const current = target(parentID);
6236
- const sessions = new Map((snapshots()[current.key] ?? []).map((session) => [session.id, session]));
6237
- return history.recent(current.key).flatMap((run) => {
6238
- const session = sessions.get(run.sessionID);
6239
- return session ? [{
6240
- session,
6241
- status: { type: "idle" },
6242
- run,
6243
- unavailable: false
6244
- }] : [];
6245
- });
6246
- }
6247
- const unsubscribe = options.subscribe((event) => {
6248
- switch (event.type) {
6249
- case "upsert":
6250
- recordSession(event);
6251
- break;
6252
- case "remove":
6253
- recordSession(event);
6254
- recordStatus({
6255
- sessionID: event.sessionID,
6256
- status: { type: "idle" }
6257
- });
6258
- break;
6259
- case "status":
6260
- recordStatus(event);
6261
- break;
6262
- case "error":
6263
- recordError(event);
6264
- break;
6265
- case "reconnect": {
6266
- const current = activeTarget ? targets.get(activeTarget) : void 0;
6267
- if (current) retryBackgroundRefresh(() => refreshTarget(current), { signal: options.lifecycle.signal }).catch(() => {});
6268
- break;
6269
- }
6270
- }
6271
- });
6272
- options.lifecycle.onDispose(() => {
6273
- unsubscribe();
6274
- if (pollTimer) clearTimeout(pollTimer);
6275
- });
6276
- return {
6277
- target,
6278
- list,
6279
- recent,
6280
- refresh,
6281
- state(parentID) {
6282
- return requests.state(target(parentID).key);
6283
- },
6284
- retry(parentID) {
6285
- return refresh(parentID, true);
6286
- },
6287
- activate(parentID) {
6288
- const current = target(parentID);
6289
- targets.set(current.key, current);
6290
- if (activeTarget !== current.key) {
6291
- if (pollTimer) clearTimeout(pollTimer);
6292
- requests.abortAll();
6293
- refreshing.clear();
6294
- activeTarget = current.key;
6295
- leases = /* @__PURE__ */ new Set();
6296
- const poll = () => {
6297
- if (activeTarget !== current.key || options.lifecycle.signal.aborted) return;
6298
- refreshTarget(current).catch(() => {});
6299
- pollTimer = setTimeout(poll, POLL_INTERVAL_MS);
6300
- };
6301
- if (options.cache === "model") pollTimer = setTimeout(poll, POLL_INTERVAL_MS);
6302
- }
6303
- const lease = Symbol(current.key);
6304
- leases.add(lease);
6305
- return () => {
6306
- if (activeTarget !== current.key || !leases.delete(lease) || leases.size > 0) return;
6307
- activeTarget = void 0;
6308
- if (pollTimer) clearTimeout(pollTimer);
6309
- pollTimer = void 0;
6310
- requests.abortAll();
6311
- refreshing.clear();
6312
- };
6313
- },
6314
- open(sessionID) {
6315
- options.open(sessionID);
6316
- }
6317
- };
6318
- }
6319
- // src/entities/subagent/model/view.ts
6320
- function filterSubagentView(rows, mode, query) {
6321
- const needle = query.trim().toLocaleLowerCase();
6322
- return rows.filter((row) => {
6323
- if (mode === "active" && row.group !== "Active") return false;
6324
- if (mode === "recent" && row.group !== "Recent") return false;
6325
- if (mode === "errors" && row.run?.outcome !== "error" && !row.unavailable) return false;
6326
- return !needle || [
6327
- row.session.title,
6328
- row.session.id,
6329
- row.run?.errorMessage
6330
- ].some((value) => value?.toLocaleLowerCase().includes(needle));
6331
- });
6332
- }
6333
- function countSubagentView(rows, query) {
6334
- return {
6335
- all: filterSubagentView(rows, "all", query).length,
6336
- active: filterSubagentView(rows, "active", query).length,
6337
- recent: filterSubagentView(rows, "recent", query).length,
6338
- errors: filterSubagentView(rows, "errors", query).length
6339
- };
6340
- }
6341
- function buildSubagentView(active, recent) {
6342
- const priority = (item) => {
6343
- if (item.run?.errorMessage || item.unavailable) return 0;
6344
- return item.status.type === "retry" ? 1 : 2;
6345
- };
6346
- return [...[...active].sort((a, b) => priority(a) - priority(b)).map((item) => ({
6347
- ...item,
6348
- group: "Active"
6349
- })), ...[...recent].sort((a, b) => (b.run?.finishedAt ?? 0) - (a.run?.finishedAt ?? 0) || a.session.id.localeCompare(b.session.id)).map((item) => ({
6350
- ...item,
6351
- group: "Recent"
6352
- }))];
6353
- }
6354
- function formatSubagentDuration(startedAt, now) {
6355
- const seconds = Math.max(0, Math.floor((now - startedAt) / 1e3));
6356
- let value = `${seconds}s`;
6357
- if (seconds >= 3600) value = `${Math.floor(seconds / 3600)}h ${String(Math.floor(seconds / 60) % 60).padStart(2, "0")}m`;
6358
- else if (seconds >= 60) value = `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
6359
- return value;
6360
- }
6361
- function createSubagentClock(key) {
6362
- const [now, setNow] = (0, solid_js.createSignal)(Date.now());
6363
- (0, solid_js.createEffect)(() => {
6364
- if (key() === void 0) return;
6365
- setNow(Date.now());
6366
- const timer = setInterval(() => setNow(Date.now()), 1e3);
6367
- (0, solid_js.onCleanup)(() => clearInterval(timer));
6368
- });
6369
- return now;
6370
- }
6371
- // src/shared/ui/gui/tabs.tsx
6372
- var _tmpl$$44 = /*#__PURE__*/ (0, solid_js_web.template)(`<nav style=display:flex;gap:4px;flex-wrap:wrap>`);
6373
- var _tmpl$2$30 = /*#__PURE__*/ (0, solid_js_web.template)(`<span>`);
6374
- var _tmpl$3$17 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=opacity:0.6;font-weight:400>`);
6375
- function GuiTabs(props) {
6376
- const Button = props.ui.Button;
6377
- function navigate(event) {
6378
- if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
6379
- const enabled = props.options.filter((option) => !option.disabled);
6380
- if (enabled.length === 0) return;
6381
- event.preventDefault();
6382
- event.stopPropagation();
6383
- const next = (enabled.findIndex((option) => option.id === props.selected) + (event.key === "ArrowRight" ? 1 : -1) + enabled.length) % enabled.length;
6384
- props.onSelect(enabled[next].id);
6385
- event.currentTarget.querySelectorAll("button:not(:disabled)")[next]?.focus();
6386
- }
6387
- return (() => {
6388
- var _el$ = _tmpl$$44();
6389
- _el$.$$keydown = navigate;
6390
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
6391
- get each() {
6392
- return props.options.map((option) => option.id);
6393
- },
6394
- children: (id) => {
6395
- const option = () => props.options.find((item) => item.id === id);
6396
- return (0, solid_js_web.createComponent)(Button, {
6397
- size: "small",
6398
- get disabled() {
6399
- return option().disabled;
6400
- },
6401
- get variant() {
6402
- return props.selected === id ? "neutral" : "ghost-muted";
6403
- },
6404
- onClick: () => props.onSelect(id),
6405
- get children() {
6406
- return [(() => {
6407
- var _el$2 = _tmpl$2$30();
6408
- (0, solid_js_web.insert)(_el$2, () => option().label);
6409
- (0, solid_js_web.effect)((_$p) => (0, solid_js_web.setStyleProperty)(_el$2, "font-weight", props.selected === id ? "600" : "400"));
6410
- return _el$2;
6411
- })(), (0, solid_js_web.createComponent)(solid_js.Show, {
6412
- get when() {
6413
- return option().count !== void 0;
6414
- },
6415
- get children() {
6416
- var _el$3 = _tmpl$3$17();
6417
- (0, solid_js_web.insert)(_el$3, () => option().count);
6418
- return _el$3;
6419
- }
6420
- })];
6421
- }
6422
- });
6423
- }
6424
- }));
6425
- (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", props.label));
6426
- return _el$;
6427
- })();
6428
- }
6429
- (0, solid_js_web.delegateEvents)(["keydown"]);
6430
- // src/shared/lib/icon-choices/index.ts
6431
- var ICON_CHOICE_IDS = [
6432
- "plus",
6433
- "check",
6434
- "circle",
6435
- "star",
6436
- "pin",
6437
- "bolt",
6438
- "book",
6439
- "search",
6440
- "code",
6441
- "terminal",
6442
- "gear",
6443
- "shield",
6444
- "lock",
6445
- "unlock",
6446
- "eye",
6447
- "clock",
6448
- "refresh",
6449
- "history",
6450
- "copy",
6451
- "export",
6452
- "edit",
6453
- "fork",
6454
- "list",
6455
- "play",
6456
- "pause",
6457
- "stop",
6458
- "rocket",
6459
- "bug",
6460
- "beaker",
6461
- "wand",
6462
- "cloud",
6463
- "server",
6464
- "folder",
6465
- "file",
6466
- "heart",
6467
- "lightbulb",
6468
- "arrow-up",
6469
- "arrow-down",
6470
- "warning",
6471
- "close"
6472
- ];
6473
- var choices = new Set(ICON_CHOICE_IDS);
6474
- function isIconChoiceId(value) {
6475
- return choices.has(value);
6476
- }
6477
- function iconChoiceLabel(id) {
6478
- return id.replaceAll("-", " ");
6479
- }
6480
- // src/shared/ui/gui/icon.tsx
6481
- var _tmpl$$43 = /*#__PURE__*/ (0, solid_js_web.template)(`<span aria-hidden=true style=display:inline-flex;width:18px;height:18px;flex-shrink:0;align-items:center;justify-content:center>`);
6482
- var _tmpl$2$29 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg width=16 height=16 viewBox="0 0 24 26"fill=none stroke=currentColor stroke-width=1.7 stroke-linecap=round stroke-linejoin=round>`);
6483
- var _tmpl$3$16 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><path></svg>`, false, true, false);
6484
- var native = {
6485
- plus: "plus",
6486
- check: "check",
6487
- search: "magnifying-glass",
6488
- code: "code",
6489
- terminal: "terminal",
6490
- gear: "settings-gear",
6491
- shield: "shield",
6492
- lock: "lock",
6493
- eye: "eye",
6494
- refresh: "refresh",
6495
- copy: "copy",
6496
- export: "share",
6497
- edit: "edit",
6498
- fork: "fork",
6499
- list: "bullet-list",
6500
- stop: "stop",
6501
- beaker: "flask",
6502
- server: "server",
6503
- folder: "folder",
6504
- file: "file-document",
6505
- "arrow-up": "arrow-up",
6506
- warning: "warning",
6507
- close: "close"
6508
- };
6509
- var paths = {
6510
- circle: ["M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0Z"],
6511
- star: ["m12 3 2.7 5.5 6.1.9-4.4 4.3 1 6.1-5.4-2.9-5.4 2.9 1-6.1-4.4-4.3 6.1-.9Z"],
6512
- pin: ["m9 3 6 0-1 6 4 4H6l4-4Z", "M12 13v8"],
6513
- bolt: ["m13 2-8 12h6l-1 8 9-13h-7Z"],
6514
- book: ["M3 4h7l2 2 2-2h7v15h-7l-2 2-2-2H3Z", "M12 6v15"],
6515
- unlock: [
6516
- "M7 10V7a5 5 0 0 1 9-3",
6517
- "M5 10h14v11H5Z",
6518
- "M12 14v3"
6519
- ],
6520
- clock: ["M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0Z", "M12 7v5l3 2"],
6521
- history: [
6522
- "M4 8a9 9 0 1 1-1 7",
6523
- "M4 3v5h5",
6524
- "M12 7v5l3 2"
6525
- ],
6526
- play: ["m7 4 13 8-13 8Z"],
6527
- pause: ["M8 4v16", "M16 4v16"],
6528
- rocket: [
6529
- "M9 16c0-8 6-13 12-13 0 6-5 12-13 12Z",
6530
- "m9 8-5 1-2 6 6-1",
6531
- "m16 15-1 5-6 2 1-6",
6532
- "m5 17-3 5 5-3"
6533
- ],
6534
- bug: [
6535
- "M8 10h8v6a4 4 0 0 1-8 0Z",
6536
- "M9 10V8a3 3 0 0 1 6 0v2",
6537
- "M5 7l3 4M19 7l-3 4M3 14h5m8 0h5M5 21l4-3m6 0 4 3M12 11v9"
6538
- ],
6539
- wand: ["m4 20 13-13 3 3L7 23Z", "M5 3v6M2 6h6M18 1v4M16 3h4"],
6540
- cloud: ["M7 19a5 5 0 1 1 0-10 6 6 0 0 1 11-1 5.5 5.5 0 0 1 0 11Z"],
6541
- heart: ["M12 21 3.5 12.5a5.5 5.5 0 0 1 8.5-7 5.5 5.5 0 0 1 8.5 7Z"],
6542
- lightbulb: ["M9 18v-2a7 7 0 1 1 6 0v2Z", "M9 21h6M10 24h4"],
6543
- "arrow-down": ["M12 3v18", "m5 14 7 7 7-7"]
6544
- };
6545
- function GuiIcon(props) {
6546
- const Icon = props.ui.Icon;
6547
- const id = () => isIconChoiceId(props.id) ? props.id : "bolt";
6548
- return (() => {
6549
- var _el$ = _tmpl$$43();
6550
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
6551
- get when() {
6552
- return native[id()];
6553
- },
6554
- get fallback() {
6555
- return (() => {
6556
- var _el$2 = _tmpl$2$29();
6557
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
6558
- get each() {
6559
- return paths[id()] ?? paths.bolt;
6560
- },
6561
- children: (path) => (() => {
6562
- var _el$3 = _tmpl$3$16();
6563
- (0, solid_js_web.setAttribute)(_el$3, "d", path);
6564
- return _el$3;
6565
- })()
6566
- }));
6567
- return _el$2;
6568
- })();
6569
- },
6570
- children: (name) => (0, solid_js_web.createComponent)(Icon, {
6571
- get name() {
6572
- return name();
6573
- },
6574
- size: "small"
6575
- })
6576
- }));
6577
- return _el$;
6578
- })();
6579
- }
6580
- // src/shared/ui/gui/multiline-field.tsx
6581
- var _tmpl$$42 = /*#__PURE__*/ (0, solid_js_web.template)(`<label style=display:grid;gap:8px><span></span><textarea style="width:100%;box-sizing:border-box;min-height:160px;max-height:40vh;resize:vertical;color:var(--v2-text-text-base, inherit);background:var(--v2-background-bg-base, transparent);border:0;caret-color:var(--v2-text-text-base, auto);border-radius:6px;padding:8px;font:inherit">`);
6582
- function GuiMultilineField(props) {
6583
- const id = (0, solid_js.createUniqueId)();
6584
- const [focused, setFocused] = (0, solid_js.createSignal)(false);
6585
- return (() => {
6586
- var _el$ = _tmpl$$42(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
6587
- (0, solid_js_web.setAttribute)(_el$, "for", id);
6588
- (0, solid_js_web.insert)(_el$2, () => props.label);
6589
- _el$3.$$input = (event) => props.onInput(event.currentTarget.value);
6590
- _el$3.addEventListener("blur", () => setFocused(false));
6591
- _el$3.addEventListener("focus", () => setFocused(true));
6592
- (0, solid_js_web.setAttribute)(_el$3, "id", id);
6593
- (0, solid_js_web.effect)((_p$) => {
6594
- var _v$ = props.disabled, _v$2 = focused() ? "1px solid var(--v2-border-border-focus)" : "1px solid transparent", _v$3 = focused() ? "none" : "var(--v2-elevation-input)", _v$4 = props.disabled ? "0.5" : "1";
6595
- _v$ !== _p$.e && (_el$3.disabled = _p$.e = _v$);
6596
- _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$3, "outline", _p$.t = _v$2);
6597
- _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$3, "box-shadow", _p$.a = _v$3);
6598
- _v$4 !== _p$.o && (0, solid_js_web.setStyleProperty)(_el$3, "opacity", _p$.o = _v$4);
6599
- return _p$;
6600
- }, {
6601
- e: void 0,
6602
- t: void 0,
6603
- a: void 0,
6604
- o: void 0
6605
- });
6606
- (0, solid_js_web.effect)(() => _el$3.value = props.value);
6607
- return _el$;
6608
- })();
6609
- }
6610
- (0, solid_js_web.delegateEvents)(["input"]);
6611
- // src/shared/ui/gui/group-control.tsx
6612
- var _tmpl$$41 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=group style=display:flex;gap:8px;flex-wrap:wrap>`);
6613
- function GuiGroupControl(props) {
6614
- const Select = props.ui.Select;
6615
- const TextInput = props.ui.TextInput;
6616
- const Button = props.ui.Button;
6617
- const [draft, setDraft] = (0, solid_js.createSignal)("");
6618
- (0, solid_js.createEffect)(() => setDraft(props.value ?? ""));
6619
- return (() => {
6620
- var _el$ = _tmpl$$41();
6621
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Select, {
6622
- get label() {
6623
- return `${props.label} existing group`;
6624
- },
6625
- get value() {
6626
- return (0, solid_js_web.memo)(() => !!props.groups.includes(draft()))() ? draft() : "";
6627
- },
6628
- get options() {
6629
- return [{
6630
- value: "",
6631
- label: "Ungrouped"
6632
- }, ...props.groups.map((value) => ({
6633
- value,
6634
- label: value
6635
- }))];
6636
- },
6637
- onChange: setDraft,
6638
- get disabled() {
6639
- return props.disabled;
6930
+ run: history.active(current.key, item.session.id),
6931
+ unavailable: unavailable()[current.key]?.[item.session.id]
6932
+ }));
6933
+ }
6934
+ function recent(parentID) {
6935
+ const current = target(parentID);
6936
+ const sessions = new Map((snapshots()[current.key] ?? []).map((session) => [session.id, session]));
6937
+ return history.recent(current.key).flatMap((run) => {
6938
+ const session = sessions.get(run.sessionID);
6939
+ return session ? [{
6940
+ session,
6941
+ status: { type: "idle" },
6942
+ run,
6943
+ unavailable: false
6944
+ }] : [];
6945
+ });
6946
+ }
6947
+ const unsubscribe = options.subscribe((event) => {
6948
+ switch (event.type) {
6949
+ case "upsert":
6950
+ recordSession(event);
6951
+ break;
6952
+ case "remove":
6953
+ recordSession(event);
6954
+ recordStatus({
6955
+ sessionID: event.sessionID,
6956
+ status: { type: "idle" }
6957
+ });
6958
+ break;
6959
+ case "status":
6960
+ recordStatus(event);
6961
+ break;
6962
+ case "error":
6963
+ recordError(event);
6964
+ break;
6965
+ case "reconnect": {
6966
+ const current = activeTarget ? targets.get(activeTarget) : void 0;
6967
+ if (current) retryBackgroundRefresh(() => refreshTarget(current), { signal: options.lifecycle.signal }).catch(() => {});
6968
+ break;
6640
6969
  }
6641
- }), null);
6642
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(TextInput, {
6643
- get label() {
6644
- return `${props.label} new group`;
6645
- },
6646
- get value() {
6647
- return draft();
6648
- },
6649
- onInput: setDraft,
6650
- get disabled() {
6651
- return props.disabled;
6652
- },
6653
- get invalid() {
6654
- return draft().length > 64;
6970
+ }
6971
+ });
6972
+ options.lifecycle.onDispose(() => {
6973
+ unsubscribe();
6974
+ if (pollTimer) clearTimeout(pollTimer);
6975
+ });
6976
+ return {
6977
+ target,
6978
+ list,
6979
+ recent,
6980
+ refresh,
6981
+ state(parentID) {
6982
+ return requests.state(target(parentID).key);
6983
+ },
6984
+ retry(parentID) {
6985
+ return refresh(parentID, true);
6986
+ },
6987
+ activate(parentID) {
6988
+ const current = target(parentID);
6989
+ targets.set(current.key, current);
6990
+ if (activeTarget !== current.key) {
6991
+ if (pollTimer) clearTimeout(pollTimer);
6992
+ requests.abortAll();
6993
+ refreshing.clear();
6994
+ activeTarget = current.key;
6995
+ leases = /* @__PURE__ */ new Set();
6996
+ const poll = () => {
6997
+ if (activeTarget !== current.key || options.lifecycle.signal.aborted) return;
6998
+ refreshTarget(current).catch(() => {});
6999
+ pollTimer = setTimeout(poll, POLL_INTERVAL_MS);
7000
+ };
7001
+ if (options.cache === "model") pollTimer = setTimeout(poll, POLL_INTERVAL_MS);
6655
7002
  }
6656
- }), null);
6657
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
6658
- size: "small",
6659
- get disabled() {
6660
- return props.disabled || draft().length > 64;
6661
- },
6662
- onClick: () => props.save(draft().trim() || void 0),
6663
- children: "Save group"
6664
- }), null);
6665
- (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$, "aria-label", `${props.label} grouping`));
6666
- return _el$;
6667
- })();
7003
+ const lease = Symbol(current.key);
7004
+ leases.add(lease);
7005
+ return () => {
7006
+ if (activeTarget !== current.key || !leases.delete(lease) || leases.size > 0) return;
7007
+ activeTarget = void 0;
7008
+ if (pollTimer) clearTimeout(pollTimer);
7009
+ pollTimer = void 0;
7010
+ requests.abortAll();
7011
+ refreshing.clear();
7012
+ };
7013
+ },
7014
+ open(sessionID) {
7015
+ options.open(sessionID);
7016
+ }
7017
+ };
7018
+ }
7019
+ // src/entities/subagent/model/view.ts
7020
+ function filterSubagentView(rows, mode, query) {
7021
+ const needle = query.trim().toLocaleLowerCase();
7022
+ return rows.filter((row) => {
7023
+ if (mode === "active" && row.group !== "Active") return false;
7024
+ if (mode === "recent" && row.group !== "Recent") return false;
7025
+ if (mode === "errors" && row.run?.outcome !== "error" && !row.unavailable) return false;
7026
+ return !needle || [
7027
+ row.session.title,
7028
+ row.session.id,
7029
+ row.run?.errorMessage
7030
+ ].some((value) => value?.toLocaleLowerCase().includes(needle));
7031
+ });
7032
+ }
7033
+ function countSubagentView(rows, query) {
7034
+ return {
7035
+ all: filterSubagentView(rows, "all", query).length,
7036
+ active: filterSubagentView(rows, "active", query).length,
7037
+ recent: filterSubagentView(rows, "recent", query).length,
7038
+ errors: filterSubagentView(rows, "errors", query).length
7039
+ };
7040
+ }
7041
+ function buildSubagentView(active, recent) {
7042
+ const priority = (item) => {
7043
+ if (item.run?.errorMessage || item.unavailable) return 0;
7044
+ return item.status.type === "retry" ? 1 : 2;
7045
+ };
7046
+ return [...[...active].sort((a, b) => priority(a) - priority(b)).map((item) => ({
7047
+ ...item,
7048
+ group: "Active"
7049
+ })), ...[...recent].sort((a, b) => (b.run?.finishedAt ?? 0) - (a.run?.finishedAt ?? 0) || a.session.id.localeCompare(b.session.id)).map((item) => ({
7050
+ ...item,
7051
+ group: "Recent"
7052
+ }))];
7053
+ }
7054
+ function formatSubagentDuration(startedAt, now) {
7055
+ const seconds = Math.max(0, Math.floor((now - startedAt) / 1e3));
7056
+ let value = `${seconds}s`;
7057
+ if (seconds >= 3600) value = `${Math.floor(seconds / 3600)}h ${String(Math.floor(seconds / 60) % 60).padStart(2, "0")}m`;
7058
+ else if (seconds >= 60) value = `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`;
7059
+ return value;
7060
+ }
7061
+ function createSubagentClock(key) {
7062
+ const [now, setNow] = (0, solid_js.createSignal)(Date.now());
7063
+ (0, solid_js.createEffect)(() => {
7064
+ if (key() === void 0) return;
7065
+ setNow(Date.now());
7066
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
7067
+ (0, solid_js.onCleanup)(() => clearInterval(timer));
7068
+ });
7069
+ return now;
6668
7070
  }
6669
7071
  // src/pages/navigator/ui/gui/subagent-row.tsx
6670
- var _tmpl$$40 = /*#__PURE__*/ (0, solid_js_web.template)(`<li><p style="font-size:12px;opacity:0.65;margin:0 0 8px;overflow-wrap:anywhere">`);
6671
- var _tmpl$2$28 = /*#__PURE__*/ (0, solid_js_web.template)(`<span> · `);
7072
+ var _tmpl$$42 = /*#__PURE__*/ (0, solid_js_web.template)(`<li><p style="font-size:12px;opacity:0.65;margin:0 0 8px;overflow-wrap:anywhere">`);
7073
+ var _tmpl$2$29 = /*#__PURE__*/ (0, solid_js_web.template)(`<span> · `);
6672
7074
  function SubagentRow(props) {
6673
7075
  const Button = props.ui.Button;
6674
7076
  return (() => {
6675
- var _el$ = _tmpl$$40(), _el$2 = _el$.firstChild;
7077
+ var _el$ = _tmpl$$42(), _el$2 = _el$.firstChild;
6676
7078
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
6677
7079
  size: "small",
6678
7080
  variant: "ghost",
@@ -6689,7 +7091,7 @@ function SubagentRow(props) {
6689
7091
  return props.item.run;
6690
7092
  },
6691
7093
  children: (run) => (() => {
6692
- var _el$3 = _tmpl$2$28();
7094
+ var _el$3 = _tmpl$2$29();
6693
7095
  _el$3.firstChild;
6694
7096
  (0, solid_js_web.insert)(_el$3, () => run().startedBeforeObservation ? "Observed after start · " : "", null);
6695
7097
  (0, solid_js_web.insert)(_el$3, () => formatSubagentDuration(run().startedAt, run().finishedAt ?? props.now), null);
@@ -6700,9 +7102,9 @@ function SubagentRow(props) {
6700
7102
  })();
6701
7103
  }
6702
7104
  // src/pages/navigator/ui/gui/subagent-section.tsx
6703
- var _tmpl$$39 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
6704
- var _tmpl$2$27 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style="padding:0;margin:8px 0 0;list-style:none">`);
6705
- var _tmpl$3$15 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching subagents.`);
7105
+ var _tmpl$$41 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
7106
+ var _tmpl$2$28 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style="padding:0;margin:8px 0 0;list-style:none">`);
7107
+ var _tmpl$3$16 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching subagents.`);
6706
7108
  function SubagentSection() {
6707
7109
  const props = useNavigatorGui();
6708
7110
  const mode = () => props.runtime.view.subagentMode();
@@ -6767,7 +7169,7 @@ function SubagentSection() {
6767
7169
  return state().error;
6768
7170
  },
6769
7171
  get children() {
6770
- var _el$ = _tmpl$$39();
7172
+ var _el$ = _tmpl$$41();
6771
7173
  (0, solid_js_web.insert)(_el$, () => state().error?.message);
6772
7174
  return _el$;
6773
7175
  }
@@ -6777,10 +7179,10 @@ function SubagentSection() {
6777
7179
  return rows().length > 0;
6778
7180
  },
6779
7181
  get fallback() {
6780
- return _tmpl$3$15();
7182
+ return _tmpl$3$16();
6781
7183
  },
6782
7184
  get children() {
6783
- var _el$2 = _tmpl$2$27();
7185
+ var _el$2 = _tmpl$2$28();
6784
7186
  (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
6785
7187
  get each() {
6786
7188
  return keys();
@@ -6959,26 +7361,95 @@ function buildTodoView(todos, mode) {
6959
7361
  }
6960
7362
  };
6961
7363
  }
7364
+ // src/pages/navigator/ui/gui/todo-presentation.ts
7365
+ function todoPresentation(status) {
7366
+ const finished = status === "completed" || status === "cancelled";
7367
+ return {
7368
+ decoration: finished ? "line-through" : "none",
7369
+ text: finished ? "var(--v2-text-text-faint)" : status === "in_progress" ? "var(--v2-text-text-base)" : "var(--v2-text-text-muted)",
7370
+ icon: status === "in_progress" ? "var(--v2-state-fg-warning)" : status === "completed" ? "var(--v2-icon-icon-muted)" : "var(--v2-icon-icon-faint)"
7371
+ };
7372
+ }
7373
+ // src/pages/navigator/ui/gui/todo-status-icon.tsx
7374
+ var _tmpl$$40 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><path d="M8 4a4 4 0 0 0 0 8V4Z"fill=currentColor></svg>`, false, true, false);
7375
+ var _tmpl$2$27 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><path d="M5 8 7.5 11 11 5.5"stroke=currentColor stroke-linecap=round stroke-linejoin=round></svg>`, false, true, false);
7376
+ var _tmpl$3$15 = /*#__PURE__*/ (0, solid_js_web.template)(`<svg><path d="M3.5 12.5 12.5 3.5"stroke=currentColor></svg>`, false, true, false);
7377
+ var _tmpl$4$10 = /*#__PURE__*/ (0, solid_js_web.template)(`<span role=img style=display:inline-flex;height:20px;align-items:center;flex-shrink:0><svg width=16 height=16 viewBox="0 0 16 16"fill=none aria-hidden=true><circle cx=8 cy=8 r=6.5 stroke=currentColor>`);
7378
+ function TodoStatusIcon(props) {
7379
+ const color = () => todoPresentation(props.status).icon;
7380
+ return (() => {
7381
+ var _el$ = _tmpl$4$10(), _el$2 = _el$.firstChild;
7382
+ _el$2.firstChild;
7383
+ (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.Switch, { get children() {
7384
+ return [
7385
+ (0, solid_js_web.createComponent)(solid_js.Match, {
7386
+ get when() {
7387
+ return props.status === "in_progress";
7388
+ },
7389
+ get children() {
7390
+ return _tmpl$$40();
7391
+ }
7392
+ }),
7393
+ (0, solid_js_web.createComponent)(solid_js.Match, {
7394
+ get when() {
7395
+ return props.status === "completed";
7396
+ },
7397
+ get children() {
7398
+ return _tmpl$2$27();
7399
+ }
7400
+ }),
7401
+ (0, solid_js_web.createComponent)(solid_js.Match, {
7402
+ get when() {
7403
+ return props.status === "cancelled";
7404
+ },
7405
+ get children() {
7406
+ return _tmpl$3$15();
7407
+ }
7408
+ })
7409
+ ];
7410
+ } }), null);
7411
+ (0, solid_js_web.effect)((_p$) => {
7412
+ var _v$ = props.status, _v$2 = color();
7413
+ _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
7414
+ _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$, "color", _p$.t = _v$2);
7415
+ return _p$;
7416
+ }, {
7417
+ e: void 0,
7418
+ t: void 0
7419
+ });
7420
+ return _el$;
7421
+ })();
7422
+ }
6962
7423
  // src/pages/navigator/ui/gui/todo-row.tsx
6963
- var _tmpl$$38 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="padding:6px 0;overflow-wrap:anywhere"><span role=img style=opacity:0.6>`);
7424
+ var _tmpl$$39 = /*#__PURE__*/ (0, solid_js_web.template)(`<li data-navigator-todo-row style="display:flex;align-items:flex-start;gap:8px;padding:4px 6px;min-width:0"><span dir=auto data-navigator-todo-text style=flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;line-height:var(--line-height-base);letter-spacing:-0.04px>`);
6964
7425
  function TodoRow(props) {
7426
+ const appearance = () => todoPresentation(props.item.status);
6965
7427
  return (() => {
6966
- var _el$ = _tmpl$$38(), _el$2 = _el$.firstChild;
6967
- (0, solid_js_web.insert)(_el$2, (() => {
6968
- var _c$ = (0, solid_js_web.memo)(() => props.item.status === "completed");
6969
- return () => _c$() ? "✓ " : props.item.status === "in_progress" ? "◉ " : "○ ";
6970
- })());
6971
- (0, solid_js_web.insert)(_el$, () => props.item.content, null);
6972
- (0, solid_js_web.effect)(() => (0, solid_js_web.setAttribute)(_el$2, "aria-label", props.item.status));
7428
+ var _el$ = _tmpl$$39(), _el$2 = _el$.firstChild;
7429
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(TodoStatusIcon, { get status() {
7430
+ return props.item.status;
7431
+ } }), _el$2);
7432
+ (0, solid_js_web.insert)(_el$2, () => props.item.content);
7433
+ (0, solid_js_web.effect)((_p$) => {
7434
+ var _v$ = props.item.status, _v$2 = appearance().text, _v$3 = appearance().decoration;
7435
+ _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "data-status", _p$.e = _v$);
7436
+ _v$2 !== _p$.t && (0, solid_js_web.setStyleProperty)(_el$2, "color", _p$.t = _v$2);
7437
+ _v$3 !== _p$.a && (0, solid_js_web.setStyleProperty)(_el$2, "text-decoration", _p$.a = _v$3);
7438
+ return _p$;
7439
+ }, {
7440
+ e: void 0,
7441
+ t: void 0,
7442
+ a: void 0
7443
+ });
6973
7444
  return _el$;
6974
7445
  })();
6975
7446
  }
6976
7447
  // src/pages/navigator/ui/gui/todo-section.tsx
6977
- var _tmpl$$37 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
7448
+ var _tmpl$$38 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
6978
7449
  var _tmpl$2$26 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65;font-size:12px> completed of `);
6979
7450
  var _tmpl$3$14 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>Reading Todo configuration…`);
6980
7451
  var _tmpl$4$9 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><p>`);
6981
- var _tmpl$5$5 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching tasks. Manage Todo through chat.`);
7452
+ var _tmpl$5$4 = /*#__PURE__*/ (0, solid_js_web.template)(`<p style=opacity:0.65>No matching tasks. Manage Todo through chat.`);
6982
7453
  var _tmpl$6$2 = /*#__PURE__*/ (0, solid_js_web.template)(`<div><h3 style=font-size:12px;opacity:0.65></h3><ul style=padding:0;margin:0;list-style:none>`);
6983
7454
  function TodoSection() {
6984
7455
  const props = useNavigatorGui();
@@ -7024,11 +7495,13 @@ function TodoSection() {
7024
7495
  },
7025
7496
  children: "Enable Todo"
7026
7497
  }), null);
7027
- (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(Button, {
7028
- size: "small",
7029
- variant: "ghost",
7030
- onClick: () => void props.runtime.refresh(true),
7031
- children: "Retry"
7498
+ (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(GuiIconAction, {
7499
+ get ui() {
7500
+ return props.ui;
7501
+ },
7502
+ icon: "refresh",
7503
+ label: "Retry",
7504
+ onClick: () => void props.runtime.refresh(true)
7032
7505
  }), null);
7033
7506
  return _el$6;
7034
7507
  })();
@@ -7076,7 +7549,7 @@ function TodoSection() {
7076
7549
  return state().error;
7077
7550
  },
7078
7551
  get children() {
7079
- var _el$ = _tmpl$$37();
7552
+ var _el$ = _tmpl$$38();
7080
7553
  (0, solid_js_web.insert)(_el$, () => state().error?.message);
7081
7554
  return _el$;
7082
7555
  }
@@ -7086,7 +7559,7 @@ function TodoSection() {
7086
7559
  return setup()?.status === "error";
7087
7560
  },
7088
7561
  get children() {
7089
- var _el$2 = _tmpl$$37();
7562
+ var _el$2 = _tmpl$$38();
7090
7563
  (0, solid_js_web.insert)(_el$2, () => setup()?.error);
7091
7564
  return _el$2;
7092
7565
  }
@@ -7096,7 +7569,7 @@ function TodoSection() {
7096
7569
  return view().rows.length > 0;
7097
7570
  },
7098
7571
  get fallback() {
7099
- return _tmpl$5$5();
7572
+ return _tmpl$5$4();
7100
7573
  },
7101
7574
  get children() {
7102
7575
  return [(() => {
@@ -7140,7 +7613,7 @@ function TodoSection() {
7140
7613
  });
7141
7614
  }
7142
7615
  // src/pages/navigator/ui/gui/sections.tsx
7143
- var _tmpl$$36 = /*#__PURE__*/ (0, solid_js_web.template)(`<hr style="border:0;border-top:1px solid currentColor;opacity:0.18;margin:14px 0">`);
7616
+ var _tmpl$$37 = /*#__PURE__*/ (0, solid_js_web.template)(`<hr style="border:0;border-top:1px solid currentColor;opacity:0.18;margin:14px 0">`);
7144
7617
  function NavigatorSections() {
7145
7618
  const services = useNavigatorGui();
7146
7619
  const preferences = () => services.runtime.catalogs?.preferences;
@@ -7159,7 +7632,7 @@ function NavigatorSections() {
7159
7632
  return index() > 0;
7160
7633
  },
7161
7634
  get children() {
7162
- return _tmpl$$36();
7635
+ return _tmpl$$37();
7163
7636
  }
7164
7637
  }), (0, solid_js_web.createComponent)(solid_js.Switch, { get children() {
7165
7638
  return [
@@ -7204,7 +7677,7 @@ function NavigatorSections() {
7204
7677
  });
7205
7678
  }
7206
7679
  // src/features/pinned-toolbar/ui/gui/icon-picker.tsx
7207
- var _tmpl$$35 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(110px, 1fr));gap:4px;margin:8px 0">`);
7680
+ var _tmpl$$36 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(110px, 1fr));gap:4px;margin:8px 0">`);
7208
7681
  function GuiIconPicker(props) {
7209
7682
  const Dialog = props.ui.Dialog;
7210
7683
  const Button = props.ui.Button;
@@ -7232,7 +7705,7 @@ function GuiIconPicker(props) {
7232
7705
  autofocus: true,
7233
7706
  "class": "w-full"
7234
7707
  }), (() => {
7235
- var _el$ = _tmpl$$35();
7708
+ var _el$ = _tmpl$$36();
7236
7709
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
7237
7710
  get each() {
7238
7711
  return choices();
@@ -7259,13 +7732,24 @@ function GuiIconPicker(props) {
7259
7732
  });
7260
7733
  }
7261
7734
  // src/features/pinned-toolbar/ui/gui/icon-group.tsx
7262
- var _tmpl$$34 = /*#__PURE__*/ (0, solid_js_web.template)(`<ol style=margin:0;padding:0;list-style:none>`);
7263
- var _tmpl$2$25 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="display:flex;gap:4px;margin:4px 0">`);
7735
+ var _tmpl$$35 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="display:flex;gap:4px;margin:4px 0">`);
7264
7736
  function GuiIconGroupEditor(props) {
7265
7737
  const Dialog = props.ui.Dialog;
7266
7738
  const Button = props.ui.Button;
7267
- const [values, setValues] = (0, solid_js.createSignal)([...props.initial]);
7268
- function choose(index) {
7739
+ let nextKey = props.initial.length;
7740
+ const [slots, setSlots] = (0, solid_js.createSignal)(props.initial.map((id, key) => ({
7741
+ id,
7742
+ key
7743
+ })));
7744
+ const values = () => slots().map((slot) => slot.id);
7745
+ function choose(key) {
7746
+ const select = (id) => setSlots((previous) => key === void 0 ? [...previous, {
7747
+ id,
7748
+ key: nextKey++
7749
+ }].slice(0, 3) : previous.map((slot) => slot.key === key ? {
7750
+ ...slot,
7751
+ id
7752
+ } : slot));
7269
7753
  props.dialogs.open((dialog) => (0, solid_js_web.createComponent)(GuiIconPicker, {
7270
7754
  get ui() {
7271
7755
  return props.ui;
@@ -7273,11 +7757,7 @@ function GuiIconGroupEditor(props) {
7273
7757
  get close() {
7274
7758
  return dialog.close;
7275
7759
  },
7276
- choose: (id) => setValues((previous) => {
7277
- const next = [...previous];
7278
- next[index] = id;
7279
- return next;
7280
- })
7760
+ choose: select
7281
7761
  }));
7282
7762
  }
7283
7763
  return (0, solid_js_web.createComponent)(Dialog, {
@@ -7299,59 +7779,64 @@ function GuiIconGroupEditor(props) {
7299
7779
  })];
7300
7780
  },
7301
7781
  get children() {
7302
- return [(() => {
7303
- var _el$ = _tmpl$$34();
7304
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
7305
- get each() {
7306
- return values().map((_value, index) => index);
7307
- },
7308
- children: (index) => (() => {
7309
- var _el$2 = _tmpl$2$25();
7310
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
7311
- variant: "ghost",
7312
- onClick: () => choose(index),
7313
- get children() {
7314
- return [
7315
- (0, solid_js_web.createComponent)(GuiIcon, {
7316
- get ui() {
7317
- return props.ui;
7318
- },
7319
- get id() {
7320
- return values()[index];
7321
- }
7322
- }),
7323
- "Icon ",
7324
- index + 1,
7325
- ": ",
7326
- (0, solid_js_web.memo)(() => iconChoiceLabel(values()[index]))
7327
- ];
7328
- }
7329
- }), null);
7330
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
7331
- variant: "ghost-muted",
7332
- onClick: () => setValues((previous) => previous.filter((_value, offset) => offset !== index)),
7333
- children: "Remove"
7334
- }), null);
7335
- return _el$2;
7336
- })()
7337
- }));
7338
- return _el$;
7339
- })(), (0, solid_js_web.createComponent)(Button, {
7782
+ return [(0, solid_js_web.createComponent)(GuiSortableList, {
7783
+ get ui() {
7784
+ return props.ui;
7785
+ },
7786
+ label: "Button icon order",
7787
+ get items() {
7788
+ return slots();
7789
+ },
7790
+ itemKey: (slot) => String(slot.key),
7791
+ itemLabel: (slot) => `Icon ${slots().findIndex((value) => value.key === slot.key) + 1}: ${iconChoiceLabel(slot.id)}`,
7792
+ scope: "icon-draft",
7793
+ move: (from, to) => setSlots((previous) => reorderItems(previous, from, to, (slot) => String(slot.key))),
7794
+ children: (slot, handle) => (() => {
7795
+ var _el$ = _tmpl$$35();
7796
+ (0, solid_js_web.insert)(_el$, handle, null);
7797
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
7798
+ variant: "ghost",
7799
+ onClick: () => choose(slot().key),
7800
+ get children() {
7801
+ return [
7802
+ (0, solid_js_web.createComponent)(GuiIcon, {
7803
+ get ui() {
7804
+ return props.ui;
7805
+ },
7806
+ get id() {
7807
+ return slot().id;
7808
+ }
7809
+ }),
7810
+ "Icon ",
7811
+ (0, solid_js_web.memo)(() => slots().findIndex((value) => value.key === slot().key) + 1),
7812
+ ": ",
7813
+ (0, solid_js_web.memo)(() => iconChoiceLabel(slot().id))
7814
+ ];
7815
+ }
7816
+ }), null);
7817
+ (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
7818
+ variant: "ghost-muted",
7819
+ onClick: () => setSlots((previous) => previous.filter((value) => value.key !== slot().key)),
7820
+ children: "Remove"
7821
+ }), null);
7822
+ return _el$;
7823
+ })()
7824
+ }), (0, solid_js_web.createComponent)(Button, {
7340
7825
  get disabled() {
7341
7826
  return values().length >= 3;
7342
7827
  },
7343
- onClick: () => choose(values().length),
7828
+ onClick: () => choose(),
7344
7829
  children: "Add icon"
7345
7830
  })];
7346
7831
  }
7347
7832
  });
7348
7833
  }
7349
7834
  // src/features/pinned-toolbar/ui/gui/pin-editor.tsx
7350
- var _tmpl$$33 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>Base appearance`);
7351
- var _tmpl$2$24 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:4px;align-items:center><span>`);
7835
+ var _tmpl$$34 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>Base appearance`);
7836
+ var _tmpl$2$25 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:4px;align-items:center><span>`);
7352
7837
  var _tmpl$3$13 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>Use a single line of up to 128 characters.`);
7353
7838
  var _tmpl$4$8 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>Saved state icons are retained. Desktop uses the base icons unless the action exposes its state publicly.`);
7354
- var _tmpl$5$4 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
7839
+ var _tmpl$5$3 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
7355
7840
  function GuiPinEditor(props) {
7356
7841
  const initial = toolbarPinAppearance(props.pin);
7357
7842
  const [base, setBase] = (0, solid_js.createSignal)([...initial.base]);
@@ -7438,9 +7923,9 @@ function GuiPinEditor(props) {
7438
7923
  },
7439
7924
  get children() {
7440
7925
  return [
7441
- _tmpl$$33(),
7926
+ _tmpl$$34(),
7442
7927
  (() => {
7443
- var _el$2 = _tmpl$2$24(), _el$3 = _el$2.firstChild;
7928
+ var _el$2 = _tmpl$2$25(), _el$3 = _el$2.firstChild;
7444
7929
  (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
7445
7930
  get each() {
7446
7931
  return base();
@@ -7507,7 +7992,7 @@ function GuiPinEditor(props) {
7507
7992
  return error();
7508
7993
  },
7509
7994
  get children() {
7510
- var _el$6 = _tmpl$5$4();
7995
+ var _el$6 = _tmpl$5$3();
7511
7996
  (0, solid_js_web.insert)(_el$6, error);
7512
7997
  return _el$6;
7513
7998
  }
@@ -7517,11 +8002,10 @@ function GuiPinEditor(props) {
7517
8002
  });
7518
8003
  }
7519
8004
  // src/features/pinned-toolbar/ui/gui/manager.tsx
7520
- var _tmpl$$32 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
7521
- var _tmpl$2$23 = /*#__PURE__*/ (0, solid_js_web.template)(`<ol style=padding:0;margin:0;list-style:none>`);
7522
- var _tmpl$3$12 = /*#__PURE__*/ (0, solid_js_web.template)(`<li>Pin a Skill or Quick Action from its row menu.`);
7523
- var _tmpl$4$7 = /*#__PURE__*/ (0, solid_js_web.template)(`<small style=display:block;opacity:0.6>`);
7524
- var _tmpl$5$3 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="padding:8px 0;display:flex;gap:4px;align-items:center;min-width:0"><span style=flex:1;min-width:0;overflow-wrap:anywhere><strong>`);
8005
+ var _tmpl$$33 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
8006
+ var _tmpl$2$24 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>Pin a Skill or Quick Action from its row menu.`);
8007
+ var _tmpl$3$12 = /*#__PURE__*/ (0, solid_js_web.template)(`<small style=display:block;opacity:0.6>`);
8008
+ var _tmpl$4$7 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style="padding:8px 0;display:flex;gap:4px;align-items:center;min-width:0"><span style=flex:1;min-width:0;overflow-wrap:anywhere><strong>`);
7525
8009
  var source = (pin) => pin.kind === "skill" ? pin.location : void 0;
7526
8010
  function GuiToolbarManager(props) {
7527
8011
  const Dialog = props.ui.Dialog;
@@ -7557,89 +8041,82 @@ function GuiToolbarManager(props) {
7557
8041
  get onClick() {
7558
8042
  return props.close;
7559
8043
  },
7560
- children: "Close"
7561
- });
7562
- },
7563
- get children() {
7564
- return [(0, solid_js_web.createComponent)(solid_js.Show, {
7565
- get when() {
7566
- return error();
7567
- },
7568
- get children() {
7569
- var _el$ = _tmpl$$32();
7570
- (0, solid_js_web.insert)(_el$, error);
7571
- return _el$;
7572
- }
7573
- }), (() => {
7574
- var _el$2 = _tmpl$2$23();
7575
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
7576
- get each() {
7577
- return props.pins.map((pin) => toolbarPinKey(pin));
8044
+ children: "Close"
8045
+ });
8046
+ },
8047
+ get children() {
8048
+ return [
8049
+ (0, solid_js_web.createComponent)(solid_js.Show, {
8050
+ get when() {
8051
+ return error();
7578
8052
  },
7579
- get fallback() {
7580
- return _tmpl$3$12();
8053
+ get children() {
8054
+ var _el$ = _tmpl$$33();
8055
+ (0, solid_js_web.insert)(_el$, error);
8056
+ return _el$;
8057
+ }
8058
+ }),
8059
+ (0, solid_js_web.createComponent)(solid_js.Show, {
8060
+ get when() {
8061
+ return props.pins.length === 0;
7581
8062
  },
7582
- children: (key, index) => (0, solid_js_web.createComponent)(solid_js.Show, {
7583
- get when() {
7584
- return props.pins.find((item) => toolbarPinKey(item) === key);
7585
- },
7586
- children: (pin) => (() => {
7587
- var _el$4 = _tmpl$5$3(), _el$5 = _el$4.firstChild, _el$6 = _el$5.firstChild;
7588
- (0, solid_js_web.insert)(_el$6, () => props.label(pin()));
7589
- (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(solid_js.Show, {
7590
- get when() {
7591
- return source(pin());
7592
- },
7593
- get children() {
7594
- var _el$7 = _tmpl$4$7();
7595
- (0, solid_js_web.insert)(_el$7, () => source(pin()));
7596
- return _el$7;
7597
- }
7598
- }), null);
7599
- (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(IconButton, {
7600
- icon: "arrow-up",
7601
- get label() {
7602
- return `Move ${props.label(pin())} up`;
7603
- },
7604
- get disabled() {
7605
- return busy() || index() === 0;
7606
- },
7607
- onClick: () => void run(() => props.move(key, -1))
7608
- }), null);
7609
- (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(IconButton, {
7610
- icon: "chevron-down",
7611
- get label() {
7612
- return `Move ${props.label(pin())} down`;
7613
- },
7614
- get disabled() {
7615
- return busy() || index() === props.pins.length - 1;
7616
- },
7617
- onClick: () => void run(() => props.move(key, 1))
7618
- }), null);
7619
- (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(Button, {
7620
- size: "small",
7621
- get disabled() {
7622
- return busy();
7623
- },
7624
- onClick: () => props.configure(key),
7625
- children: "Configure"
7626
- }), null);
7627
- (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(IconButton, {
7628
- icon: "close",
7629
- get label() {
7630
- return `Unpin ${props.label(pin())}`;
7631
- },
7632
- get disabled() {
7633
- return busy();
7634
- },
7635
- onClick: () => void run(() => props.remove(key))
7636
- }), null);
7637
- return _el$4;
7638
- })()
7639
- })
7640
- }));
7641
- return _el$2;
7642
- })()];
8063
+ get children() {
8064
+ return _tmpl$2$24();
8065
+ }
8066
+ }),
8067
+ (0, solid_js_web.createComponent)(GuiSortableList, {
8068
+ get ui() {
8069
+ return props.ui;
8070
+ },
8071
+ label: "Toolbar pin order",
8072
+ get items() {
8073
+ return props.pins;
8074
+ },
8075
+ itemKey: toolbarPinKey,
8076
+ get itemLabel() {
8077
+ return props.label;
8078
+ },
8079
+ scope: "toolbar",
8080
+ get disabled() {
8081
+ return busy() || !props.current();
8082
+ },
8083
+ move: (from, to) => void run(() => props.move(from, to)),
8084
+ children: (pin, handle) => (() => {
8085
+ var _el$3 = _tmpl$4$7(), _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild;
8086
+ (0, solid_js_web.insert)(_el$5, () => props.label(pin()));
8087
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(solid_js.Show, {
8088
+ get when() {
8089
+ return source(pin());
8090
+ },
8091
+ get children() {
8092
+ var _el$6 = _tmpl$3$12();
8093
+ (0, solid_js_web.insert)(_el$6, () => source(pin()));
8094
+ return _el$6;
8095
+ }
8096
+ }), null);
8097
+ (0, solid_js_web.insert)(_el$3, handle, null);
8098
+ (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(Button, {
8099
+ size: "small",
8100
+ get disabled() {
8101
+ return busy();
8102
+ },
8103
+ onClick: () => props.configure(toolbarPinKey(pin())),
8104
+ children: "Configure"
8105
+ }), null);
8106
+ (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(IconButton, {
8107
+ icon: "close",
8108
+ get label() {
8109
+ return `Unpin ${props.label(pin())}`;
8110
+ },
8111
+ get disabled() {
8112
+ return busy();
8113
+ },
8114
+ onClick: () => void run(() => props.remove(toolbarPinKey(pin())))
8115
+ }), null);
8116
+ return _el$3;
8117
+ })()
8118
+ })
8119
+ ];
7643
8120
  }
7644
8121
  });
7645
8122
  }
@@ -7760,8 +8237,8 @@ function createMeasuredToolbar() {
7760
8237
  };
7761
8238
  }
7762
8239
  // src/features/pinned-toolbar/ui/gui/toolbar-button.tsx
7763
- var _tmpl$$31 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap>`);
7764
- var _tmpl$2$22 = /*#__PURE__*/ (0, solid_js_web.template)(`<span aria-hidden=true style=display:inline-flex;gap:4px;align-items:center;min-width:0;max-width:100%><span style=white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0>`);
8240
+ var _tmpl$$32 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap>`);
8241
+ var _tmpl$2$23 = /*#__PURE__*/ (0, solid_js_web.template)(`<span aria-hidden=true style=display:inline-flex;gap:4px;align-items:center;min-width:0;max-width:100%><span style=white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0>`);
7765
8242
  var _tmpl$3$11 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=display:inline-flex;min-width:0;flex-shrink:0>`);
7766
8243
  function GuiToolbarButton(props) {
7767
8244
  const Button = props.ui.Button;
@@ -7783,11 +8260,11 @@ function GuiToolbarButton(props) {
7783
8260
  },
7784
8261
  get children() {
7785
8262
  return [(() => {
7786
- var _el$ = _tmpl$$31();
8263
+ var _el$ = _tmpl$$32();
7787
8264
  (0, solid_js_web.insert)(_el$, label);
7788
8265
  return _el$;
7789
8266
  })(), (() => {
7790
- var _el$2 = _tmpl$2$22(), _el$3 = _el$2.firstChild;
8267
+ var _el$2 = _tmpl$2$23(), _el$3 = _el$2.firstChild;
7791
8268
  (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.For, {
7792
8269
  get each() {
7793
8270
  return view().icons;
@@ -7846,8 +8323,8 @@ function GuiToolbarButton(props) {
7846
8323
  }
7847
8324
  (0, solid_js_web.delegateEvents)(["contextmenu", "keydown"]);
7848
8325
  // src/features/pinned-toolbar/ui/gui/toolbar.tsx
7849
- var _tmpl$$30 = /*#__PURE__*/ (0, solid_js_web.template)(`<span data-toolbar-visible>`);
7850
- var _tmpl$2$21 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-toolbar role=toolbar tabindex=-1 aria-label="Navigator pins"style="position:relative;display:flex;gap:4px;align-items:center;justify-content:center;padding:0 4px;min-height:28px;margin-bottom:8px"><div aria-hidden=true inert style=position:absolute;left:0;top:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none><div style=display:flex;gap:4px;width:max-content><span>`);
8326
+ var _tmpl$$31 = /*#__PURE__*/ (0, solid_js_web.template)(`<span data-toolbar-visible>`);
8327
+ var _tmpl$2$22 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-toolbar role=toolbar tabindex=-1 aria-label="Navigator pins"style="position:relative;display:flex;gap:4px;align-items:center;justify-content:center;padding:0 4px;min-height:28px;margin-bottom:8px"><div aria-hidden=true inert style=position:absolute;left:0;top:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none><div style=display:flex;gap:4px;width:max-content><span>`);
7851
8328
  function navigate(event) {
7852
8329
  if (event.defaultPrevented || event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
7853
8330
  const buttons = [...event.currentTarget.querySelectorAll(":scope [data-toolbar-visible] button")];
@@ -7871,7 +8348,7 @@ function GuiPinnedToolbar(props) {
7871
8348
  return keys().length > 0;
7872
8349
  },
7873
8350
  get children() {
7874
- var _el$ = _tmpl$2$21(), _el$3 = _el$.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild;
8351
+ var _el$ = _tmpl$2$22(), _el$3 = _el$.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild;
7875
8352
  _el$.$$keydown = navigate;
7876
8353
  var _ref$ = layout.root;
7877
8354
  typeof _ref$ === "function" ? (0, solid_js_web.use)(_ref$, _el$) : layout.root = _el$;
@@ -7907,7 +8384,7 @@ function GuiPinnedToolbar(props) {
7907
8384
  return hidden().length > 0;
7908
8385
  },
7909
8386
  get children() {
7910
- var _el$2 = _tmpl$$30();
8387
+ var _el$2 = _tmpl$$31();
7911
8388
  (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Menu, {
7912
8389
  label: "More toolbar items",
7913
8390
  trigger: "icon",
@@ -7998,7 +8475,7 @@ function NavigatorPinnedToolbar() {
7998
8475
  });
7999
8476
  }
8000
8477
  // src/pages/navigator/ui/gui/monitoring-content.tsx
8001
- var _tmpl$$29 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-content style=padding:12px;overflow-y:auto;min-height:0;flex:1;font-size:13px>`);
8478
+ var _tmpl$$30 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-content style=padding:12px;overflow-y:auto;min-height:0;flex:1;font-size:13px>`);
8002
8479
  function MonitoringContent(props) {
8003
8480
  let body;
8004
8481
  (0, solid_js.onMount)(() => {
@@ -8007,7 +8484,7 @@ function MonitoringContent(props) {
8007
8484
  return (0, solid_js_web.createComponent)(NavigatorGuiContext.Provider, {
8008
8485
  value: props,
8009
8486
  get children() {
8010
- var _el$ = _tmpl$$29();
8487
+ var _el$ = _tmpl$$30();
8011
8488
  _el$.addEventListener("scroll", (event) => props.onScroll?.(event.currentTarget.scrollTop));
8012
8489
  (0, solid_js_web.use)((node) => {
8013
8490
  body = node;
@@ -8662,10 +9139,8 @@ function createMiniWindowInteraction(props) {
8662
9139
  };
8663
9140
  }
8664
9141
  // src/pages/navigator/ui/gui/mini-window.tsx
8665
- var _tmpl$$28 = /*#__PURE__*/ (0, solid_js_web.template)(`<aside data-navigator-floating style="position:fixed;display:flex;flex-direction:column;z-index:40;background:var(--v2-background-bg-layer-01);color:var(--v2-text-text-base);border:1px solid var(--v2-border-border-base, color-mix(in srgb, currentColor 20%, transparent));border-radius:12px;box-shadow:var(--v2-elevation-overlay, 0 8px 32px #0003);overflow:hidden;font-size:13px"><header style=display:flex;align-items:center;gap:4px;padding:8px;flex-wrap:wrap;cursor:move;touch-action:none;user-select:none><span data-navigator-move title="Drag or use arrow keys; Shift moves ten pixels"></span><strong style=flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap></strong></header><footer style=display:flex;gap:4px;padding:6px;align-items:center><span title="Drag or use arrow keys; Shift resizes ten pixels"style=margin-left:auto;cursor:nwse-resize;touch-action:none>`);
9142
+ var _tmpl$$29 = /*#__PURE__*/ (0, solid_js_web.template)(`<aside data-navigator-floating style="position:fixed;display:flex;flex-direction:column;z-index:40;background:var(--v2-background-bg-layer-01);color:var(--v2-text-text-base);border:1px solid var(--v2-border-border-base, color-mix(in srgb, currentColor 20%, transparent));border-radius:12px;box-shadow:var(--v2-elevation-overlay, 0 8px 32px #0003);overflow:hidden;font-size:13px"><header style=display:grid;gap:6px;padding:8px;cursor:move;touch-action:none;user-select:none><strong data-navigator-session-title style=min-width:0;overflow:hidden;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:20px></strong><div role=group aria-label="Mini-window actions"style=display:flex;gap:4px;align-items:center><span data-navigator-move title="Drag or use arrow keys; Shift moves ten pixels"></span></div></header><footer style=display:flex;gap:4px;padding:6px;align-items:center><span title="Drag or use arrow keys; Shift resizes ten pixels"style=margin-left:auto;cursor:nwse-resize;touch-action:none>`);
8666
9143
  function NavigatorMiniWindow(props) {
8667
- const Button = props.ui.Button;
8668
- const IconButton = props.ui.IconButton;
8669
9144
  const interaction = createMiniWindowInteraction(props);
8670
9145
  function dragHeader(event) {
8671
9146
  const target = event.target;
@@ -8673,63 +9148,77 @@ function NavigatorMiniWindow(props) {
8673
9148
  interaction.start(event, "move");
8674
9149
  }
8675
9150
  return (() => {
8676
- var _el$ = _tmpl$$28(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$2.nextSibling, _el$6 = _el$5.firstChild;
9151
+ var _el$ = _tmpl$$29(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, _el$5 = _el$4.firstChild, _el$6 = _el$2.nextSibling, _el$7 = _el$6.firstChild;
8677
9152
  (0, solid_js_web.addEventListener)(_el$2, "lostpointercapture", interaction.end);
8678
9153
  (0, solid_js_web.addEventListener)(_el$2, "pointercancel", interaction.end);
8679
9154
  (0, solid_js_web.addEventListener)(_el$2, "pointerup", interaction.end, true);
8680
9155
  (0, solid_js_web.addEventListener)(_el$2, "pointermove", interaction.move, true);
8681
9156
  _el$2.$$pointerdown = dragHeader;
8682
- _el$3.$$keydown = (event) => interaction.key(event, "move");
8683
- (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(IconButton, {
9157
+ (0, solid_js_web.insert)(_el$3, () => props.title);
9158
+ _el$5.$$keydown = (event) => interaction.key(event, "move");
9159
+ (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(GuiIconAction, {
9160
+ get ui() {
9161
+ return props.ui;
9162
+ },
8684
9163
  icon: "dot-grid",
8685
9164
  label: "Move Navigator mini-window"
8686
9165
  }));
8687
- (0, solid_js_web.insert)(_el$4, () => props.title);
8688
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
8689
- size: "small",
8690
- variant: "ghost",
9166
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(GuiIconAction, {
9167
+ get ui() {
9168
+ return props.ui;
9169
+ },
9170
+ icon: "layout-right",
9171
+ label: "Dock",
8691
9172
  get onClick() {
8692
9173
  return props.redock;
8693
- },
8694
- children: "Dock"
9174
+ }
8695
9175
  }), null);
8696
- (0, solid_js_web.insert)(_el$, () => props.children, _el$5);
8697
- (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(Button, {
8698
- size: "small",
8699
- variant: "ghost",
9176
+ (0, solid_js_web.insert)(_el$, () => props.children, _el$6);
9177
+ (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(GuiIconAction, {
9178
+ get ui() {
9179
+ return props.ui;
9180
+ },
9181
+ icon: "magnifying-glass",
9182
+ label: "Search",
8700
9183
  get onClick() {
8701
9184
  return props.search;
9185
+ }
9186
+ }), _el$7);
9187
+ (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(GuiIconAction, {
9188
+ get ui() {
9189
+ return props.ui;
8702
9190
  },
8703
- children: "Search"
8704
- }), _el$6);
8705
- (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(Button, {
8706
- size: "small",
8707
- variant: "ghost-muted",
9191
+ icon: "refresh",
9192
+ label: "Refresh",
8708
9193
  get onClick() {
8709
9194
  return props.refresh;
9195
+ }
9196
+ }), _el$7);
9197
+ (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(GuiIconAction, {
9198
+ get ui() {
9199
+ return props.ui;
8710
9200
  },
8711
- children: "Refresh"
8712
- }), _el$6);
8713
- (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(Button, {
8714
- size: "small",
8715
- variant: "ghost-muted",
9201
+ icon: "outline-reset",
9202
+ label: "Reset position",
8716
9203
  get onClick() {
8717
9204
  return interaction.reset;
9205
+ }
9206
+ }), _el$7);
9207
+ _el$7.$$keydown = (event) => interaction.key(event, "resize");
9208
+ (0, solid_js_web.addEventListener)(_el$7, "lostpointercapture", interaction.end);
9209
+ (0, solid_js_web.addEventListener)(_el$7, "pointercancel", interaction.end);
9210
+ (0, solid_js_web.addEventListener)(_el$7, "pointerup", interaction.end, true);
9211
+ (0, solid_js_web.addEventListener)(_el$7, "pointermove", interaction.move, true);
9212
+ _el$7.$$pointerdown = (event) => interaction.start(event, "resize");
9213
+ (0, solid_js_web.insert)(_el$7, (0, solid_js_web.createComponent)(GuiIconAction, {
9214
+ get ui() {
9215
+ return props.ui;
8718
9216
  },
8719
- children: "Reset position"
8720
- }), _el$6);
8721
- _el$6.$$keydown = (event) => interaction.key(event, "resize");
8722
- (0, solid_js_web.addEventListener)(_el$6, "lostpointercapture", interaction.end);
8723
- (0, solid_js_web.addEventListener)(_el$6, "pointercancel", interaction.end);
8724
- (0, solid_js_web.addEventListener)(_el$6, "pointerup", interaction.end, true);
8725
- (0, solid_js_web.addEventListener)(_el$6, "pointermove", interaction.move, true);
8726
- _el$6.$$pointerdown = (event) => interaction.start(event, "resize");
8727
- (0, solid_js_web.insert)(_el$6, (0, solid_js_web.createComponent)(IconButton, {
8728
9217
  icon: "outline-expand",
8729
9218
  label: "Resize Navigator mini-window"
8730
9219
  }));
8731
9220
  (0, solid_js_web.effect)((_p$) => {
8732
- var _v$ = `Navigator mini-window: ${props.title}`, _v$2 = props.modal, _v$3 = props.modal, _v$4 = `${props.rect.x}px`, _v$5 = `${props.rect.y}px`, _v$6 = `${props.rect.width}px`, _v$7 = `${props.rect.height}px`, _v$8 = props.modal ? "hidden" : "visible", _v$9 = props.mode;
9221
+ var _v$ = `Navigator mini-window: ${props.title}`, _v$2 = props.modal, _v$3 = props.modal, _v$4 = `${props.rect.x}px`, _v$5 = `${props.rect.y}px`, _v$6 = `${props.rect.width}px`, _v$7 = `${props.rect.height}px`, _v$8 = props.modal ? "hidden" : "visible", _v$9 = props.mode, _v$0 = props.title;
8733
9222
  _v$ !== _p$.e && (0, solid_js_web.setAttribute)(_el$, "aria-label", _p$.e = _v$);
8734
9223
  _v$2 !== _p$.t && (0, solid_js_web.setAttribute)(_el$, "aria-hidden", _p$.t = _v$2);
8735
9224
  _v$3 !== _p$.a && (_el$.inert = _p$.a = _v$3);
@@ -8739,6 +9228,7 @@ function NavigatorMiniWindow(props) {
8739
9228
  _v$7 !== _p$.s && (0, solid_js_web.setStyleProperty)(_el$, "height", _p$.s = _v$7);
8740
9229
  _v$8 !== _p$.h && (0, solid_js_web.setStyleProperty)(_el$, "visibility", _p$.h = _v$8);
8741
9230
  _v$9 !== _p$.r && (0, solid_js_web.setStyleProperty)(_el$, "color-scheme", _p$.r = _v$9);
9231
+ _v$0 !== _p$.d && (0, solid_js_web.setAttribute)(_el$3, "title", _p$.d = _v$0);
8742
9232
  return _p$;
8743
9233
  }, {
8744
9234
  e: void 0,
@@ -8749,7 +9239,8 @@ function NavigatorMiniWindow(props) {
8749
9239
  n: void 0,
8750
9240
  s: void 0,
8751
9241
  h: void 0,
8752
- r: void 0
9242
+ r: void 0,
9243
+ d: void 0
8753
9244
  });
8754
9245
  return _el$;
8755
9246
  })();
@@ -8761,48 +9252,56 @@ function NavigatorMiniWindow(props) {
8761
9252
  "keydown"
8762
9253
  ]);
8763
9254
  // src/pages/navigator/ui/gui/monitoring-panel.tsx
8764
- var _tmpl$$27 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-panel style=display:flex;flex-direction:column;height:100%;min-height:0><header style="padding:8px 12px;display:flex;gap:4px;align-items:center"><strong style=flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap>`);
8765
- var _tmpl$2$20 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=padding:12px><p>Navigator is in its mini-window.`);
8766
- var _tmpl$3$10 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>Loading Navigator…`);
9255
+ var _tmpl$$28 = /*#__PURE__*/ (0, solid_js_web.template)(`<div data-navigator-panel style=display:flex;flex-direction:column;height:100%;min-height:0><header data-navigator-panel-header style="padding:8px 12px;display:grid;gap:6px;min-width:0"><strong data-navigator-session-title style=min-width:0;overflow:hidden;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:20px></strong><div role=group aria-label="Navigator actions"style=display:flex;gap:4px;align-items:center;flex-wrap:wrap>`);
9256
+ var _tmpl$2$21 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=padding:12px><p>Navigator is in its mini-window.`);
9257
+ var _tmpl$3$10 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status> Loading Navigator…`);
8767
9258
  function MonitoringPanel(props) {
8768
- const Button = props.ui.Button;
9259
+ const Loader = props.ui.Loader;
8769
9260
  return (() => {
8770
- var _el$ = _tmpl$$27(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
9261
+ var _el$ = _tmpl$$28(), _el$3 = _el$.firstChild.firstChild, _el$4 = _el$3.nextSibling;
8771
9262
  (0, solid_js_web.insert)(_el$3, () => props.title);
8772
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
8773
- size: "small",
8774
- variant: "ghost",
9263
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(GuiIconAction, {
9264
+ get ui() {
9265
+ return props.ui;
9266
+ },
9267
+ icon: "magnifying-glass",
9268
+ label: "Search",
8775
9269
  get onClick() {
8776
9270
  return props.search;
8777
- },
8778
- children: "Search"
9271
+ }
8779
9272
  }), null);
8780
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
8781
- size: "small",
8782
- variant: "ghost-muted",
8783
- onClick: () => void props.runtime?.refresh(true),
8784
- children: "Refresh"
9273
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(GuiIconAction, {
9274
+ get ui() {
9275
+ return props.ui;
9276
+ },
9277
+ icon: "refresh",
9278
+ label: "Refresh",
9279
+ onClick: () => void props.runtime?.refresh(true)
8785
9280
  }), null);
8786
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Button, {
8787
- size: "small",
8788
- variant: "ghost",
9281
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(GuiIconAction, {
9282
+ get ui() {
9283
+ return props.ui;
9284
+ },
9285
+ icon: "expand",
9286
+ label: "Float",
8789
9287
  get onClick() {
8790
9288
  return props.float;
8791
- },
8792
- children: "Float"
9289
+ }
8793
9290
  }), null);
8794
- (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(solid_js.Show, {
9291
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(solid_js.Show, {
8795
9292
  get when() {
8796
9293
  return props.onSettings;
8797
9294
  },
8798
9295
  get children() {
8799
- return (0, solid_js_web.createComponent)(Button, {
8800
- size: "small",
8801
- variant: "ghost",
9296
+ return (0, solid_js_web.createComponent)(GuiIconAction, {
9297
+ get ui() {
9298
+ return props.ui;
9299
+ },
9300
+ icon: "settings-gear",
9301
+ label: "Settings",
8802
9302
  get onClick() {
8803
9303
  return props.onSettings;
8804
- },
8805
- children: "Settings"
9304
+ }
8806
9305
  });
8807
9306
  }
8808
9307
  }), null);
@@ -8812,16 +9311,19 @@ function MonitoringPanel(props) {
8812
9311
  },
8813
9312
  get fallback() {
8814
9313
  return (() => {
8815
- var _el$4 = _tmpl$2$20();
8816
- _el$4.firstChild;
8817
- (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(Button, {
8818
- size: "small",
9314
+ var _el$5 = _tmpl$2$21();
9315
+ _el$5.firstChild;
9316
+ (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(GuiIconAction, {
9317
+ get ui() {
9318
+ return props.ui;
9319
+ },
9320
+ icon: "layout-right",
9321
+ label: "Dock Navigator",
8819
9322
  get onClick() {
8820
9323
  return props.dock;
8821
- },
8822
- children: "Dock Navigator"
9324
+ }
8823
9325
  }), null);
8824
- return _el$4;
9326
+ return _el$5;
8825
9327
  })();
8826
9328
  },
8827
9329
  get children() {
@@ -8830,7 +9332,11 @@ function MonitoringPanel(props) {
8830
9332
  return props.runtime;
8831
9333
  },
8832
9334
  get fallback() {
8833
- return _tmpl$3$10();
9335
+ return (() => {
9336
+ var _el$7 = _tmpl$3$10(), _el$8 = _el$7.firstChild;
9337
+ (0, solid_js_web.insert)(_el$7, (0, solid_js_web.createComponent)(Loader, { size: "small" }), _el$8);
9338
+ return _el$7;
9339
+ })();
8834
9340
  },
8835
9341
  children: (runtime) => (0, solid_js_web.createComponent)(MonitoringContent, (0, solid_js_web.mergeProps)(props, { get runtime() {
8836
9342
  return runtime();
@@ -8877,9 +9383,23 @@ function parseChangelog(source) {
8877
9383
  function changesSince(source, previous, current) {
8878
9384
  return parseChangelog(source).filter((entry) => entry.changes.length > 0 && isNewerVersion(entry.version, previous) && !isNewerVersion(entry.version, current));
8879
9385
  }
9386
+ // src/entities/navigator-release/model/update-progress.ts
9387
+ function updateProgressView(progress) {
9388
+ const labels = {
9389
+ preparing: "Preparing",
9390
+ downloading: "Downloading",
9391
+ installing: "Downloading / installing",
9392
+ verifying: "Verifying"
9393
+ };
9394
+ const fraction = progress.total !== void 0 && Number.isFinite(progress.total) && progress.total > 0 && progress.received !== void 0 && Number.isFinite(progress.received) && progress.received >= 0 ? Math.min(1, progress.received / progress.total) : void 0;
9395
+ return {
9396
+ label: fraction === void 0 ? labels[progress.phase] : `${labels[progress.phase]} ${Math.floor(fraction * 100)}%`,
9397
+ fraction
9398
+ };
9399
+ }
8880
9400
  // src/features/sidebar-settings/ui/gui/mcp-preset-menu.tsx
8881
- var _tmpl$$26 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style="margin:8px 0;padding:0;list-style:none">`);
8882
- var _tmpl$2$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<li>No matching saved presets.`);
9401
+ var _tmpl$$27 = /*#__PURE__*/ (0, solid_js_web.template)(`<ul style="margin:8px 0;padding:0;list-style:none">`);
9402
+ var _tmpl$2$20 = /*#__PURE__*/ (0, solid_js_web.template)(`<li>No matching saved presets.`);
8883
9403
  var _tmpl$3$9 = /*#__PURE__*/ (0, solid_js_web.template)(`<li>`);
8884
9404
  function GuiMcpPresetMenu(props) {
8885
9405
  const Dialog = props.ui.Dialog;
@@ -8912,13 +9432,13 @@ function GuiMcpPresetMenu(props) {
8912
9432
  autofocus: true,
8913
9433
  "class": "w-full"
8914
9434
  }), (() => {
8915
- var _el$ = _tmpl$$26();
9435
+ var _el$ = _tmpl$$27();
8916
9436
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.For, {
8917
9437
  get each() {
8918
9438
  return names();
8919
9439
  },
8920
9440
  get fallback() {
8921
- return _tmpl$2$19();
9441
+ return _tmpl$2$20();
8922
9442
  },
8923
9443
  children: (name) => (() => {
8924
9444
  var _el$3 = _tmpl$3$9();
@@ -8936,8 +9456,8 @@ function GuiMcpPresetMenu(props) {
8936
9456
  });
8937
9457
  }
8938
9458
  // src/features/sidebar-settings/ui/gui/mcp-preview-rows.tsx
8939
- var _tmpl$$25 = /*#__PURE__*/ (0, solid_js_web.template)(`<section><h3></h3><ul style=padding:0;list-style:none>`);
8940
- var _tmpl$2$18 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="padding:6px 0;overflow-wrap:anywhere"><strong></strong><p style="margin:2px 0;opacity:0.65"> · `);
9459
+ var _tmpl$$26 = /*#__PURE__*/ (0, solid_js_web.template)(`<section><h3></h3><ul style=padding:0;list-style:none>`);
9460
+ var _tmpl$2$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="padding:6px 0;overflow-wrap:anywhere"><strong></strong><p style="margin:2px 0;opacity:0.65"> · `);
8941
9461
  var groups = [
8942
9462
  "Changes",
8943
9463
  "Skipped",
@@ -8955,7 +9475,7 @@ function GuiMcpPreviewRows(props) {
8955
9475
  return props.model.preview().rows.some((row) => group(row.change) === section);
8956
9476
  },
8957
9477
  get children() {
8958
- var _el$ = _tmpl$$25(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
9478
+ var _el$ = _tmpl$$26(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
8959
9479
  (0, solid_js_web.insert)(_el$2, section);
8960
9480
  (0, solid_js_web.insert)(_el$3, (0, solid_js_web.createComponent)(solid_js.For, {
8961
9481
  get each() {
@@ -8964,7 +9484,7 @@ function GuiMcpPreviewRows(props) {
8964
9484
  children: (name) => {
8965
9485
  const row = () => props.model.preview().rows.find((item) => item.name === name);
8966
9486
  return (() => {
8967
- var _el$4 = _tmpl$2$18(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.firstChild;
9487
+ var _el$4 = _tmpl$2$19(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.firstChild;
8968
9488
  (0, solid_js_web.insert)(_el$5, name);
8969
9489
  (0, solid_js_web.insert)(_el$6, () => row().status ?? "Not available", _el$7);
8970
9490
  (0, solid_js_web.insert)(_el$6, () => row().change, null);
@@ -8982,8 +9502,8 @@ function GuiMcpPreviewRows(props) {
8982
9502
  });
8983
9503
  }
8984
9504
  // src/features/sidebar-settings/ui/gui/mcp-preset-preview.tsx
8985
- var _tmpl$$24 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>`);
8986
- var _tmpl$2$17 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
9505
+ var _tmpl$$25 = /*#__PURE__*/ (0, solid_js_web.template)(`<p>`);
9506
+ var _tmpl$2$18 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=alert>`);
8987
9507
  function GuiMcpPresetPreview(props) {
8988
9508
  const Dialog = props.ui.Dialog;
8989
9509
  const Button = props.ui.Button;
@@ -9044,7 +9564,7 @@ function GuiMcpPresetPreview(props) {
9044
9564
  get children() {
9045
9565
  return [
9046
9566
  (() => {
9047
- var _el$ = _tmpl$$24();
9567
+ var _el$ = _tmpl$$25();
9048
9568
  (0, solid_js_web.insert)(_el$, () => props.persistent ? "Desired states will be remembered for this scope, including unavailable servers." : "MCP persistence is off; desired states will not be saved.");
9049
9569
  return _el$;
9050
9570
  })(),
@@ -9053,7 +9573,7 @@ function GuiMcpPresetPreview(props) {
9053
9573
  return props.model.blocked() || error();
9054
9574
  },
9055
9575
  get children() {
9056
- var _el$2 = _tmpl$2$17();
9576
+ var _el$2 = _tmpl$2$18();
9057
9577
  (0, solid_js_web.insert)(_el$2, () => error() || props.model.blocked());
9058
9578
  return _el$2;
9059
9579
  }
@@ -9066,7 +9586,7 @@ function GuiMcpPresetPreview(props) {
9066
9586
  });
9067
9587
  }
9068
9588
  // src/features/sidebar-settings/ui/gui/settings-about.tsx
9069
- var _tmpl$$23 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>`);
9589
+ var _tmpl$$24 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>`);
9070
9590
  function GuiSettingsAbout(props) {
9071
9591
  const Section = props.ui.SettingsSection;
9072
9592
  const Row = props.ui.SettingsRow;
@@ -9076,11 +9596,15 @@ function GuiSettingsAbout(props) {
9076
9596
  const versions = () => props.about.changelog.filter((entry) => entry.changes.length > 0);
9077
9597
  const [version, setVersion] = (0, solid_js.createSignal)(versions()[0]?.version);
9078
9598
  const changes = () => versions().find((entry) => entry.version === version())?.changes.map((change) => `- ${change}`).join("\n") ?? "No user-facing release notes are bundled.";
9599
+ const progress = () => {
9600
+ const current = props.about.progress?.();
9601
+ return current && updateProgressView(current);
9602
+ };
9079
9603
  return [(0, solid_js_web.createComponent)(Section, {
9080
9604
  title: "About Navigator",
9081
9605
  get footer() {
9082
9606
  return (() => {
9083
- var _el$ = _tmpl$$23();
9607
+ var _el$ = _tmpl$$24();
9084
9608
  (0, solid_js_web.insert)(_el$, () => props.about.status());
9085
9609
  return _el$;
9086
9610
  })();
@@ -9089,7 +9613,17 @@ function GuiSettingsAbout(props) {
9089
9613
  return [
9090
9614
  (0, solid_js_web.createComponent)(Row, {
9091
9615
  get title() {
9092
- return `Navigator ${props.about.version}`;
9616
+ return (0, solid_js_web.memo)(() => !!progress())() ? (0, solid_js_web.createComponent)(GuiProgress, {
9617
+ get ui() {
9618
+ return props.ui;
9619
+ },
9620
+ get label() {
9621
+ return `Navigator · ${progress().label}`;
9622
+ },
9623
+ get value() {
9624
+ return progress().fraction;
9625
+ }
9626
+ }) : `Navigator ${props.about.version}`;
9093
9627
  },
9094
9628
  get description() {
9095
9629
  return `OpenCode Desktop ${props.about.hostVersion}. Experimental Desktop GUI.`;
@@ -9167,7 +9701,7 @@ function GuiSettingsAbout(props) {
9167
9701
  })];
9168
9702
  }
9169
9703
  // src/features/sidebar-settings/ui/gui/settings-behavior.tsx
9170
- var _tmpl$$22 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:8px;align-items:center>`);
9704
+ var _tmpl$$23 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:8px;align-items:center>`);
9171
9705
  function GuiSettingsBehavior(props) {
9172
9706
  const Section = props.ui.SettingsSection;
9173
9707
  const Row = props.ui.SettingsRow;
@@ -9251,7 +9785,7 @@ function GuiSettingsBehavior(props) {
9251
9785
  title: "Limits refresh",
9252
9786
  description: "Whole minutes, at least one. Manual refresh and model/account switches remain immediate.",
9253
9787
  get children() {
9254
- var _el$ = _tmpl$$22();
9788
+ var _el$ = _tmpl$$23();
9255
9789
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(TextInput, {
9256
9790
  label: "Limits refresh in minutes",
9257
9791
  get value() {
@@ -9334,6 +9868,7 @@ function GuiSettingsBehavior(props) {
9334
9868
  ];
9335
9869
  }
9336
9870
  // src/features/sidebar-settings/ui/gui/settings-catalogs.tsx
9871
+ var _tmpl$$22 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=display:flex;gap:8px;align-items:center>`);
9337
9872
  function GuiSettingsCatalogs(props) {
9338
9873
  const Section = props.ui.SettingsSection;
9339
9874
  const Row = props.ui.SettingsRow;
@@ -9346,42 +9881,40 @@ function GuiSettingsCatalogs(props) {
9346
9881
  title: "Quick Actions",
9347
9882
  footer: "Visibility and order are scoped. Favorites are private and user-wide; usage never reorders actions.",
9348
9883
  get children() {
9349
- return (0, solid_js_web.createComponent)(solid_js.For, {
9350
- get each() {
9884
+ return (0, solid_js_web.createComponent)(GuiSortableList, {
9885
+ get ui() {
9886
+ return props.ui;
9887
+ },
9888
+ label: "Quick Action order",
9889
+ get items() {
9351
9890
  return props.catalogs.actions();
9352
9891
  },
9353
- children: (action, index) => (0, solid_js_web.createComponent)(Row, {
9892
+ itemKey: (action) => action.id,
9893
+ itemLabel: (action) => action.label,
9894
+ get scope() {
9895
+ return props.preferences.selectedScopeKey();
9896
+ },
9897
+ move: (from, to) => props.preferences.reorderQuickAction(from, to, props.preferences.selectedScopeKey()),
9898
+ children: (action, handle) => (0, solid_js_web.createComponent)(Row, {
9354
9899
  get title() {
9355
- return action.label;
9900
+ return (() => {
9901
+ var _el$ = _tmpl$$22();
9902
+ (0, solid_js_web.insert)(_el$, handle, null);
9903
+ (0, solid_js_web.insert)(_el$, () => action().label, null);
9904
+ return _el$;
9905
+ })();
9356
9906
  },
9357
9907
  get children() {
9358
- return [
9359
- (0, solid_js_web.createComponent)(Button, {
9360
- size: "small",
9361
- get disabled() {
9362
- return index() === 0;
9363
- },
9364
- onClick: () => props.preferences.moveQuickAction(action.id, -1),
9365
- children: "Move up"
9366
- }),
9367
- (0, solid_js_web.createComponent)(Button, {
9368
- size: "small",
9369
- get disabled() {
9370
- return index() === props.catalogs.actions().length - 1;
9371
- },
9372
- onClick: () => props.preferences.moveQuickAction(action.id, 1),
9373
- children: "Move down"
9374
- }),
9375
- (0, solid_js_web.createComponent)(Switch, {
9376
- get label() {
9377
- return `Show action ${action.label}`;
9378
- },
9379
- get checked() {
9380
- return props.preferences.selectedQuickActionVisible(action.id);
9381
- },
9382
- onChange: () => props.preferences.toggleQuickAction(action.id)
9383
- })
9384
- ];
9908
+ return (0, solid_js_web.createComponent)(Switch, {
9909
+ get label() {
9910
+ return `Show action ${action().label}`;
9911
+ },
9912
+ hideLabel: true,
9913
+ get checked() {
9914
+ return props.preferences.selectedQuickActionVisible(action().id);
9915
+ },
9916
+ onChange: () => props.preferences.toggleQuickAction(action().id)
9917
+ });
9385
9918
  }
9386
9919
  })
9387
9920
  });
@@ -9546,7 +10079,7 @@ function GuiPresetControls(props) {
9546
10079
  }
9547
10080
  // src/features/sidebar-settings/ui/gui/settings-presets.tsx
9548
10081
  var _tmpl$$20 = /*#__PURE__*/ (0, solid_js_web.template)(`<div style=display:flex;gap:8px;flex-wrap:wrap>`);
9549
- var _tmpl$2$16 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>`);
10082
+ var _tmpl$2$17 = /*#__PURE__*/ (0, solid_js_web.template)(`<p role=status>`);
9550
10083
  function GuiSettingsPresets(props) {
9551
10084
  const Section = props.ui.SettingsSection;
9552
10085
  const Row = props.ui.SettingsRow;
@@ -9573,7 +10106,7 @@ function GuiSettingsPresets(props) {
9573
10106
  return message();
9574
10107
  },
9575
10108
  get children() {
9576
- var _el$2 = _tmpl$2$16();
10109
+ var _el$2 = _tmpl$2$17();
9577
10110
  (0, solid_js_web.insert)(_el$2, message);
9578
10111
  return _el$2;
9579
10112
  }
@@ -9762,7 +10295,8 @@ function GuiSettingsResets(props) {
9762
10295
  });
9763
10296
  }
9764
10297
  // src/features/sidebar-settings/ui/gui/section-settings-row.tsx
9765
- var _tmpl$$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=group style=display:flex;gap:8px;align-items:center;flex-wrap:wrap>`);
10298
+ var _tmpl$$19 = /*#__PURE__*/ (0, solid_js_web.template)(`<div role=group style=display:flex;gap:8px;align-items:center;flex-wrap:wrap><span style=display:flex;gap:6px;align-items:center><span>Visible</span></span><span style=display:flex;gap:6px;align-items:center><span>Expanded`);
10299
+ var _tmpl$2$16 = /*#__PURE__*/ (0, solid_js_web.template)(`<span style=display:flex;gap:8px;align-items:center>`);
9766
10300
  function GuiSectionSettingsRow(props) {
9767
10301
  const Row = props.ui.SettingsRow;
9768
10302
  const Switch = props.ui.Switch;
@@ -9774,7 +10308,12 @@ function GuiSectionSettingsRow(props) {
9774
10308
  (0, solid_js.createEffect)(() => setLimit(String(props.preferences.selectedSectionItemLimit(props.section))));
9775
10309
  return (0, solid_js_web.createComponent)(Row, {
9776
10310
  get title() {
9777
- return label();
10311
+ return (() => {
10312
+ var _el$6 = _tmpl$2$16();
10313
+ (0, solid_js_web.insert)(_el$6, () => props.handle, null);
10314
+ (0, solid_js_web.insert)(_el$6, label, null);
10315
+ return _el$6;
10316
+ })();
9778
10317
  },
9779
10318
  get id() {
9780
10319
  return `navigator-section-${props.section}`;
@@ -9783,24 +10322,12 @@ function GuiSectionSettingsRow(props) {
9783
10322
  return props.section === "lsp" ? "LSP is not exposed by this Desktop API. Its saved layout is retained for supported TUI hosts." : void 0;
9784
10323
  },
9785
10324
  get children() {
9786
- var _el$ = _tmpl$$19();
9787
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
9788
- size: "small",
9789
- get disabled() {
9790
- return props.index === 0;
9791
- },
9792
- onClick: () => props.preferences.moveSelectedSection(props.section, -1),
9793
- children: "Move up"
9794
- }), null);
9795
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Button, {
9796
- size: "small",
9797
- get disabled() {
9798
- return props.index === props.count - 1;
9799
- },
9800
- onClick: () => props.preferences.moveSelectedSection(props.section, 1),
9801
- children: "Move down"
9802
- }), null);
9803
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Switch, {
10325
+ var _el$ = _tmpl$$19(), _el$2 = _el$.firstChild;
10326
+ _el$2.firstChild;
10327
+ var _el$4 = _el$2.nextSibling;
10328
+ _el$4.firstChild;
10329
+ (0, solid_js_web.insert)(_el$2, (0, solid_js_web.createComponent)(Switch, {
10330
+ hideLabel: true,
9804
10331
  get label() {
9805
10332
  return `Show ${label()}`;
9806
10333
  },
@@ -9809,7 +10336,8 @@ function GuiSectionSettingsRow(props) {
9809
10336
  },
9810
10337
  onChange: () => props.preferences.toggleSelectedSection(props.section)
9811
10338
  }), null);
9812
- (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(Switch, {
10339
+ (0, solid_js_web.insert)(_el$4, (0, solid_js_web.createComponent)(Switch, {
10340
+ hideLabel: true,
9813
10341
  get label() {
9814
10342
  return `Expand ${label()}`;
9815
10343
  },
@@ -9857,18 +10385,25 @@ function GuiSettingsSections(props) {
9857
10385
  title: "Sections",
9858
10386
  footer: "Visibility changes apply immediately. Save this layout as the scoped default to use it in future sessions.",
9859
10387
  get children() {
9860
- return (0, solid_js_web.createComponent)(solid_js.For, {
9861
- get each() {
10388
+ return (0, solid_js_web.createComponent)(GuiSortableList, {
10389
+ get ui() {
10390
+ return props.ui;
10391
+ },
10392
+ label: "Section order",
10393
+ get items() {
9862
10394
  return preferences.selectedSectionOrder();
9863
10395
  },
9864
- children: (section, index) => (0, solid_js_web.createComponent)(GuiSectionSettingsRow, (0, solid_js_web.mergeProps)(props, {
9865
- section,
9866
- get index() {
9867
- return index();
10396
+ itemKey: (section) => section,
10397
+ itemLabel: (section) => SECTION_DEFINITIONS.find((value) => value.name === section)?.label ?? section,
10398
+ get scope() {
10399
+ return preferences.selectedScopeKey();
10400
+ },
10401
+ move: (from, to) => preferences.reorderSelectedSection(from, to, preferences.selectedScopeKey()),
10402
+ children: (section, handle) => (0, solid_js_web.createComponent)(GuiSectionSettingsRow, (0, solid_js_web.mergeProps)(props, {
10403
+ get section() {
10404
+ return section();
9868
10405
  },
9869
- get count() {
9870
- return preferences.selectedSectionOrder().length;
9871
- }
10406
+ handle
9872
10407
  }))
9873
10408
  });
9874
10409
  }
@@ -9902,103 +10437,105 @@ function GuiNavigatorSettings(props) {
9902
10437
  title: "Navigator",
9903
10438
  description: "Shared sidebar settings for this Desktop and your local TUI instances.",
9904
10439
  get children() {
9905
- return (0, solid_js_web.createComponent)(solid_js.Show, {
9906
- get when() {
9907
- return props.preferences.ready();
9908
- },
9909
- get fallback() {
9910
- return _tmpl$$18();
9911
- },
9912
- get children() {
9913
- return [
9914
- (0, solid_js_web.createComponent)(Section, {
9915
- title: "Preference scope",
9916
- get footer() {
9917
- return (0, solid_js_web.createComponent)(solid_js.Show, {
9918
- get when() {
9919
- return status();
9920
- },
9921
- get children() {
9922
- var _el$2 = _tmpl$2$15();
9923
- (0, solid_js_web.insert)(_el$2, status);
9924
- return _el$2;
9925
- }
9926
- });
9927
- },
9928
- get children() {
9929
- return [
9930
- (0, solid_js_web.createComponent)(Row, {
9931
- title: "Scope",
9932
- description: "Global applies everywhere. Worktree overrides use the currently selected server and location.",
9933
- get children() {
9934
- return (0, solid_js_web.createComponent)(Select, {
9935
- label: "Navigator preference scope",
9936
- get value() {
9937
- return props.preferences.preferenceScope();
9938
- },
9939
- get options() {
9940
- return [{
9941
- value: "global",
9942
- label: "Global"
9943
- }, ...props.preferences.canUseWorktreeScope() ? [{
9944
- value: "worktree",
9945
- label: "Worktree"
9946
- }] : []];
9947
- },
9948
- get onChange() {
9949
- return props.preferences.setPreferenceScope;
9950
- }
9951
- });
9952
- }
9953
- }),
9954
- (0, solid_js_web.createComponent)(Row, {
9955
- title: "Layout default",
9956
- description: "Changing section visibility or order does not implicitly replace the saved default.",
9957
- get children() {
9958
- return (0, solid_js_web.createComponent)(Button, {
9959
- get busy() {
9960
- return busy();
9961
- },
9962
- onClick: () => void run(props.preferences.saveLayoutAsDefault, "Layout default saved for this scope."),
9963
- children: "Save layout as default"
9964
- });
9965
- }
9966
- }),
9967
- (0, solid_js_web.createComponent)(Row, {
9968
- title: "Reload private preferences",
9969
- description: "Flush local writes, then load changes from other Navigator instances. Temporary layout overrides are cleared.",
10440
+ return (0, solid_js_web.createComponent)(GuiSettingsSurface, { get children() {
10441
+ return (0, solid_js_web.createComponent)(solid_js.Show, {
10442
+ get when() {
10443
+ return props.preferences.ready();
10444
+ },
10445
+ get fallback() {
10446
+ return _tmpl$$18();
10447
+ },
10448
+ get children() {
10449
+ return [
10450
+ (0, solid_js_web.createComponent)(Section, {
10451
+ title: "Preference scope",
10452
+ get footer() {
10453
+ return (0, solid_js_web.createComponent)(solid_js.Show, {
10454
+ get when() {
10455
+ return status();
10456
+ },
9970
10457
  get children() {
9971
- return (0, solid_js_web.createComponent)(Button, {
9972
- get busy() {
9973
- return busy();
9974
- },
9975
- onClick: () => void run(props.preferences.reloadFromFile, "Private preferences reloaded."),
9976
- children: "Reload preferences"
9977
- });
10458
+ var _el$2 = _tmpl$2$15();
10459
+ (0, solid_js_web.insert)(_el$2, status);
10460
+ return _el$2;
9978
10461
  }
9979
- })
9980
- ];
9981
- }
9982
- }),
9983
- (0, solid_js_web.createComponent)(GuiSettingsSections, props),
9984
- (0, solid_js_web.createComponent)(GuiSettingsBehavior, props),
9985
- (0, solid_js_web.createComponent)(GuiSettingsPresets, props),
9986
- (0, solid_js_web.createComponent)(GuiSettingsCatalogs, props),
9987
- (0, solid_js_web.createComponent)(GuiSettingsResets, props),
9988
- (0, solid_js_web.createComponent)(GuiSettingsAbout, {
9989
- get ui() {
9990
- return props.ui;
9991
- },
9992
- get about() {
9993
- return props.about;
9994
- },
9995
- get showGuide() {
9996
- return props.showGuide;
9997
- }
9998
- })
9999
- ];
10000
- }
10001
- });
10462
+ });
10463
+ },
10464
+ get children() {
10465
+ return [
10466
+ (0, solid_js_web.createComponent)(Row, {
10467
+ title: "Scope",
10468
+ description: "Global applies everywhere. Worktree overrides use the currently selected server and location.",
10469
+ get children() {
10470
+ return (0, solid_js_web.createComponent)(Select, {
10471
+ label: "Navigator preference scope",
10472
+ get value() {
10473
+ return props.preferences.preferenceScope();
10474
+ },
10475
+ get options() {
10476
+ return [{
10477
+ value: "global",
10478
+ label: "Global"
10479
+ }, ...props.preferences.canUseWorktreeScope() ? [{
10480
+ value: "worktree",
10481
+ label: "Worktree"
10482
+ }] : []];
10483
+ },
10484
+ get onChange() {
10485
+ return props.preferences.setPreferenceScope;
10486
+ }
10487
+ });
10488
+ }
10489
+ }),
10490
+ (0, solid_js_web.createComponent)(Row, {
10491
+ title: "Layout default",
10492
+ description: "Changing section visibility or order does not implicitly replace the saved default.",
10493
+ get children() {
10494
+ return (0, solid_js_web.createComponent)(Button, {
10495
+ get busy() {
10496
+ return busy();
10497
+ },
10498
+ onClick: () => void run(props.preferences.saveLayoutAsDefault, "Layout default saved for this scope."),
10499
+ children: "Save layout as default"
10500
+ });
10501
+ }
10502
+ }),
10503
+ (0, solid_js_web.createComponent)(Row, {
10504
+ title: "Reload private preferences",
10505
+ description: "Flush local writes, then load changes from other Navigator instances. Temporary layout overrides are cleared.",
10506
+ get children() {
10507
+ return (0, solid_js_web.createComponent)(Button, {
10508
+ get busy() {
10509
+ return busy();
10510
+ },
10511
+ onClick: () => void run(props.preferences.reloadFromFile, "Private preferences reloaded."),
10512
+ children: "Reload preferences"
10513
+ });
10514
+ }
10515
+ })
10516
+ ];
10517
+ }
10518
+ }),
10519
+ (0, solid_js_web.createComponent)(GuiSettingsSections, props),
10520
+ (0, solid_js_web.createComponent)(GuiSettingsBehavior, props),
10521
+ (0, solid_js_web.createComponent)(GuiSettingsPresets, props),
10522
+ (0, solid_js_web.createComponent)(GuiSettingsCatalogs, props),
10523
+ (0, solid_js_web.createComponent)(GuiSettingsResets, props),
10524
+ (0, solid_js_web.createComponent)(GuiSettingsAbout, {
10525
+ get ui() {
10526
+ return props.ui;
10527
+ },
10528
+ get about() {
10529
+ return props.about;
10530
+ },
10531
+ get showGuide() {
10532
+ return props.showGuide;
10533
+ }
10534
+ })
10535
+ ];
10536
+ }
10537
+ });
10538
+ } });
10002
10539
  }
10003
10540
  });
10004
10541
  }
@@ -10604,7 +11141,7 @@ function createGuiBugReport(context) {
10604
11141
  let token = "";
10605
11142
  (0, solid_js.onCleanup)(() => abort.abort());
10606
11143
  const model = createBugReportModel({
10607
- navigatorVersion: "0.27.1",
11144
+ navigatorVersion: "0.27.3",
10608
11145
  openCodeVersion: context.app.version,
10609
11146
  platform: context.app.platform,
10610
11147
  runtime: "OpenCode Desktop GUI"
@@ -10656,8 +11193,8 @@ function createGuiBugReport(context) {
10656
11193
  }
10657
11194
  // src/app/gui/release.ts
10658
11195
  var guiRelease = {
10659
- version: "0.27.1",
10660
- changelog: "# Changelog\n\n## [0.27.1] - 2026-11-11\n\n- Drag the Desktop mini-window by its header as well as its move handle, while keeping Dock a separate action. Match its surface and controls to OpenCode's light and dark themes.\n- Use native Desktop switches and selectors for Skill trust and Todo configuration, and align multiline fields with the host theme and focus styles.\n- Show a remaining-lifetime scale and time until expiry for banked reset credits with provider-supplied dates. Keep missing expiry explicit and prevent selecting expired credits for review.\n\n## [0.27.0] - 2026-11-11\n\n- Add an experimental OpenCode Desktop interface with a native panel, narrow-screen drawer, and opt-in in-app mini-window. Move, resize, reset and redock it with the mouse or keyboard while keeping the selected session and filters.\n- Bring Search Everything, Skills, MCP groups and presets, Quick Actions, and customizable toolbar pins to Desktop. Share private favorites, trust decisions and preferences with local TUI instances without duplicating monitoring.\n- Add native Desktop settings with scoped section controls, groups, reviewed presets and project profiles, portable import/export, a seven-slide setup guide, About, browsable release history and editable GitHub Bug reports.\n- Keep superseded Desktop configuration reads from showing a false error after a newer refresh, while preserving genuine failures and retry guidance.\n- Show verified provider/account quotas in Desktop with explicit account linking and separately confirmed banked resets. Keep remote sessions isolated from local accounts and preserve unresolved reset attempts across retries.\n- Support OpenCode 1.19.5 and 2.2.6 while preserving both TUI adapters, existing settings, pinned-source updates and read-only legacy Todo archives.\n\n## [0.26.0] - 2026-11-06\n\n- Prevent background retries from accumulating cancellation listeners and preferences loading from waiting indefinitely on recovery markers.\n- Show exact active Subagent durations from OpenCode 2's native execution start time, preserving the observation fallback when timing is unavailable. Keep interrupted executions marked as cancelled in recent history.\n\n## [0.25.0] - 2026-10-31\n\n- Offer Enable Todo when OpenCode 2's native tool is disabled, with an existing server-config picker, exact change preview and separate confirmation. Preserve unrelated JSONC content and refuse stale targets; distinguish disabled Todo from an enabled empty list.\n- Update the seven-slide setup wizard for native Todo enablement, provider/account limits, multi-icon or text-only toolbar pins, About/changelog and Bug reports. Describe each host's real permissions/LSP capabilities and keep navigation visible while short-terminal content scrolls.\n\n## [0.24.0] - 2026-10-31\n\n- Support the latest stable OpenCode 1.19.5 and 2.2.5 releases, preserving both host adapters and existing sidebar behavior.\n- Preserve Limits scales and reset-credit controls across equivalent quota snapshots and unrelated workspace events, while retaining current-account and credential-change guards.\n- Keep rounded selection backgrounds consistent in nested dialogs and highlighted rows.\n- Configure each toolbar button with zero to three icons and optional custom text, including text-only buttons. Keep legacy/state choices, unsaved drafts, rounded clipping, and width-aware More/wrapping.\n- Keep Quick Action rows and their hover/focus state stable while typing in the main prompt on both supported OpenCode hosts.\n- Show toolbar hover/focus hints without moving Limits or other sections, preserving centered, uniformly sized icon controls and empty-toolbar collapse.\n- Add Settings → About with the running versions, explicit update status/checking, confirmed updates, and a versioned changelog that can be reopened independently of first-launch upgrade notes.\n- Prepare editable Bug reports with confirmed sending through authenticated GitHub CLI and an independent Open in browser action. Retain drafts through errors and cancellation, and require manual review before retrying an uncertain submission.\n\n## [0.23.0] - 2026-10-28\n\n- Support only the latest stable OpenCode 1.x and 2.x releases, currently 1.19.5 and 2.2.0, including the newer terminal renderer.\n- Keep Skills, MCP servers, and Subagents current as OpenCode 2 updates, and launch the intended Skill when multiple sources have the same name.\n- Show OpenCode Console workspace budgets and Go allowance through the public host usage API without a Navigator server plugin. Preserve workspace scope, native units, reset times, and explicit failure/retry states.\n- Preserve Navigator shortcuts and nested-dialog navigation with OpenCode 2.2's keyboard handling.\n- Verify a supported runtime before changing a pinned Navigator snapshot, preserving the installed version when no compatible verifier is available.\n\n## [0.22.0] - 2026-10-23\n\n- Pin Quick Actions and Skills to a compact sidebar toolbar independently of bookmarks. Choose icons in a searchable picker, customize supported state icons, reorder buttons, and select a More menu or wrapping rows. Preserve Skill confirmations, unavailable reasons, keyboard navigation, and Text fallback; hide the toolbar entirely when empty.\n- Use OpenCode 2's built-in Todo history for the sidebar, with native completed-write and revert semantics, existing filters/progress, and manual Refresh. Keep edits in ordinary chat, preserve previous Navigator tasks as a read-only archive, and retain the independent provider-quota server integration.\n- Support OpenCode 2.1's plugin and rendering APIs while retaining the OpenCode 1.x adapter; restore custom dialogs and rounded selections on the newer native renderer.\n- Replace the shortcut toast with an icon-led hint featuring highlighted keys, navigation/update groups, a distinct Escape action, and responsive Text/no-corner-font layouts. Keep prompt focus and share its action list with scrollable keyboard help.\n- Update Subagent status immediately from OpenCode 2's execution lifecycle and keep unresolved external credentials separate from verified provider accounts.\n- Keep OpenCode updates on the current major line and preserve pinned-source Navigator updates when a target release needs newer runtime dependencies.\n\n## [0.21.8] - 2026-10-09\n\n- Make the unfilled part of Limits scales substantially quieter, matching the theme-derived sidebar-divider tone. Keep the filled segment, reset text, and remaining values readable in both icon modes and after theme changes.\n\n## [0.21.7] - 2026-10-09\n\n- Remove the vertical guide and its reserved gutter from Limits in both icon modes. Keep full-width quota scales, reset-left/remaining-right details, and one blank row between quota windows.\n\n## [0.21.6] - 2026-10-09\n\n- Refine Limits with a continuous muted guide, full-width thin-line quota scales, and reset-left/remaining-right details. Keep long reset text from overlapping the remaining value and preserve Text fallback, resize clipping, and existing controls.\n\n## [0.21.5] - 2026-10-07\n\n- Make automatic Limits refresh configurable in scoped Behavior settings and portable configuration, with a five-minute default and one-minute minimum. Keep manual Refresh and supported model/account switches immediate.\n- Avoid extra quota polling from unrelated preference changes and unchanged provider-account metadata observations.\n\n## [0.21.4] - 2026-10-07\n\n- Add `c` to Navigator shortcuts for a manual update check and `u` to check then confirm a Navigator update. Report available, current, or unavailable results, preserve the installation method and scope, and require a restart after updating.\n- Ignore cancelled, outdated, and repeated update confirmations, and recover from stalled update checks without blocking later checks.\n\n## [0.21.3] - 2026-10-06\n\n- Replace block quota meters with full-width adaptive thin-line scales and keep quota rows inside the sidebar's padding after resize. Show only reset time below each meter, removing technical account and window-duration suffixes.\n\n## [0.21.2] - 2026-10-06\n\n- Make Limits compact with clickable provider/model and variant controls, remaining-headroom progress rows, and muted reset times. Move Refresh and Banked Resets into the section header, remove the section counter and Provider sources action, and keep reset consumption behind a separate confirmation.\n\n## [0.21.1] - 2026-10-06\n\n- Fix Codex account quota windows being hidden when the provider omits model metadata. Show ordinary usage as shared account quota, retain banked-reset counts, and keep separate model buckets explicitly associated.\n\n## [0.21.0] - 2026-10-06\n\n- Automatically verify Codex account identity from OpenCode 2's ChatGPT connection. When host identity is unavailable, confirm a private binding once per connection or provider and reuse it across models, projects, and sessions; preserve compatible older confirmations and isolate conflicting ones until relinked.\n- Expand OpenCode 2 Limits with documented native subscription allowances, API rate-limit observations, configured capacities, account balances, and key budgets. Keep provider-native units, currencies, model associations, and active-account boundaries explicit.\n- Add Provider sources to explain each provider's supported measurements, required permissions, and unavailable APIs without guessing remaining quota.\n- Keep available account balances visible when a separate subscription read fails, and show exhausted NanoGPT subscription windows instead of hiding them.\n- Keep stale quota guidance visible after failed refreshes, reject late results after credential changes, and recover from stalled provider reads without blocking model response streams.\n\n## [0.20.0] - 2026-10-05\n\n- Add a default-visible Limits section for the selected model's provider-native quota windows, reset times, account identity, and freshness. Codex CLI support requires an explicit private account binding; unsupported providers show calm guidance.\n- Review Codex banked reset credits and use them only after a separate confirmation and fresh eligibility checks. Preserve the same attempt key when reconciling an uncertain result, including after a restart.\n- Save named, commit-safe project layout and MCP profiles to `.opencode/navigator.json` with the `opencode-navigator-profile` command, an explicit preview, and `--apply`; profile application remains manual.\n\n## [0.19.3] - 2026-10-03\n\n- Indent grouped Todo, Subagent, Skill, and MCP rows and align MCP server status indicators with their group headings.\n- Place Skill, MCP, and Quick Action bookmarks before item names. Reveal unselected bookmarks on hover or keyboard focus, keep favorites visible, and keep labels steady.\n\n## [0.19.2] - 2026-10-02\n\n- Show MCP group connection states with the same radio-style indicators as individual servers, without Connect/Disconnect labels on group headings.\n\n## [0.19.1] - 2026-10-02\n\n- Remember the OpenCode 1.x auto-approve mode as a user-wide default for new sessions, including changes made through the host's permission toggle.\n\n## [0.19.0] - 2026-10-02\n\n- Connect or disconnect all eligible MCP servers in a group from its heading, including filtered-out items, with keyboard access and failed-only retry.\n- Organize Skills into user-wide groups by source location, with Favorites first and group names searchable across the full catalog.\n- Reload saved Navigator settings from disk in another running OpenCode session without restarting it.\n\n## [0.18.2] - 2026-10-02\n\n- Open the host Settings dialog from the OpenCode 2.x Permissions Quick Action, where the user can change between `prompt` and `auto accept`; keep the direct auto-approve toggle on OpenCode 1.x.\n\n## [0.18.1] - 2026-10-01\n\n- Show calm Todo setup guidance in OpenCode 2.x when Navigator's server plugin is absent, without a request error or Retry control. Actual server failures still offer Retry.\n- Keep Quick Action names readable in narrow sidebars by placing unavailable reasons below them and showing shortcuts only when they fit.\n\n## [0.18.0] - 2026-10-01\n\n- Add durable per-session Todo lists to OpenCode 2.x through a separately configured Navigator server plugin and an agent Todo tool.\n- Update Todo in the sidebar as tasks change, and offer an off-by-default per-session switch for model-context Todo instructions.\n- Keep Todo visible with setup guidance when the OpenCode 2.x server plugin is missing.\n\n## [0.17.3] - 2026-09-30\n\n## [0.17.2] - 2026-09-30\n\n- Update a pinned local Navigator installation from its version label while keeping its file or directory source unchanged.\n- Hide the session title and creation date independently with Global/Worktree settings in OpenCode 1.x.\n\n## [0.17.1] - 2026-09-29\n\n- Fix the local Navigator installation changing unexpectedly when rebuilding the repository; it now stays on its pinned version.\n\n## [0.17.0] - 2026-09-29\n\n- Add an opt-in Behavior setting to start a new session directly in chat with the sidebar from Home or New session.\n- Remember the previously installed Navigator version and show the intervening release notes once after an update.\n\n## [0.16.1] - 2026-09-25\n\n- Make the entire OpenCode or Navigator version label open its own update confirmation when an update is available. The separator and the other version remain inactive.\n- Replace the small update arrow with a more prominent filled-circle Nerd Font indicator, retaining an ASCII fallback.\n\n## [0.16.0] - 2026-09-25\n\n- Show both OpenCode and Navigator versions in the OpenCode 2.x footer.\n- Confirm updates from the footer or command palette, preserve the original installation method and scope, and report the outcome with a restart reminder.\n\nEarlier release notes are available on [GitHub Releases](https://github.com/St1ggy/opencode-navigator/releases).\n\n[0.22.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.8...v0.22.0\n[0.21.8]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.7...v0.21.8\n[0.21.7]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.6...v0.21.7\n[0.21.6]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.5...v0.21.6\n[0.21.5]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.4...v0.21.5\n[0.21.4]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.3...v0.21.4\n[0.21.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.2...v0.21.3\n[0.21.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.1...v0.21.2\n[0.21.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.0...v0.21.1\n[0.21.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.20.0...v0.21.0\n[0.20.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.3...v0.20.0\n[0.19.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.2...v0.19.3\n[0.19.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.1...v0.19.2\n[0.19.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.0...v0.19.1\n[0.19.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.2...v0.19.0\n[0.18.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.1...v0.18.2\n[0.18.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.0...v0.18.1\n[0.18.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.3...v0.18.0\n[0.17.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.2...v0.17.3\n[0.17.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.1...v0.17.2\n[0.17.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.0...v0.17.1\n[0.17.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.16.1...v0.17.0\n[0.16.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.16.0...v0.16.1\n[0.16.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.15.0...v0.16.0\n"
11196
+ version: "0.27.3",
11197
+ changelog: "# Changelog\n\n## [0.27.3] - 2026-11-12\n\n- Keep the Desktop session title on its own full-width row in both the panel and floating mini-window, with readable wrapping instead of squeezing it between action labels.\n- Use native icon buttons with tooltips for familiar sidebar actions such as Search, Refresh, Settings, Float, Dock and information. Keep descriptive labels for account linking, Banked Resets, presets and other less obvious operations.\n- Remove the persistent account/model scope explanation from successful Desktop Limits. Keep the short update time, quota data and actionable warnings without changing account scope or reset-credit safeguards.\n\n## [0.27.2] - 2026-11-11\n\n- Keep Desktop Settings fields and actions inside their rows at narrow widths, with readable labels and compact group creation.\n- Reorder Desktop sections, Quick Actions, toolbar pins and button icons by dragging their handles, or use Space and arrow keys to move them with the keyboard. Escape cancels an unfinished move.\n- Show download and installation progress in place of the running version in TUI and inline in Desktop About. Show real percentages only when byte totals are available; otherwise show the active stage.\n- Bring Desktop section arrows, loading indicators and Todo rows closer to native OpenCode. Completed and cancelled tasks use muted, struck-through text with distinct status icons.\n- Remove duplicate model and reasoning-effort selectors from the Desktop sidebar. Select them below chat instead; Limits continue to follow the active model.\n\n## [0.27.1] - 2026-11-11\n\n- Drag the Desktop mini-window by its header as well as its move handle, while keeping Dock a separate action. Match its surface and controls to OpenCode's light and dark themes.\n- Use native Desktop switches and selectors for Skill trust and Todo configuration, and align multiline fields with the host theme and focus styles.\n- Show a remaining-lifetime scale and time until expiry for banked reset credits with provider-supplied dates. Keep missing expiry explicit and prevent selecting expired credits for review.\n\n## [0.27.0] - 2026-11-11\n\n- Add an experimental OpenCode Desktop interface with a native panel, narrow-screen drawer, and opt-in in-app mini-window. Move, resize, reset and redock it with the mouse or keyboard while keeping the selected session and filters.\n- Bring Search Everything, Skills, MCP groups and presets, Quick Actions, and customizable toolbar pins to Desktop. Share private favorites, trust decisions and preferences with local TUI instances without duplicating monitoring.\n- Add native Desktop settings with scoped section controls, groups, reviewed presets and project profiles, portable import/export, a seven-slide setup guide, About, browsable release history and editable GitHub Bug reports.\n- Keep superseded Desktop configuration reads from showing a false error after a newer refresh, while preserving genuine failures and retry guidance.\n- Show verified provider/account quotas in Desktop with explicit account linking and separately confirmed banked resets. Keep remote sessions isolated from local accounts and preserve unresolved reset attempts across retries.\n- Support OpenCode 1.19.5 and 2.2.6 while preserving both TUI adapters, existing settings, pinned-source updates and read-only legacy Todo archives.\n\n## [0.26.0] - 2026-11-06\n\n- Prevent background retries from accumulating cancellation listeners and preferences loading from waiting indefinitely on recovery markers.\n- Show exact active Subagent durations from OpenCode 2's native execution start time, preserving the observation fallback when timing is unavailable. Keep interrupted executions marked as cancelled in recent history.\n\n## [0.25.0] - 2026-10-31\n\n- Offer Enable Todo when OpenCode 2's native tool is disabled, with an existing server-config picker, exact change preview and separate confirmation. Preserve unrelated JSONC content and refuse stale targets; distinguish disabled Todo from an enabled empty list.\n- Update the seven-slide setup wizard for native Todo enablement, provider/account limits, multi-icon or text-only toolbar pins, About/changelog and Bug reports. Describe each host's real permissions/LSP capabilities and keep navigation visible while short-terminal content scrolls.\n\n## [0.24.0] - 2026-10-31\n\n- Support the latest stable OpenCode 1.19.5 and 2.2.5 releases, preserving both host adapters and existing sidebar behavior.\n- Preserve Limits scales and reset-credit controls across equivalent quota snapshots and unrelated workspace events, while retaining current-account and credential-change guards.\n- Keep rounded selection backgrounds consistent in nested dialogs and highlighted rows.\n- Configure each toolbar button with zero to three icons and optional custom text, including text-only buttons. Keep legacy/state choices, unsaved drafts, rounded clipping, and width-aware More/wrapping.\n- Keep Quick Action rows and their hover/focus state stable while typing in the main prompt on both supported OpenCode hosts.\n- Show toolbar hover/focus hints without moving Limits or other sections, preserving centered, uniformly sized icon controls and empty-toolbar collapse.\n- Add Settings → About with the running versions, explicit update status/checking, confirmed updates, and a versioned changelog that can be reopened independently of first-launch upgrade notes.\n- Prepare editable Bug reports with confirmed sending through authenticated GitHub CLI and an independent Open in browser action. Retain drafts through errors and cancellation, and require manual review before retrying an uncertain submission.\n\n## [0.23.0] - 2026-10-28\n\n- Support only the latest stable OpenCode 1.x and 2.x releases, currently 1.19.5 and 2.2.0, including the newer terminal renderer.\n- Keep Skills, MCP servers, and Subagents current as OpenCode 2 updates, and launch the intended Skill when multiple sources have the same name.\n- Show OpenCode Console workspace budgets and Go allowance through the public host usage API without a Navigator server plugin. Preserve workspace scope, native units, reset times, and explicit failure/retry states.\n- Preserve Navigator shortcuts and nested-dialog navigation with OpenCode 2.2's keyboard handling.\n- Verify a supported runtime before changing a pinned Navigator snapshot, preserving the installed version when no compatible verifier is available.\n\n## [0.22.0] - 2026-10-23\n\n- Pin Quick Actions and Skills to a compact sidebar toolbar independently of bookmarks. Choose icons in a searchable picker, customize supported state icons, reorder buttons, and select a More menu or wrapping rows. Preserve Skill confirmations, unavailable reasons, keyboard navigation, and Text fallback; hide the toolbar entirely when empty.\n- Use OpenCode 2's built-in Todo history for the sidebar, with native completed-write and revert semantics, existing filters/progress, and manual Refresh. Keep edits in ordinary chat, preserve previous Navigator tasks as a read-only archive, and retain the independent provider-quota server integration.\n- Support OpenCode 2.1's plugin and rendering APIs while retaining the OpenCode 1.x adapter; restore custom dialogs and rounded selections on the newer native renderer.\n- Replace the shortcut toast with an icon-led hint featuring highlighted keys, navigation/update groups, a distinct Escape action, and responsive Text/no-corner-font layouts. Keep prompt focus and share its action list with scrollable keyboard help.\n- Update Subagent status immediately from OpenCode 2's execution lifecycle and keep unresolved external credentials separate from verified provider accounts.\n- Keep OpenCode updates on the current major line and preserve pinned-source Navigator updates when a target release needs newer runtime dependencies.\n\n## [0.21.8] - 2026-10-09\n\n- Make the unfilled part of Limits scales substantially quieter, matching the theme-derived sidebar-divider tone. Keep the filled segment, reset text, and remaining values readable in both icon modes and after theme changes.\n\n## [0.21.7] - 2026-10-09\n\n- Remove the vertical guide and its reserved gutter from Limits in both icon modes. Keep full-width quota scales, reset-left/remaining-right details, and one blank row between quota windows.\n\n## [0.21.6] - 2026-10-09\n\n- Refine Limits with a continuous muted guide, full-width thin-line quota scales, and reset-left/remaining-right details. Keep long reset text from overlapping the remaining value and preserve Text fallback, resize clipping, and existing controls.\n\n## [0.21.5] - 2026-10-07\n\n- Make automatic Limits refresh configurable in scoped Behavior settings and portable configuration, with a five-minute default and one-minute minimum. Keep manual Refresh and supported model/account switches immediate.\n- Avoid extra quota polling from unrelated preference changes and unchanged provider-account metadata observations.\n\n## [0.21.4] - 2026-10-07\n\n- Add `c` to Navigator shortcuts for a manual update check and `u` to check then confirm a Navigator update. Report available, current, or unavailable results, preserve the installation method and scope, and require a restart after updating.\n- Ignore cancelled, outdated, and repeated update confirmations, and recover from stalled update checks without blocking later checks.\n\n## [0.21.3] - 2026-10-06\n\n- Replace block quota meters with full-width adaptive thin-line scales and keep quota rows inside the sidebar's padding after resize. Show only reset time below each meter, removing technical account and window-duration suffixes.\n\n## [0.21.2] - 2026-10-06\n\n- Make Limits compact with clickable provider/model and variant controls, remaining-headroom progress rows, and muted reset times. Move Refresh and Banked Resets into the section header, remove the section counter and Provider sources action, and keep reset consumption behind a separate confirmation.\n\n## [0.21.1] - 2026-10-06\n\n- Fix Codex account quota windows being hidden when the provider omits model metadata. Show ordinary usage as shared account quota, retain banked-reset counts, and keep separate model buckets explicitly associated.\n\n## [0.21.0] - 2026-10-06\n\n- Automatically verify Codex account identity from OpenCode 2's ChatGPT connection. When host identity is unavailable, confirm a private binding once per connection or provider and reuse it across models, projects, and sessions; preserve compatible older confirmations and isolate conflicting ones until relinked.\n- Expand OpenCode 2 Limits with documented native subscription allowances, API rate-limit observations, configured capacities, account balances, and key budgets. Keep provider-native units, currencies, model associations, and active-account boundaries explicit.\n- Add Provider sources to explain each provider's supported measurements, required permissions, and unavailable APIs without guessing remaining quota.\n- Keep available account balances visible when a separate subscription read fails, and show exhausted NanoGPT subscription windows instead of hiding them.\n- Keep stale quota guidance visible after failed refreshes, reject late results after credential changes, and recover from stalled provider reads without blocking model response streams.\n\n## [0.20.0] - 2026-10-05\n\n- Add a default-visible Limits section for the selected model's provider-native quota windows, reset times, account identity, and freshness. Codex CLI support requires an explicit private account binding; unsupported providers show calm guidance.\n- Review Codex banked reset credits and use them only after a separate confirmation and fresh eligibility checks. Preserve the same attempt key when reconciling an uncertain result, including after a restart.\n- Save named, commit-safe project layout and MCP profiles to `.opencode/navigator.json` with the `opencode-navigator-profile` command, an explicit preview, and `--apply`; profile application remains manual.\n\n## [0.19.3] - 2026-10-03\n\n- Indent grouped Todo, Subagent, Skill, and MCP rows and align MCP server status indicators with their group headings.\n- Place Skill, MCP, and Quick Action bookmarks before item names. Reveal unselected bookmarks on hover or keyboard focus, keep favorites visible, and keep labels steady.\n\n## [0.19.2] - 2026-10-02\n\n- Show MCP group connection states with the same radio-style indicators as individual servers, without Connect/Disconnect labels on group headings.\n\n## [0.19.1] - 2026-10-02\n\n- Remember the OpenCode 1.x auto-approve mode as a user-wide default for new sessions, including changes made through the host's permission toggle.\n\n## [0.19.0] - 2026-10-02\n\n- Connect or disconnect all eligible MCP servers in a group from its heading, including filtered-out items, with keyboard access and failed-only retry.\n- Organize Skills into user-wide groups by source location, with Favorites first and group names searchable across the full catalog.\n- Reload saved Navigator settings from disk in another running OpenCode session without restarting it.\n\n## [0.18.2] - 2026-10-02\n\n- Open the host Settings dialog from the OpenCode 2.x Permissions Quick Action, where the user can change between `prompt` and `auto accept`; keep the direct auto-approve toggle on OpenCode 1.x.\n\n## [0.18.1] - 2026-10-01\n\n- Show calm Todo setup guidance in OpenCode 2.x when Navigator's server plugin is absent, without a request error or Retry control. Actual server failures still offer Retry.\n- Keep Quick Action names readable in narrow sidebars by placing unavailable reasons below them and showing shortcuts only when they fit.\n\n## [0.18.0] - 2026-10-01\n\n- Add durable per-session Todo lists to OpenCode 2.x through a separately configured Navigator server plugin and an agent Todo tool.\n- Update Todo in the sidebar as tasks change, and offer an off-by-default per-session switch for model-context Todo instructions.\n- Keep Todo visible with setup guidance when the OpenCode 2.x server plugin is missing.\n\n## [0.17.3] - 2026-09-30\n\n## [0.17.2] - 2026-09-30\n\n- Update a pinned local Navigator installation from its version label while keeping its file or directory source unchanged.\n- Hide the session title and creation date independently with Global/Worktree settings in OpenCode 1.x.\n\n## [0.17.1] - 2026-09-29\n\n- Fix the local Navigator installation changing unexpectedly when rebuilding the repository; it now stays on its pinned version.\n\n## [0.17.0] - 2026-09-29\n\n- Add an opt-in Behavior setting to start a new session directly in chat with the sidebar from Home or New session.\n- Remember the previously installed Navigator version and show the intervening release notes once after an update.\n\n## [0.16.1] - 2026-09-25\n\n- Make the entire OpenCode or Navigator version label open its own update confirmation when an update is available. The separator and the other version remain inactive.\n- Replace the small update arrow with a more prominent filled-circle Nerd Font indicator, retaining an ASCII fallback.\n\n## [0.16.0] - 2026-09-25\n\n- Show both OpenCode and Navigator versions in the OpenCode 2.x footer.\n- Confirm updates from the footer or command palette, preserve the original installation method and scope, and report the outcome with a restart reminder.\n\nEarlier release notes are available on [GitHub Releases](https://github.com/St1ggy/opencode-navigator/releases).\n\n[0.22.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.8...v0.22.0\n[0.21.8]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.7...v0.21.8\n[0.21.7]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.6...v0.21.7\n[0.21.6]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.5...v0.21.6\n[0.21.5]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.4...v0.21.5\n[0.21.4]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.3...v0.21.4\n[0.21.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.2...v0.21.3\n[0.21.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.1...v0.21.2\n[0.21.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.21.0...v0.21.1\n[0.21.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.20.0...v0.21.0\n[0.20.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.3...v0.20.0\n[0.19.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.2...v0.19.3\n[0.19.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.1...v0.19.2\n[0.19.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.19.0...v0.19.1\n[0.19.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.2...v0.19.0\n[0.18.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.1...v0.18.2\n[0.18.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.18.0...v0.18.1\n[0.18.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.3...v0.18.0\n[0.17.3]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.2...v0.17.3\n[0.17.2]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.1...v0.17.2\n[0.17.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.17.0...v0.17.1\n[0.17.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.16.1...v0.17.0\n[0.16.1]: https://github.com/St1ggy/opencode-navigator/compare/v0.16.0...v0.16.1\n[0.16.0]: https://github.com/St1ggy/opencode-navigator/compare/v0.15.0...v0.16.0\n"
10661
11198
  };
10662
11199
  // src/app/gui/about.ts
10663
11200
  function createGuiAbout(context) {
@@ -10665,13 +11202,18 @@ function createGuiAbout(context) {
10665
11202
  const [busy, setBusy] = (0, solid_js.createSignal)(false);
10666
11203
  const [status, setStatus] = (0, solid_js.createSignal)("Updates have not been checked.");
10667
11204
  const [token, setToken] = (0, solid_js.createSignal)();
11205
+ const progress = () => {
11206
+ const current = ipc.state();
11207
+ return current && current.phase !== "idle" ? { phase: current.phase } : void 0;
11208
+ };
10668
11209
  return {
10669
11210
  report: createGuiBugReport(context),
10670
11211
  version: guiRelease.version,
10671
11212
  hostVersion: context.app?.version ?? "unknown",
10672
11213
  changelog: parseChangelog(guiRelease.changelog),
10673
11214
  status,
10674
- busy,
11215
+ busy: () => busy() || Boolean(progress()),
11216
+ progress,
10675
11217
  available: () => Boolean(token()),
10676
11218
  async check() {
10677
11219
  if (busy()) return;
@@ -14063,10 +14605,11 @@ function createGuiPresetDialogs(context, preferences, registry, mcp) {
14063
14605
  };
14064
14606
  }
14065
14607
  // src/features/search-everything/ui/gui/result-row.tsx
14066
- var _tmpl$$7 = /*#__PURE__*/ (0, solid_js_web.template)(`<span role=status>Working…`);
14608
+ var _tmpl$$7 = /*#__PURE__*/ (0, solid_js_web.template)(`<span role=status style=display:inline-flex;gap:6px;align-items:center>Working…`);
14067
14609
  var _tmpl$2$6 = /*#__PURE__*/ (0, solid_js_web.template)(`<li style="display:flex;gap:4px;align-items:center;min-width:0;padding:3px 0"><button type=button style=flex:1;min-width:0;text-align:left;padding:8px;color:inherit;border:0;border-radius:6px><strong style=display:block;overflow-wrap:anywhere></strong><span style=display:block;opacity:0.65;font-size:12px;overflow-wrap:anywhere>`);
14068
14610
  function GuiSearchResultRow(props) {
14069
14611
  const Button = props.ui.Button;
14612
+ const Loader = props.ui.Loader;
14070
14613
  return (() => {
14071
14614
  var _el$ = _tmpl$2$6(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling;
14072
14615
  _el$2.$$click = (event) => {
@@ -14081,7 +14624,9 @@ function GuiSearchResultRow(props) {
14081
14624
  return props.busy;
14082
14625
  },
14083
14626
  get children() {
14084
- return _tmpl$$7();
14627
+ var _el$5 = _tmpl$$7(), _el$6 = _el$5.firstChild;
14628
+ (0, solid_js_web.insert)(_el$5, (0, solid_js_web.createComponent)(Loader, { size: "small" }), _el$6);
14629
+ return _el$5;
14085
14630
  }
14086
14631
  }), null);
14087
14632
  (0, solid_js_web.insert)(_el$, (0, solid_js_web.createComponent)(solid_js.Show, {
@@ -15287,12 +15832,12 @@ function createGuiToolbarDialogs(context, registry, preferences) {
15287
15832
  if (!current()) throw new DOMException("Toolbar target changed", "AbortError");
15288
15833
  await save(update);
15289
15834
  }
15290
- async function move(key, direction) {
15835
+ async function move(key, target) {
15291
15836
  const pins = preferences.toolbarPins();
15292
15837
  const index = pins.findIndex((pin) => toolbarPinKey(pin) === key);
15293
- const destination = index + direction;
15294
- if (index === -1 || destination < 0 || destination >= pins.length) return;
15295
- const before = pins[destination + (direction === 1 ? 1 : 0)];
15838
+ const destination = pins.findIndex((pin) => toolbarPinKey(pin) === target);
15839
+ if (index === -1 || destination === -1 || destination >= pins.length) return;
15840
+ const before = pins[destination + (destination > index ? 1 : 0)];
15296
15841
  await change({
15297
15842
  operation: "move",
15298
15843
  key,
@@ -15633,7 +16178,7 @@ var setup = (context) => {
15633
16178
  try {
15634
16179
  await preferences.flush();
15635
16180
  context.toast.show({
15636
- title: `Navigator 0.27.1`,
16181
+ title: `Navigator 0.27.3`,
15637
16182
  variant: "default"
15638
16183
  });
15639
16184
  } catch {