@termix-ssh/plugin-sdk 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/PLUGIN-GUIDE.md +66 -4
  3. package/cli/commands/build.mjs +55 -36
  4. package/cli/commands/validate.mjs +4 -0
  5. package/dist/backend.d.ts +32 -7
  6. package/dist/frontend.d.ts +7 -0
  7. package/dist/host/{PluginsTab-ZSMTJ5C5.js → PluginsTab-CM2B33BD.js} +26 -16
  8. package/dist/host/chunk-7J4QNGFM.js +345 -0
  9. package/dist/host/{chunk-J3ZQPBDK.js → chunk-HQ6NNFSX.js} +115 -1575
  10. package/dist/host/chunk-I5F2PSMR.js +1526 -0
  11. package/dist/host/{chunk-2MU6WNSX.js → chunk-JRV2HIDK.js} +1 -1
  12. package/dist/host/{chunk-7LEJ3HYF.js → chunk-P26QKKU5.js} +41 -10
  13. package/dist/host/{chunk-CITUENSU.js → chunk-ROB2SH5Q.js} +77 -49
  14. package/dist/host/chunk-TK3Y437H.js +53 -0
  15. package/dist/host/{chunk-EYONBBRJ.js → chunk-VKNGNEF3.js} +34650 -34968
  16. package/dist/host/core-classes.json +1 -1
  17. package/dist/host/frame-host.css +2 -0
  18. package/dist/host/frame-host.js +242 -0
  19. package/dist/host/{linked-server-OUF3POT7.js → linked-server-J5G457OA.js} +1 -1
  20. package/dist/host/{rbac-api-LZVENREG.js → rbac-api-PY22W5UY.js} +5 -1
  21. package/dist/host/testing-host.js +9 -6
  22. package/dist/host/types/src/types/host-sidebar-preferences.d.ts +2 -0
  23. package/dist/host/types/src/types/index.d.ts +8 -0
  24. package/dist/host/types/src/ui/api/look-api.d.ts +2 -0
  25. package/dist/host/types/src/ui/api/plugins-api.d.ts +3 -2
  26. package/dist/host/types/src/ui/api/rbac-api.d.ts +19 -1
  27. package/dist/host/types/src/ui/components/data-view.d.ts +1 -1
  28. package/dist/host/types/src/ui/lib/app-return.d.ts +13 -0
  29. package/dist/host/types/src/ui/main-axios.d.ts +1 -1
  30. package/dist/host/types/src/ui/plugin-host/loader.d.ts +6 -0
  31. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -1
  32. package/dist/host/types/src/ui/plugins/plugin-model.d.ts +1 -1
  33. package/dist/host/types/src/ui/plugins/use-plugins-manager.d.ts +1 -1
  34. package/dist/host/types/src/ui/shell/tab-registry.d.ts +2 -0
  35. package/dist/host/ui.js +418 -388
  36. package/dist/host/{use-permissions-BYCDS5PD.js → use-permissions-EWRI3WLT.js} +1 -1
  37. package/dist/host-commands.d.ts +9 -6
  38. package/dist/host-commands.js +38 -11
  39. package/dist/host-scripts/docker.d.ts +137 -0
  40. package/dist/host-scripts/docker.js +345 -0
  41. package/dist/host-scripts/index.d.ts +15 -0
  42. package/dist/host-scripts/index.js +15 -0
  43. package/dist/host-scripts/journal.d.ts +45 -0
  44. package/dist/host-scripts/journal.js +194 -0
  45. package/dist/host-scripts/metrics.d.ts +134 -0
  46. package/dist/host-scripts/metrics.js +320 -0
  47. package/dist/host-scripts/openrc.d.ts +6 -0
  48. package/dist/host-scripts/openrc.js +90 -0
  49. package/dist/host-scripts/shell.d.ts +43 -0
  50. package/dist/host-scripts/shell.js +95 -0
  51. package/dist/host-scripts/systemd.d.ts +73 -0
  52. package/dist/host-scripts/systemd.js +320 -0
  53. package/dist/host-scripts/text.d.ts +23 -0
  54. package/dist/host-scripts/text.js +111 -0
  55. package/dist/manifest.d.ts +28 -1
  56. package/dist/manifest.js +108 -1
  57. package/package.json +9 -1
  58. package/schema/manifest.schema.json +42 -0
@@ -3,65 +3,33 @@ import {
3
3
  hasPermission,
4
4
  resetPermissionsCache,
5
5
  usePermissions
6
- } from "./chunk-2MU6WNSX.js";
6
+ } from "./chunk-JRV2HIDK.js";
7
7
  import {
8
- ArrowDownToLine,
9
- ArrowLeftToLine,
10
- ArrowRightToLine,
11
- Button,
12
- Check,
13
- Checkbox,
14
- CheckboxIndicator,
15
- CircleSlash,
16
8
  Columns2,
17
- DefaultsOnly,
18
- DocsLink,
19
- Eye,
20
- EyeOff,
21
- FakeSwitch,
22
- HostDefaultBadge,
23
- Input,
24
9
  KeyRound,
25
- LayoutPanelLeft,
26
10
  LibraryBig,
27
11
  Maximize2,
28
12
  OPEN_PLUGINS_EVENT,
29
13
  OPEN_PLUGIN_SETTINGS_EVENT,
30
- PLUGINS_CHANGED_EVENT,
31
14
  PLUGIN_SETTINGS_CHANGED_EVENT,
32
- PanelRight,
33
15
  Plug,
34
- PluginIcon,
35
16
  Puzzle,
36
17
  RotateCcw,
37
18
  Rows2,
38
- SectionCard,
39
- Select,
40
- SelectContent,
41
- SelectItem,
42
- SelectTrigger,
43
- SelectValue,
44
19
  Server,
45
- SettingRow,
46
20
  Settings,
47
21
  Terminal,
48
22
  X,
49
23
  Zap,
50
- cn,
51
24
  getPluginAdminSettings,
52
25
  getPluginHostSettings,
53
26
  getPluginUserSettings,
54
- getPlugins,
55
27
  guardCallback,
56
- hasVisibleFields,
57
- isFieldShown,
58
- isRedactedSecret,
59
28
  notifyShellReady,
60
29
  notifyTabsChanged,
61
30
  pluginDocsPage,
62
31
  pluginDocsUrl,
63
32
  pluginKey,
64
- pluginName,
65
33
  scopeHostArgs,
66
34
  scopeHostCallback,
67
35
  scopeHostFields,
@@ -83,7 +51,7 @@ import {
83
51
  withIconBoundary,
84
52
  withPluginContext,
85
53
  withPluginScope
86
- } from "./chunk-EYONBBRJ.js";
54
+ } from "./chunk-VKNGNEF3.js";
87
55
  import {
88
56
  SYNC_CHANGED_EVENT,
89
57
  TRUST_TOKEN_KEY,
@@ -128,95 +96,14 @@ import {
128
96
  saveUserPreferences,
129
97
  setCookie,
130
98
  tabTypeForActivity,
131
- unregisteredViewStatus,
132
99
  usePluginStore,
133
100
  useTabTypes,
134
101
  websocketAuthProtocols
135
- } from "./chunk-7LEJ3HYF.js";
102
+ } from "./chunk-P26QKKU5.js";
136
103
  import {
137
- define_import_meta_env_default,
138
104
  init_define_import_meta_env
139
105
  } from "./chunk-YGYBM7SQ.js";
140
106
 
141
- // src/ui/components/checkbox.tsx
142
- init_define_import_meta_env();
143
- import { jsx } from "react/jsx-runtime";
144
- function Checkbox2({
145
- className,
146
- ...props
147
- }) {
148
- return /* @__PURE__ */ jsx(
149
- Checkbox,
150
- {
151
- "data-slot": "checkbox",
152
- className: cn(
153
- "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-none border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
154
- className
155
- ),
156
- ...props,
157
- children: /* @__PURE__ */ jsx(
158
- CheckboxIndicator,
159
- {
160
- "data-slot": "checkbox-indicator",
161
- className: "flex items-center justify-center text-current transition-none",
162
- children: /* @__PURE__ */ jsx(Check, { className: "size-3.5" })
163
- }
164
- )
165
- }
166
- );
167
- }
168
-
169
- // src/ui/components/password-input.tsx
170
- init_define_import_meta_env();
171
- import * as React from "react";
172
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
173
- var PasswordInput = React.forwardRef(({ className, ...props }, ref) => {
174
- const [showPassword, setShowPassword] = React.useState(false);
175
- return /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
176
- /* @__PURE__ */ jsx2(
177
- Input,
178
- {
179
- ref,
180
- type: showPassword ? "text" : "password",
181
- className: cn("h-11 text-base pr-12", className),
182
- ...props
183
- }
184
- ),
185
- /* @__PURE__ */ jsx2(
186
- "button",
187
- {
188
- type: "button",
189
- onClick: () => setShowPassword((prev) => !prev),
190
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition",
191
- "aria-label": showPassword ? "Hide password" : "Show password",
192
- children: showPassword ? /* @__PURE__ */ jsx2(EyeOff, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx2(Eye, { className: "h-5 w-5" })
193
- }
194
- )
195
- ] });
196
- });
197
- PasswordInput.displayName = "PasswordInput";
198
-
199
- // src/ui/components/textarea.tsx
200
- init_define_import_meta_env();
201
- import * as React2 from "react";
202
- import { jsx as jsx3 } from "react/jsx-runtime";
203
- var Textarea = React2.forwardRef(
204
- ({ className, ...props }, ref) => {
205
- return /* @__PURE__ */ jsx3(
206
- "textarea",
207
- {
208
- className: cn(
209
- "flex min-h-[80px] w-full rounded-none border border-input bg-transparent dark:bg-input/30 px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] duration-200 outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 disabled:pointer-events-none",
210
- className
211
- ),
212
- ref,
213
- ...props
214
- }
215
- );
216
- }
217
- );
218
- Textarea.displayName = "Textarea";
219
-
220
107
  // src/ui/lib/rem.ts
221
108
  init_define_import_meta_env();
222
109
  var ROOT_PX = 14;
@@ -232,13 +119,13 @@ function remScale() {
232
119
 
233
120
  // src/ui/hooks/use-mobile.ts
234
121
  init_define_import_meta_env();
235
- import * as React3 from "react";
122
+ import * as React from "react";
236
123
  var MOBILE_BREAKPOINT = 768;
237
124
  function useIsMobile() {
238
- const [isMobile, setIsMobile] = React3.useState(
125
+ const [isMobile, setIsMobile] = React.useState(
239
126
  void 0
240
127
  );
241
- React3.useEffect(() => {
128
+ React.useEffect(() => {
242
129
  if (isElectron()) {
243
130
  setIsMobile(false);
244
131
  return;
@@ -1144,6 +1031,10 @@ var en_default = {
1144
1031
  ready: "Termix {{version}} is ready to install",
1145
1032
  readyHint: "It installs the next time Termix restarts."
1146
1033
  },
1034
+ safeMode: {
1035
+ title: "Safe mode",
1036
+ description: "Only layout and theme plugins are running. Restart the server without TERMIX_SAFE_MODE to turn the rest back on."
1037
+ },
1147
1038
  companion: {
1148
1039
  close: "Close panel",
1149
1040
  moveSide: "Move panel",
@@ -1495,6 +1386,8 @@ var en_default = {
1495
1386
  closeOnOpenDesc: "Close the sidebar after you open a host from it",
1496
1387
  showFolderPaths: "Show folder paths",
1497
1388
  showFolderPathsDesc: "Show the parent folder path above nested subfolder names. Subfolders stay indented either way.",
1389
+ stripedRows: "Alternating row colors",
1390
+ stripedRowsDesc: "Shade every other row in the host list",
1498
1391
  rowDetailsTitle: "Row details",
1499
1392
  rowDetailsDesc: "Choose what shows on each host.",
1500
1393
  showAddress: "Address",
@@ -1680,6 +1573,8 @@ var en_default = {
1680
1573
  sharedBadge: "Shared",
1681
1574
  sharedBadgeTooltip: "Shared by {{owner}} ({{level}} access)",
1682
1575
  viewOnlyBanner: "This host is shared with you by {{owner}} with view access. The configuration is read-only.",
1576
+ folderRuleRemoveFromHosts: "Also remove this access from every host in the folder and its subfolders",
1577
+ sharedCopyBanner: "This host is shared with you by {{owner}}. The desktop keeps a read-only copy, so change it on the server and it syncs here.",
1683
1578
  sharedEditBanner: "This host is shared with you by {{owner}} with edit access. Changes apply to the real host; authentication references can only be changed by the owner.",
1684
1579
  ownerOnlyControl: "Only the host owner can change this",
1685
1580
  ownerAuthPrivate: "The host owner's SSH authentication is private. Use \u201CSet personal SSH authentication\u201D from the host menu to choose your own credential.",
@@ -2220,7 +2115,8 @@ var en_default = {
2220
2115
  disabled: "Disabled",
2221
2116
  failed: "Stopped unexpectedly",
2222
2117
  blocked: "Waiting on another plugin",
2223
- server: "Runs on your server"
2118
+ server: "Runs on your server",
2119
+ safeMode: "Off in safe mode"
2224
2120
  },
2225
2121
  groups: {
2226
2122
  running: "Running",
@@ -2857,7 +2753,7 @@ function resetRegisteredRailItems() {
2857
2753
 
2858
2754
  // src/ui/sidebar/HostManagerTabs.tsx
2859
2755
  init_define_import_meta_env();
2860
- import { jsx as jsx4 } from "react/jsx-runtime";
2756
+ import { jsx } from "react/jsx-runtime";
2861
2757
  var sections = createRegistry(byOrderThenId);
2862
2758
  var registerHostEditorSection = sections.register;
2863
2759
  var unregisterHostEditorSection = sections.unregister;
@@ -2881,7 +2777,7 @@ function mergeByOrder(core, group, t, protocols2, defaultsMode = false) {
2881
2777
  return {
2882
2778
  id: section.id,
2883
2779
  label: t(section.labelKey, { defaultValue: section.label }),
2884
- icon: Icon ? /* @__PURE__ */ jsx4(Icon, { className: "size-3" }) : null,
2780
+ icon: Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-3" }) : null,
2885
2781
  order: section.order ?? 1e3
2886
2782
  };
2887
2783
  });
@@ -2893,13 +2789,13 @@ function makeHostTabs(t, protocols2, defaultsMode = false) {
2893
2789
  {
2894
2790
  id: "general",
2895
2791
  label: t("hosts.tabGeneral"),
2896
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2792
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2897
2793
  order: 0
2898
2794
  },
2899
2795
  {
2900
2796
  id: "ssh",
2901
2797
  label: t("hosts.tabSsh"),
2902
- icon: /* @__PURE__ */ jsx4(Terminal, { className: "size-3" }),
2798
+ icon: /* @__PURE__ */ jsx(Terminal, { className: "size-3" }),
2903
2799
  order: 10
2904
2800
  }
2905
2801
  ],
@@ -2915,7 +2811,7 @@ function makeHostSshSubTabs(t, protocols2, defaultsMode = false) {
2915
2811
  {
2916
2812
  id: "ssh",
2917
2813
  label: t("hosts.tabGeneral"),
2918
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2814
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2919
2815
  order: 0
2920
2816
  }
2921
2817
  ],
@@ -3389,7 +3285,7 @@ async function pluginWsUrl(pluginId, path2, options = {}) {
3389
3285
  }
3390
3286
  const token = await fetchSocketTicket() ?? localStorage.getItem("jwt");
3391
3287
  const protocols2 = websocketAuthProtocols(token);
3392
- const devProxy = !define_import_meta_env_default.VITE_API_HOST;
3288
+ const devProxy = false;
3393
3289
  const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
3394
3290
  if (devProxy) {
3395
3291
  return {
@@ -3465,7 +3361,7 @@ function sectionPosition(section, order2 = 0, position) {
3465
3361
 
3466
3362
  // src/ui/hooks/use-status-color-scheme.ts
3467
3363
  init_define_import_meta_env();
3468
- import { useState as useState3, useEffect as useEffect2 } from "react";
3364
+ import { useState as useState2, useEffect as useEffect2 } from "react";
3469
3365
  var PREFS_KEY = "hostSidebarPreferences";
3470
3366
  var PREFS_EVENT = "hostSidebarPreferencesChanged";
3471
3367
  function readStatusColorScheme() {
@@ -3479,7 +3375,7 @@ function readStatusColorScheme() {
3479
3375
  }
3480
3376
  }
3481
3377
  function useStatusColorScheme() {
3482
- const [scheme, setScheme] = useState3(
3378
+ const [scheme, setScheme] = useState2(
3483
3379
  readStatusColorScheme
3484
3380
  );
3485
3381
  useEffect2(() => {
@@ -3519,10 +3415,6 @@ function getStatusClasses(status, scheme, variant, loading = false) {
3519
3415
  return online ? "border-accent-brand/40 text-accent-brand bg-accent-brand/10" : "border-border/50 text-muted-foreground/60 bg-muted/30";
3520
3416
  }
3521
3417
 
3522
- // src/ui/components/theme-provider.tsx
3523
- init_define_import_meta_env();
3524
- import { createContext, useContext, useEffect as useEffect3, useState as useState4 } from "react";
3525
-
3526
3418
  // src/ui/look/theme-registry.ts
3527
3419
  init_define_import_meta_env();
3528
3420
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
@@ -3606,6 +3498,10 @@ function arePluginThemesKnown() {
3606
3498
  return pluginThemesKnown;
3607
3499
  }
3608
3500
 
3501
+ // src/ui/components/theme-provider.tsx
3502
+ init_define_import_meta_env();
3503
+ import { createContext, useContext, useEffect as useEffect3, useState as useState3 } from "react";
3504
+
3609
3505
  // src/ui/look/theme-css.ts
3610
3506
  init_define_import_meta_env();
3611
3507
  var LINK_ID = "termix-theme-css";
@@ -3656,7 +3552,7 @@ function applyThemeCss(id, theme) {
3656
3552
  }
3657
3553
 
3658
3554
  // src/ui/components/theme-provider.tsx
3659
- import { jsx as jsx5 } from "react/jsx-runtime";
3555
+ import { jsx as jsx2 } from "react/jsx-runtime";
3660
3556
  var initialState = {
3661
3557
  theme: "dark",
3662
3558
  setTheme: () => null,
@@ -3670,10 +3566,10 @@ function ThemeProvider({
3670
3566
  storageKey = "vite-ui-theme",
3671
3567
  ...props
3672
3568
  }) {
3673
- const [theme, setTheme] = useState4(
3569
+ const [theme, setTheme] = useState3(
3674
3570
  () => localStorage.getItem(storageKey) || defaultTheme
3675
3571
  );
3676
- const [previewTheme, setPreviewTheme] = useState4(null);
3572
+ const [previewTheme, setPreviewTheme] = useState3(null);
3677
3573
  const themes = useThemes();
3678
3574
  const shownTheme = effectiveThemeId(theme);
3679
3575
  const activeTheme = previewTheme ?? shownTheme;
@@ -3696,7 +3592,7 @@ function ThemeProvider({
3696
3592
  setPreviewTheme(preview);
3697
3593
  }
3698
3594
  };
3699
- return /* @__PURE__ */ jsx5(ThemeProviderContext.Provider, { ...props, value, children });
3595
+ return /* @__PURE__ */ jsx2(ThemeProviderContext.Provider, { ...props, value, children });
3700
3596
  }
3701
3597
  var useTheme = () => {
3702
3598
  const context = useContext(ThemeProviderContext);
@@ -3707,7 +3603,7 @@ var useTheme = () => {
3707
3603
 
3708
3604
  // src/ui/hooks/useSshAuthProviders.ts
3709
3605
  init_define_import_meta_env();
3710
- import { useEffect as useEffect4, useMemo as useMemo2, useState as useState5 } from "react";
3606
+ import { useEffect as useEffect4, useMemo as useMemo2, useState as useState4 } from "react";
3711
3607
  var BUILTIN_TYPES = ["password", "key", "credential", "agent", "none"];
3712
3608
  function builtinSummary(type) {
3713
3609
  return {
@@ -3725,7 +3621,7 @@ function builtinSummary(type) {
3725
3621
  var cache = null;
3726
3622
  function useSshAuthProviders() {
3727
3623
  const editors = useSshAuthEditors();
3728
- const [summaries, setSummaries] = useState5(
3624
+ const [summaries, setSummaries] = useState4(
3729
3625
  null
3730
3626
  );
3731
3627
  useEffect4(() => {
@@ -3807,14 +3703,14 @@ import {
3807
3703
  useContext as useContext2,
3808
3704
  useEffect as useEffect5,
3809
3705
  useMemo as useMemo4,
3810
- useState as useState6
3706
+ useState as useState5
3811
3707
  } from "react";
3812
3708
 
3813
3709
  // src/ui/contexts/branding-document.ts
3814
3710
  init_define_import_meta_env();
3815
3711
 
3816
3712
  // src/ui/contexts/BrandingContext.tsx
3817
- import { jsx as jsx6 } from "react/jsx-runtime";
3713
+ import { jsx as jsx3 } from "react/jsx-runtime";
3818
3714
  var DEFAULT_APP_NAME = "Termix";
3819
3715
  var BrandingContext = createContext2({
3820
3716
  ready: false,
@@ -5205,7 +5101,7 @@ import {
5205
5101
  useLayoutEffect as useLayoutEffect2,
5206
5102
  useMemo as useMemo5,
5207
5103
  useRef as useRef6,
5208
- useState as useState12
5104
+ useState as useState11
5209
5105
  } from "react";
5210
5106
 
5211
5107
  // src/ui/shell/stage/tab-stage-store.ts
@@ -5213,14 +5109,14 @@ init_define_import_meta_env();
5213
5109
  function createTabStageStore() {
5214
5110
  const nodes = /* @__PURE__ */ new Map();
5215
5111
  const paneEls = /* @__PURE__ */ new Map();
5216
- const listeners9 = /* @__PURE__ */ new Set();
5112
+ const listeners8 = /* @__PURE__ */ new Set();
5217
5113
  let normalView = null;
5218
- let version4 = 0;
5114
+ let version3 = 0;
5219
5115
  let order2 = [];
5220
5116
  let lastAnimatedTabId = null;
5221
5117
  const bump = () => {
5222
- version4 += 1;
5223
- for (const listener of listeners9) listener();
5118
+ version3 += 1;
5119
+ for (const listener of listeners8) listener();
5224
5120
  };
5225
5121
  function getNode(tabId, ownBackground) {
5226
5122
  let el = nodes.get(tabId);
@@ -5322,18 +5218,18 @@ function createTabStageStore() {
5322
5218
  setNormalView,
5323
5219
  place,
5324
5220
  subscribe: (listener) => {
5325
- listeners9.add(listener);
5221
+ listeners8.add(listener);
5326
5222
  return () => {
5327
- listeners9.delete(listener);
5223
+ listeners8.delete(listener);
5328
5224
  };
5329
5225
  },
5330
- version: () => version4
5226
+ version: () => version3
5331
5227
  };
5332
5228
  }
5333
5229
 
5334
5230
  // src/ui/shell/controller/use-regions.ts
5335
5231
  init_define_import_meta_env();
5336
- import { useCallback as useCallback2, useEffect as useEffect6, useRef, useState as useState7 } from "react";
5232
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef, useState as useState6 } from "react";
5337
5233
  var MIN_WIDTH = 160;
5338
5234
  var MAX_WIDTH = 480;
5339
5235
  function clampWidth(width) {
@@ -5366,15 +5262,15 @@ function startDockDrag(e, startW, sign, setWidth, setDragging) {
5366
5262
  }
5367
5263
  var DOCK_DEFAULT_WIDTH = 291;
5368
5264
  function useRegions(isMobile) {
5369
- const [sidebarOpen, setSidebarOpen] = useState7(true);
5370
- const [railView, setRailView] = useState7("hosts");
5371
- const [sidebarWidth, setSidebarWidth] = useState7(() => {
5265
+ const [sidebarOpen, setSidebarOpen] = useState6(true);
5266
+ const [railView, setRailView] = useState6("hosts");
5267
+ const [sidebarWidth, setSidebarWidth] = useState6(() => {
5372
5268
  const saved = localStorage.getItem("termix_sidebarWidth");
5373
5269
  return saved ? parseInt(saved, 10) : 291;
5374
5270
  });
5375
- const [sidebarDragging, setSidebarDragging] = useState7(false);
5376
- const [panelEditing, setPanelEditing] = useState7({});
5377
- const [scopeEditing, setScopeEditing] = useState7({});
5271
+ const [sidebarDragging, setSidebarDragging] = useState6(false);
5272
+ const [panelEditing, setPanelEditing] = useState6({});
5273
+ const [scopeEditing, setScopeEditing] = useState6({});
5378
5274
  const editingSetters = useRef(
5379
5275
  /* @__PURE__ */ new Map()
5380
5276
  );
@@ -5400,15 +5296,15 @@ function useRegions(isMobile) {
5400
5296
  );
5401
5297
  return editingSetters.current.get(key);
5402
5298
  }, []);
5403
- const [rightRailView, setRightRailView] = useState7(() => {
5299
+ const [rightRailView, setRightRailView] = useState6(() => {
5404
5300
  const saved = localStorage.getItem("termix_rightRailView");
5405
5301
  return saved && rightDockableIds().includes(saved) ? saved : null;
5406
5302
  });
5407
- const [rightSidebarWidth, setRightSidebarWidth] = useState7(() => {
5303
+ const [rightSidebarWidth, setRightSidebarWidth] = useState6(() => {
5408
5304
  const saved = localStorage.getItem("termix_rightSidebarWidth");
5409
5305
  return saved ? parseInt(saved, 10) : 291;
5410
5306
  });
5411
- const [rightSidebarDragging, setRightSidebarDragging] = useState7(false);
5307
+ const [rightSidebarDragging, setRightSidebarDragging] = useState6(false);
5412
5308
  const lastRightRailViewRef = useRef(
5413
5309
  localStorage.getItem("termix_lastRightRailView")
5414
5310
  );
@@ -5519,7 +5415,7 @@ function useRegions(isMobile) {
5519
5415
 
5520
5416
  // src/ui/shell/controller/use-hosts.ts
5521
5417
  init_define_import_meta_env();
5522
- import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef2, useState as useState8 } from "react";
5418
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef2, useState as useState7 } from "react";
5523
5419
 
5524
5420
  // src/ui/sidebar/build-host-tree.ts
5525
5421
  init_define_import_meta_env();
@@ -5591,10 +5487,10 @@ function buildHostTree(hosts, folderMeta) {
5591
5487
 
5592
5488
  // src/ui/shell/controller/use-hosts.ts
5593
5489
  function useHostList() {
5594
- const [tree, setTree] = useState8(null);
5595
- const [loading, setLoading] = useState8(true);
5596
- const [loaded, setLoaded] = useState8(false);
5597
- const [list, setList] = useState8([]);
5490
+ const [tree, setTree] = useState7(null);
5491
+ const [loading, setLoading] = useState7(true);
5492
+ const [loaded, setLoaded] = useState7(false);
5493
+ const [list, setList] = useState7([]);
5598
5494
  const listRef = useRef2(list);
5599
5495
  listRef.current = list;
5600
5496
  const reload = useCallback3(async () => {
@@ -5651,7 +5547,7 @@ import {
5651
5547
  useCallback as useCallback4,
5652
5548
  useEffect as useEffect8,
5653
5549
  useRef as useRef3,
5654
- useState as useState9
5550
+ useState as useState8
5655
5551
  } from "react";
5656
5552
  import { toast } from "sonner";
5657
5553
  import { useTranslation } from "react-i18next";
@@ -5703,10 +5599,10 @@ function useTabs({
5703
5599
  }) {
5704
5600
  const { t } = useTranslation();
5705
5601
  const splitLabel = useSplitLabel();
5706
- const [tabs, setTabs] = useState9([dashboardTab(t("nav.dashboard"))]);
5707
- const [activeTabId, setActiveTabId] = useState9("dashboard");
5708
- const [tabsReady, setTabsReady] = useState9(false);
5709
- const [backgroundTabRecords, setBackgroundTabRecords] = useState9([]);
5602
+ const [tabs, setTabs] = useState8([dashboardTab(t("nav.dashboard"))]);
5603
+ const [activeTabId, setActiveTabId] = useState8("dashboard");
5604
+ const [tabsReady, setTabsReady] = useState8(false);
5605
+ const [backgroundTabRecords, setBackgroundTabRecords] = useState8([]);
5710
5606
  const paneLayoutRestoredRef = useRef3(false);
5711
5607
  const splitTabsRestoredRef = useRef3(false);
5712
5608
  const splitOpsRef = useRef3({
@@ -5795,14 +5691,14 @@ function useTabs({
5795
5691
  if (!isSplitTab(active)) return activeTabId;
5796
5692
  return findPane(active.split, active.split.focusedPaneId)?.tabId ?? "";
5797
5693
  })();
5798
- const [lastTerminalTabId, setLastTerminalTabId] = useState9(activeTabId);
5694
+ const [lastTerminalTabId, setLastTerminalTabId] = useState8(activeTabId);
5799
5695
  useEffect8(() => {
5800
5696
  const working = tabs.find((t2) => t2.id === workingTabId);
5801
5697
  if (working && getTabType(working.type)?.commandTarget) {
5802
5698
  setLastTerminalTabId(working.id);
5803
5699
  }
5804
5700
  }, [workingTabId, tabs]);
5805
- const [showTabNumbers, setShowTabNumbers] = useState9(
5701
+ const [showTabNumbers, setShowTabNumbers] = useState8(
5806
5702
  () => localStorage.getItem("showTabNumbers") === "true"
5807
5703
  );
5808
5704
  useEffect8(() => {
@@ -6356,7 +6252,7 @@ function useSplit({
6356
6252
  tabsState,
6357
6253
  isMobile,
6358
6254
  paneEls,
6359
- callbacks: callbacks2,
6255
+ callbacks,
6360
6256
  resizeAllTerminals
6361
6257
  }) {
6362
6258
  const { t } = useTranslation2();
@@ -6755,8 +6651,8 @@ function useSplit({
6755
6651
  };
6756
6652
  }
6757
6653
  const pickerShell = {
6758
- ...callbacks2,
6759
- openTab: (host, type, options) => callbacks2.openTab(host, type, { ...options, forceNewTab: true })
6654
+ ...callbacks,
6655
+ openTab: (host, type, options) => callbacks.openTab(host, type, { ...options, forceNewTab: true })
6760
6656
  };
6761
6657
  const split = {
6762
6658
  active: activeSplit,
@@ -6922,7 +6818,7 @@ function useWorkspaceLayout(tabsState, regions) {
6922
6818
 
6923
6819
  // src/ui/shell/controller/use-shell-keybindings.ts
6924
6820
  init_define_import_meta_env();
6925
- import { useEffect as useEffect11, useRef as useRef5, useState as useState10 } from "react";
6821
+ import { useEffect as useEffect11, useRef as useRef5, useState as useState9 } from "react";
6926
6822
 
6927
6823
  // src/ui/lib/pane-nav-hotkey.ts
6928
6824
  init_define_import_meta_env();
@@ -7026,7 +6922,7 @@ function useShellKeybindings({
7026
6922
  closeActiveTabRef.current();
7027
6923
  });
7028
6924
  }, []);
7029
- const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState10(() => {
6925
+ const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState9(() => {
7030
6926
  const v = localStorage.getItem("commandPaletteShortcutEnabled");
7031
6927
  return v !== null ? v === "true" : true;
7032
6928
  });
@@ -7093,7 +6989,7 @@ function useShellKeybindings({
7093
6989
  const tabDigit = getTabJumpDigit(e);
7094
6990
  if (tabDigit !== null) {
7095
6991
  const currentTabs = tabsRef.current.filter(
7096
- (tab) => !tab.parentSplitTabId
6992
+ (tab) => !tab.parentSplitTabId && tab.type !== "dashboard"
7097
6993
  );
7098
6994
  const index = tabDigit - 1;
7099
6995
  if (index < currentTabs.length) {
@@ -7139,7 +7035,7 @@ function useShellKeybindings({
7139
7035
 
7140
7036
  // src/ui/shell/controller/use-shell-lifecycle.ts
7141
7037
  init_define_import_meta_env();
7142
- import { useCallback as useCallback5, useEffect as useEffect12, useState as useState11 } from "react";
7038
+ import { useCallback as useCallback5, useEffect as useEffect12, useState as useState10 } from "react";
7143
7039
  import { toast as toast3 } from "sonner";
7144
7040
  import { useTranslation as useTranslation3 } from "react-i18next";
7145
7041
  var SNAPSHOT_KEYS = [
@@ -7218,12 +7114,12 @@ function useShellLifecycle({
7218
7114
  }) {
7219
7115
  const { t, i18n: i18n2 } = useTranslation3();
7220
7116
  const { setTheme } = useTheme();
7221
- const [userPrefs, setUserPrefs] = useState11({
7117
+ const [userPrefs, setUserPrefs] = useState10({
7222
7118
  reopenTabsOnLogin: false
7223
7119
  });
7224
- const [userPrefsLoaded, setUserPrefsLoaded] = useState11(false);
7225
- const [isAdmin, setIsAdmin] = useState11(false);
7226
- const [isAppFullscreen, setIsAppFullscreen] = useState11(
7120
+ const [userPrefsLoaded, setUserPrefsLoaded] = useState10(false);
7121
+ const [isAdmin, setIsAdmin] = useState10(false);
7122
+ const [isAppFullscreen, setIsAppFullscreen] = useState10(
7227
7123
  () => !!document.fullscreenElement
7228
7124
  );
7229
7125
  useEffect12(() => {
@@ -7354,7 +7250,7 @@ function useShellSnapshot() {
7354
7250
  }
7355
7251
 
7356
7252
  // src/ui/shell/controller/ShellControllerProvider.tsx
7357
- import { jsx as jsx7 } from "react/jsx-runtime";
7253
+ import { jsx as jsx4 } from "react/jsx-runtime";
7358
7254
  var ShellControllerContext = createContext3(null);
7359
7255
  var TabStageContext = createContext3(null);
7360
7256
  function useShellController() {
@@ -7386,7 +7282,7 @@ function ShellControllerProvider({
7386
7282
  useTabTypes();
7387
7283
  const { settled: pluginsSettled } = usePluginStore();
7388
7284
  const isMobile = useIsMobile();
7389
- const [stageStore] = useState12(createTabStageStore);
7285
+ const [stageStore] = useState11(createTabStageStore);
7390
7286
  const confirmRef = useRef6(null);
7391
7287
  const openSingletonRef = useRef6(
7392
7288
  () => void 0
@@ -7397,7 +7293,7 @@ function ShellControllerProvider({
7397
7293
  });
7398
7294
  const regions = useRegions(isMobile);
7399
7295
  const hosts = useHostList();
7400
- const [paletteOpen, setPaletteOpen] = useState12(false);
7296
+ const [paletteOpen, setPaletteOpen] = useState11(false);
7401
7297
  const tabsState = useTabs({
7402
7298
  hosts: hosts.list,
7403
7299
  hostsRef: hosts.listRef,
@@ -7454,6 +7350,10 @@ function ShellControllerProvider({
7454
7350
  regions.right.setView((prev) => prev === id ? null : prev);
7455
7351
  },
7456
7352
  openHostEditor: (draft) => requestManage({ kind: "host", hostId: null, draft }),
7353
+ editHost: (hostId) => {
7354
+ requestManage({ kind: "host", hostId: String(hostId) });
7355
+ if (isMobile) regions.left.setOpen(false);
7356
+ },
7457
7357
  saveQuickConnect: tabsState.saveQuickConnectHost,
7458
7358
  confirm: (options) => confirmRef.current ? confirmRef.current(options) : Promise.resolve(false)
7459
7359
  };
@@ -7461,7 +7361,7 @@ function ShellControllerProvider({
7461
7361
  useLayoutEffect2(() => {
7462
7362
  shellImplRef.current = shellCallbacksImpl;
7463
7363
  });
7464
- const callbacks2 = useMemo5(
7364
+ const callbacks = useMemo5(
7465
7365
  () => ({
7466
7366
  openTab: (...args) => shellImplRef.current.openTab(...args),
7467
7367
  openSingletonTab: (...args) => shellImplRef.current.openSingletonTab(...args),
@@ -7471,6 +7371,7 @@ function ShellControllerProvider({
7471
7371
  openRailView: (...args) => shellImplRef.current.openRailView(...args),
7472
7372
  closeRailView: (...args) => shellImplRef.current.closeRailView(...args),
7473
7373
  openHostEditor: (...args) => shellImplRef.current.openHostEditor?.(...args),
7374
+ editHost: (...args) => shellImplRef.current.editHost?.(...args),
7474
7375
  saveQuickConnect: (...args) => shellImplRef.current.saveQuickConnect(...args),
7475
7376
  confirm: (...args) => shellImplRef.current.confirm(...args)
7476
7377
  }),
@@ -7478,13 +7379,13 @@ function ShellControllerProvider({
7478
7379
  );
7479
7380
  const panelCallbacks = useMemo5(
7480
7381
  () => ({
7481
- ...callbacks2,
7382
+ ...callbacks,
7482
7383
  openTab: (...args) => {
7483
- callbacks2.openTab(...args);
7384
+ callbacks.openTab(...args);
7484
7385
  if (isMobile) regions.left.setOpen(false);
7485
7386
  }
7486
7387
  }),
7487
- [callbacks2, isMobile]
7388
+ [callbacks, isMobile]
7488
7389
  );
7489
7390
  const resizeAllTerminals = useCallback6(() => {
7490
7391
  const id = requestAnimationFrame(() => {
@@ -7501,7 +7402,7 @@ function ShellControllerProvider({
7501
7402
  tabsState,
7502
7403
  isMobile,
7503
7404
  paneEls: stageStore.paneEls,
7504
- callbacks: callbacks2,
7405
+ callbacks,
7505
7406
  resizeAllTerminals
7506
7407
  });
7507
7408
  useEffect13(() => {
@@ -7526,7 +7427,7 @@ function ShellControllerProvider({
7526
7427
  toggleFullscreen: lifecycle.toggleAppFullscreen
7527
7428
  });
7528
7429
  useEffect13(() => {
7529
- setShellCallbacks(callbacks2);
7430
+ setShellCallbacks(callbacks);
7530
7431
  });
7531
7432
  const activeTab = tabs.find((tab) => tab.id === activeTabId);
7532
7433
  const controller = {
@@ -7575,7 +7476,7 @@ function ShellControllerProvider({
7575
7476
  active: lifecycle.isAppFullscreen,
7576
7477
  toggle: lifecycle.toggleAppFullscreen
7577
7478
  },
7578
- callbacks: callbacks2,
7479
+ callbacks,
7579
7480
  panelCallbacks,
7580
7481
  settings: {
7581
7482
  username,
@@ -7592,7 +7493,7 @@ function ShellControllerProvider({
7592
7493
  publishShellController(controller);
7593
7494
  });
7594
7495
  useEffect13(() => () => publishShellController(null), []);
7595
- return /* @__PURE__ */ jsx7(TabStageContext.Provider, { value: stageStore, children: /* @__PURE__ */ jsx7(ShellControllerContext.Provider, { value: controller, children }) });
7496
+ return /* @__PURE__ */ jsx4(TabStageContext.Provider, { value: stageStore, children: /* @__PURE__ */ jsx4(ShellControllerContext.Provider, { value: controller, children }) });
7596
7497
  }
7597
7498
 
7598
7499
  // src/ui/lib/adaptive-polling.ts
@@ -7701,7 +7602,7 @@ import {
7701
7602
  useRef as useRef7,
7702
7603
  useMemo as useMemo6,
7703
7604
  useSyncExternalStore as useSyncExternalStore8,
7704
- useState as useState13
7605
+ useState as useState12
7705
7606
  } from "react";
7706
7607
 
7707
7608
  // src/ui/lib/server-status-store.ts
@@ -7795,7 +7696,7 @@ var ServerStatusStore = class {
7795
7696
  };
7796
7697
 
7797
7698
  // src/ui/lib/ServerStatusContext.tsx
7798
- import { jsx as jsx8 } from "react/jsx-runtime";
7699
+ import { jsx as jsx5 } from "react/jsx-runtime";
7799
7700
  var StatusStoreContext = createContext4(null);
7800
7701
  var ServerStatusContext = createContext4(null);
7801
7702
  var activeStore = null;
@@ -7812,7 +7713,7 @@ function ServerStatusProvider({
7812
7713
  storeRef.current = new ServerStatusStore();
7813
7714
  }
7814
7715
  const store = storeRef.current;
7815
- const [version4, setVersion] = useState13(0);
7716
+ const [version3, setVersion] = useState12(0);
7816
7717
  const mountedRef = useRef7(true);
7817
7718
  const refreshInFlightRef = useRef7(null);
7818
7719
  useEffect14(() => store.subscribeAll(() => setVersion((v) => v + 1)), [store]);
@@ -7895,9 +7796,9 @@ function ServerStatusProvider({
7895
7796
  }),
7896
7797
  // version refreshes the statuses/initialLoadComplete snapshots
7897
7798
  // eslint-disable-next-line react-hooks/exhaustive-deps
7898
- [version4, refreshStatuses, getStatus]
7799
+ [version3, refreshStatuses, getStatus]
7899
7800
  );
7900
- return /* @__PURE__ */ jsx8(StatusStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx8(ServerStatusContext.Provider, { value: contextValue, children }) });
7801
+ return /* @__PURE__ */ jsx5(StatusStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx5(ServerStatusContext.Provider, { value: contextValue, children }) });
7901
7802
  }
7902
7803
  function useStatusStore() {
7903
7804
  const store = useContext4(StatusStoreContext);
@@ -7947,7 +7848,7 @@ function useOptionalHostStatusEntry(hostId) {
7947
7848
 
7948
7849
  // src/ui/plugin-host/bridge.ts
7949
7850
  init_define_import_meta_env();
7950
- import { useCallback as useCallback8, useEffect as useEffect15, useMemo as useMemo8, useState as useState14 } from "react";
7851
+ import { useCallback as useCallback8, useEffect as useEffect15, useMemo as useMemo8, useState as useState13 } from "react";
7951
7852
 
7952
7853
  // src/ui/shell/companions.ts
7953
7854
  init_define_import_meta_env();
@@ -8267,7 +8168,7 @@ var pluginHostBridge = {
8267
8168
  },
8268
8169
  useSettings(pluginId, scope, hostId) {
8269
8170
  const numericHostId = hostId === void 0 ? void 0 : Number.parseInt(String(hostId), 10);
8270
- const [state2, setState] = useState14({ values: {}, loaded: false });
8171
+ const [state2, setState] = useState13({ values: {}, loaded: false });
8271
8172
  useEffect15(() => {
8272
8173
  let cancelled = false;
8273
8174
  if (scope === "host" && !Number.isFinite(numericHostId)) return;
@@ -8323,7 +8224,7 @@ var pluginHostBridge = {
8323
8224
  );
8324
8225
  },
8325
8226
  useCurrentUser() {
8326
- const [user, setUser] = useState14(null);
8227
+ const [user, setUser] = useState13(null);
8327
8228
  useEffect15(() => {
8328
8229
  let cancelled = false;
8329
8230
  loadCurrentUser().then((next) => {
@@ -8907,8 +8808,8 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8907
8808
  },
8908
8809
  registerOnboardingStep(step) {
8909
8810
  requireStepId(step.id, "onboarding step");
8910
- const version4 = step.version ?? 1;
8911
- if (!Number.isInteger(version4) || version4 < 1) {
8811
+ const version3 = step.version ?? 1;
8812
+ if (!Number.isInteger(version3) || version3 < 1) {
8912
8813
  throw new Error(
8913
8814
  `${pluginId}: onboarding step "${step.id}" needs a whole version of 1 or more`
8914
8815
  );
@@ -8918,7 +8819,7 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8918
8819
  onboardingSteps.register({
8919
8820
  id: pluginStepKey(pluginId, step.id),
8920
8821
  pluginId,
8921
- version: version4,
8822
+ version: version3,
8922
8823
  titleKey: key(step.titleKey),
8923
8824
  descriptionKey: step.descriptionKey ? key(step.descriptionKey) : void 0,
8924
8825
  icon: step.icon ? withIconBoundary(pluginId, step.icon) : void 0,
@@ -9056,918 +8957,6 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
9056
8957
  };
9057
8958
  }
9058
8959
 
9059
- // src/ui/hooks/use-installed-plugins.ts
9060
- init_define_import_meta_env();
9061
- import { useEffect as useEffect17, useState as useState15 } from "react";
9062
- function useInstalledPlugins() {
9063
- const [plugins, setPlugins] = useState15([]);
9064
- useEffect17(() => {
9065
- let cancelled = false;
9066
- let request = 0;
9067
- const load2 = () => {
9068
- const current2 = ++request;
9069
- void getPlugins().then((loaded) => {
9070
- if (!cancelled && current2 === request) setPlugins(loaded);
9071
- }).catch(() => {
9072
- });
9073
- };
9074
- load2();
9075
- window.addEventListener(PLUGINS_CHANGED_EVENT, load2);
9076
- return () => {
9077
- cancelled = true;
9078
- window.removeEventListener(PLUGINS_CHANGED_EVENT, load2);
9079
- };
9080
- }, []);
9081
- return plugins;
9082
- }
9083
-
9084
- // src/ui/settings/SettingsFields.tsx
9085
- init_define_import_meta_env();
9086
- import { useTranslation as useTranslation4 } from "react-i18next";
9087
- import { Fragment, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
9088
- function SettingsFieldRow({
9089
- pluginId,
9090
- field,
9091
- values,
9092
- setValue,
9093
- running,
9094
- error,
9095
- disabled = false,
9096
- defaultKey
9097
- }) {
9098
- const { t } = useTranslation4();
9099
- useSettingsComponents();
9100
- const label = (key) => key ? t(pluginKey(pluginId, key)) : "";
9101
- if (field.type === "custom") {
9102
- const Component = getSettingsComponent(pluginId, field.component);
9103
- if (!Component) return null;
9104
- return /* @__PURE__ */ jsx9(
9105
- Component,
9106
- {
9107
- pluginId,
9108
- values,
9109
- setValue,
9110
- running
9111
- }
9112
- );
9113
- }
9114
- const value = values[field.key];
9115
- const readOnly = disabled || !running;
9116
- const description = field.descriptionKey ? /* @__PURE__ */ jsxs2(Fragment, { children: [
9117
- label(field.descriptionKey),
9118
- error && /* @__PURE__ */ jsx9("span", { className: "block text-destructive mt-0.5", children: error })
9119
- ] }) : error ? /* @__PURE__ */ jsx9("span", { className: "text-destructive", children: error }) : void 0;
9120
- const control = /* @__PURE__ */ jsx9(
9121
- FieldControl,
9122
- {
9123
- pluginId,
9124
- field,
9125
- value,
9126
- setValue,
9127
- readOnly
9128
- }
9129
- );
9130
- if (field.type === "boolean") {
9131
- return /* @__PURE__ */ jsx9(
9132
- SettingRow,
9133
- {
9134
- label: label(field.labelKey),
9135
- description,
9136
- badge: error ? t("common.error") : void 0,
9137
- defaultKey,
9138
- children: control
9139
- }
9140
- );
9141
- }
9142
- return /* @__PURE__ */ jsx9(DefaultsOnly, { settingKey: defaultKey ?? "", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1.5 py-3 border-b border-border last:border-0 min-w-0", children: [
9143
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
9144
- /* @__PURE__ */ jsx9("span", { className: "text-sm font-medium leading-snug", children: label(field.labelKey) }),
9145
- error && /* @__PURE__ */ jsx9("span", { className: "text-[10px] font-bold text-warning border border-warning/40 px-1 shrink-0", children: t("common.error") }),
9146
- defaultKey && /* @__PURE__ */ jsx9(HostDefaultBadge, { settingKey: defaultKey })
9147
- ] }),
9148
- description && /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground leading-snug", children: description }),
9149
- /* @__PURE__ */ jsx9("div", { className: "min-w-0", children: control })
9150
- ] }) });
9151
- }
9152
- function FieldControl({
9153
- pluginId,
9154
- field,
9155
- value,
9156
- setValue,
9157
- readOnly
9158
- }) {
9159
- const { t } = useTranslation4();
9160
- const label = (key) => key ? t(pluginKey(pluginId, key)) : "";
9161
- const placeholder = field.placeholderKey ? label(field.placeholderKey) : void 0;
9162
- switch (field.type) {
9163
- case "boolean":
9164
- return /* @__PURE__ */ jsx9(
9165
- FakeSwitch,
9166
- {
9167
- checked: value === true,
9168
- disabled: readOnly,
9169
- onChange: (next) => setValue(field.key, next)
9170
- }
9171
- );
9172
- case "number":
9173
- return /* @__PURE__ */ jsx9(
9174
- Input,
9175
- {
9176
- type: "number",
9177
- className: "h-8 w-full max-w-40 text-sm",
9178
- disabled: readOnly,
9179
- min: field.min,
9180
- max: field.max,
9181
- placeholder,
9182
- value: value === void 0 || value === null ? "" : String(value),
9183
- onChange: (event) => setValue(
9184
- field.key,
9185
- event.target.value === "" ? null : Number(event.target.value)
9186
- )
9187
- }
9188
- );
9189
- case "select":
9190
- return /* @__PURE__ */ jsxs2(
9191
- Select,
9192
- {
9193
- disabled: readOnly,
9194
- value: typeof value === "string" ? value : "",
9195
- onValueChange: (next) => setValue(field.key, next),
9196
- children: [
9197
- /* @__PURE__ */ jsx9(SelectTrigger, { className: "h-8 w-full min-w-0 text-sm", children: /* @__PURE__ */ jsx9(SelectValue, { placeholder }) }),
9198
- /* @__PURE__ */ jsx9(SelectContent, { children: (field.options ?? []).map((option) => /* @__PURE__ */ jsx9(SelectItem, { value: option.value, children: label(option.labelKey) }, option.value)) })
9199
- ]
9200
- }
9201
- );
9202
- case "multiselect": {
9203
- const selected = Array.isArray(value) ? value : [];
9204
- return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-1.5", children: (field.options ?? []).map((option) => /* @__PURE__ */ jsxs2(
9205
- "label",
9206
- {
9207
- className: "flex items-center gap-2 text-xs text-muted-foreground",
9208
- children: [
9209
- /* @__PURE__ */ jsx9(
9210
- Checkbox2,
9211
- {
9212
- disabled: readOnly,
9213
- checked: selected.includes(option.value),
9214
- onCheckedChange: (checked) => setValue(
9215
- field.key,
9216
- checked ? [...selected, option.value] : selected.filter((entry) => entry !== option.value)
9217
- )
9218
- }
9219
- ),
9220
- /* @__PURE__ */ jsx9("span", { children: label(option.labelKey) })
9221
- ]
9222
- },
9223
- option.value
9224
- )) });
9225
- }
9226
- case "secret":
9227
- return /* @__PURE__ */ jsx9(
9228
- SecretControl,
9229
- {
9230
- field,
9231
- value,
9232
- setValue,
9233
- readOnly,
9234
- placeholder
9235
- }
9236
- );
9237
- case "textarea":
9238
- case "json":
9239
- return /* @__PURE__ */ jsx9(
9240
- Textarea,
9241
- {
9242
- className: "w-full text-sm font-mono min-h-16",
9243
- disabled: readOnly,
9244
- placeholder,
9245
- value: typeof value === "string" ? value : value === void 0 || value === null ? "" : JSON.stringify(value, null, 2),
9246
- onChange: (event) => setValue(field.key, event.target.value)
9247
- }
9248
- );
9249
- default:
9250
- return /* @__PURE__ */ jsx9(
9251
- Input,
9252
- {
9253
- className: "h-8 w-full text-sm",
9254
- disabled: readOnly,
9255
- placeholder,
9256
- value: typeof value === "string" ? value : "",
9257
- onChange: (event) => setValue(field.key, event.target.value)
9258
- }
9259
- );
9260
- }
9261
- }
9262
- function SecretControl({
9263
- field,
9264
- value,
9265
- setValue,
9266
- readOnly,
9267
- placeholder
9268
- }) {
9269
- const { t } = useTranslation4();
9270
- const redacted = isRedactedSecret(value);
9271
- const stored = redacted && value.set;
9272
- if (redacted) {
9273
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-2", children: [
9274
- /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: stored ? t("settings.secretSet") : t("settings.secretNotSet") }),
9275
- /* @__PURE__ */ jsx9(
9276
- Button,
9277
- {
9278
- variant: "outline",
9279
- size: "sm",
9280
- className: "h-7 text-xs",
9281
- disabled: readOnly,
9282
- onClick: () => setValue(field.key, ""),
9283
- children: stored ? t("settings.secretReplace") : t("common.edit")
9284
- }
9285
- )
9286
- ] });
9287
- }
9288
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 min-w-0", children: [
9289
- /* @__PURE__ */ jsx9("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx9(
9290
- PasswordInput,
9291
- {
9292
- className: "h-8 w-full text-sm",
9293
- disabled: readOnly,
9294
- placeholder,
9295
- value: typeof value === "string" ? value : "",
9296
- onChange: (event) => setValue(field.key, event.target.value)
9297
- }
9298
- ) }),
9299
- /* @__PURE__ */ jsx9(
9300
- Button,
9301
- {
9302
- variant: "ghost",
9303
- size: "sm",
9304
- className: "h-7 text-xs",
9305
- disabled: readOnly,
9306
- onClick: () => setValue(field.key, { set: stored }),
9307
- children: t("common.cancel")
9308
- }
9309
- )
9310
- ] });
9311
- }
9312
-
9313
- // src/ui/settings/HostPluginSections.tsx
9314
- init_define_import_meta_env();
9315
- import { useMemo as useMemo9 } from "react";
9316
- import { useTranslation as useTranslation5 } from "react-i18next";
9317
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
9318
- function usePluginHostSections() {
9319
- const plugins = useInstalledPlugins();
9320
- return useMemo9(
9321
- () => plugins.filter((plugin) => {
9322
- if (!plugin.enabled) return false;
9323
- const host = plugin.contributes?.settings?.host;
9324
- return !!host && (hasVisibleFields(host.fields) || !!host.enableKey);
9325
- }),
9326
- [plugins]
9327
- );
9328
- }
9329
- function hostFieldDefaults(plugin) {
9330
- const host = plugin.contributes?.settings?.host;
9331
- if (!host) return {};
9332
- const defaults3 = {};
9333
- if (host.enableKey) defaults3[host.enableKey] = host.enableDefault ?? false;
9334
- for (const field of host.fields) {
9335
- if (field.default !== void 0) defaults3[field.key] = field.default;
9336
- }
9337
- return defaults3;
9338
- }
9339
- function HostPluginSections({
9340
- plugins,
9341
- values,
9342
- setValue
9343
- }) {
9344
- if (plugins.length === 0) return null;
9345
- return /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-2", children: plugins.map((plugin) => /* @__PURE__ */ jsx10(
9346
- HostPluginSection,
9347
- {
9348
- plugin,
9349
- values: values[plugin.id] ?? {},
9350
- setValue: (key, value) => setValue(plugin.id, key, value)
9351
- },
9352
- plugin.id
9353
- )) });
9354
- }
9355
- function HostFeatureFields({
9356
- form,
9357
- updateForm
9358
- }) {
9359
- const pluginId = usePluginScope();
9360
- const plugin = usePluginHostSections().find((p) => p.id === pluginId);
9361
- if (!plugin) return null;
9362
- const values = form?.pluginSettings ?? {};
9363
- return /* @__PURE__ */ jsx10(
9364
- HostPluginSection,
9365
- {
9366
- plugin,
9367
- values: values[plugin.id] ?? {},
9368
- setValue: (key, value) => updateForm((current2) => {
9369
- const all = current2.pluginSettings ?? {};
9370
- return {
9371
- ...current2,
9372
- pluginSettings: {
9373
- ...all,
9374
- [plugin.id]: { ...all[plugin.id] ?? {}, [key]: value }
9375
- }
9376
- };
9377
- })
9378
- }
9379
- );
9380
- }
9381
- function HostPluginSection({
9382
- plugin,
9383
- values,
9384
- setValue
9385
- }) {
9386
- const { t } = useTranslation5();
9387
- const host = plugin.contributes?.settings?.host;
9388
- if (!host) return null;
9389
- const shown = { ...hostFieldDefaults(plugin), ...values };
9390
- const running = plugin.enabled && plugin.state !== "failed";
9391
- const enableKey = host.enableKey;
9392
- const enabled = enableKey ? shown[enableKey] === true : true;
9393
- return /* @__PURE__ */ jsxs3(
9394
- SectionCard,
9395
- {
9396
- title: pluginName(t, plugin),
9397
- icon: /* @__PURE__ */ jsx10(PluginIcon, { name: plugin.icon, className: "size-3.5" }),
9398
- action: /* @__PURE__ */ jsx10(DocsLink, { href: plugin.docs }),
9399
- children: [
9400
- !running && /* @__PURE__ */ jsx10("div", { className: "my-3 border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning", children: t("settings.featureUnavailable", {
9401
- name: pluginName(t, plugin)
9402
- }) }),
9403
- enableKey && /* @__PURE__ */ jsx10(
9404
- SettingsFieldRow,
9405
- {
9406
- pluginId: plugin.id,
9407
- field: {
9408
- key: enableKey,
9409
- type: "boolean",
9410
- labelKey: host.enableLabelKey,
9411
- descriptionKey: host.enableDescriptionKey
9412
- },
9413
- values: shown,
9414
- setValue,
9415
- running,
9416
- defaultKey: `${plugin.id}.${enableKey}`
9417
- }
9418
- ),
9419
- enabled && host.fields.filter((field) => isFieldShown(field, shown)).map((field) => /* @__PURE__ */ jsx10(
9420
- SettingsFieldRow,
9421
- {
9422
- pluginId: plugin.id,
9423
- field,
9424
- values: shown,
9425
- setValue,
9426
- running,
9427
- defaultKey: `${plugin.id}.${field.key}`
9428
- },
9429
- field.key
9430
- ))
9431
- ]
9432
- }
9433
- );
9434
- }
9435
-
9436
- // src/ui/shell/ActionSlot.tsx
9437
- init_define_import_meta_env();
9438
- import React5 from "react";
9439
- import { useTranslation as useTranslation6 } from "react-i18next";
9440
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
9441
- function ActionSlot({
9442
- slotId,
9443
- context,
9444
- className,
9445
- enabled = true,
9446
- renderItem,
9447
- hideLabels,
9448
- when
9449
- }) {
9450
- const { t } = useTranslation6();
9451
- const contributions2 = useActionSlot(slotId, when).filter(
9452
- (contribution) => contribution.kind !== "component"
9453
- );
9454
- if (!enabled || contributions2.length === 0) return null;
9455
- return /* @__PURE__ */ jsx11(Fragment2, { children: contributions2.map((contribution) => {
9456
- const invoke = () => {
9457
- void invokeAction(contribution.actionId, ...context?.() ?? []);
9458
- };
9459
- if (renderItem) {
9460
- return /* @__PURE__ */ jsx11(React5.Fragment, { children: renderItem(contribution, invoke) }, contribution.actionId);
9461
- }
9462
- const Icon = contribution.icon;
9463
- const label = t(contribution.titleKey);
9464
- return /* @__PURE__ */ jsxs4(
9465
- "button",
9466
- {
9467
- type: "button",
9468
- className,
9469
- "aria-label": label,
9470
- title: label,
9471
- onClick: invoke,
9472
- children: [
9473
- Icon && /* @__PURE__ */ jsx11(Icon, { className: "size-4 shrink-0" }),
9474
- !hideLabels && label
9475
- ]
9476
- },
9477
- contribution.actionId
9478
- );
9479
- }) });
9480
- }
9481
- function ComponentSlot({
9482
- slotId,
9483
- props,
9484
- when
9485
- }) {
9486
- const contributions2 = useActionSlot(slotId, when).filter(
9487
- (contribution) => contribution.kind === "component" && contribution.component
9488
- );
9489
- if (contributions2.length === 0) return null;
9490
- return /* @__PURE__ */ jsx11(Fragment2, { children: contributions2.map((contribution) => {
9491
- const Contributed = contribution.component;
9492
- return /* @__PURE__ */ jsx11(Contributed, { ...props ?? {} }, contribution.actionId);
9493
- }) });
9494
- }
9495
-
9496
- // src/ui/shell/tab-handles.ts
9497
- init_define_import_meta_env();
9498
- import { useSyncExternalStore as useSyncExternalStore11 } from "react";
9499
- var version3 = 0;
9500
- var listeners8 = /* @__PURE__ */ new Set();
9501
- var callbacks = /* @__PURE__ */ new WeakMap();
9502
- function subscribe5(listener) {
9503
- listeners8.add(listener);
9504
- return () => listeners8.delete(listener);
9505
- }
9506
- function trackTabHandle(ref) {
9507
- if (!ref) return void 0;
9508
- let callback = callbacks.get(ref);
9509
- if (!callback) {
9510
- callback = (value) => {
9511
- if (ref.current === value) return;
9512
- ref.current = value;
9513
- version3 += 1;
9514
- for (const listener of listeners8) listener();
9515
- };
9516
- callbacks.set(ref, callback);
9517
- }
9518
- return callback;
9519
- }
9520
- function useTabHandlesVersion() {
9521
- return useSyncExternalStore11(
9522
- subscribe5,
9523
- () => version3,
9524
- () => version3
9525
- );
9526
- }
9527
-
9528
- // src/ui/plugin-host/PluginViewPlaceholder.tsx
9529
- init_define_import_meta_env();
9530
- import { useTranslation as useTranslation7 } from "react-i18next";
9531
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
9532
- function PluginViewPlaceholder({
9533
- kind,
9534
- viewId,
9535
- compact = false
9536
- }) {
9537
- const { t } = useTranslation7();
9538
- usePluginStore();
9539
- const { has } = usePermissions();
9540
- const { status, owner } = unregisteredViewStatus(kind, viewId);
9541
- const name = owner ? pluginName(t, owner.summary) : viewId;
9542
- if (status === "loading") {
9543
- return /* @__PURE__ */ jsx12(
9544
- "div",
9545
- {
9546
- className: "flex h-full w-full items-center justify-center",
9547
- "aria-label": t("plugins.runtime.loading", { name }),
9548
- children: /* @__PURE__ */ jsx12("div", { className: "size-5 rounded-full border-2 border-muted-foreground/30 border-t-muted-foreground/70 animate-spin" })
9549
- }
9550
- );
9551
- }
9552
- const title = status === "failed" ? t("plugins.runtime.failed", { name }) : owner ? t("plugins.runtime.needsPlugin", { name }) : t("plugins.runtime.needsUnknownPlugin");
9553
- const hint = status === "failed" ? t("plugins.runtime.failedHint") : status === "disabled" ? t("plugins.runtime.disabledHint") : t("plugins.runtime.missingHint");
9554
- return /* @__PURE__ */ jsxs5(
9555
- "div",
9556
- {
9557
- "data-testid": "plugin-view-placeholder",
9558
- "data-status": status,
9559
- className: `flex h-full w-full flex-col items-center justify-center gap-2 text-center ${compact ? "p-3" : "p-6"}`,
9560
- children: [
9561
- /* @__PURE__ */ jsx12(CircleSlash, { className: "size-6 text-muted-foreground/40" }),
9562
- /* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold text-muted-foreground", children: title }),
9563
- /* @__PURE__ */ jsx12("span", { className: "text-xs text-muted-foreground/70", children: hint }),
9564
- !compact && status !== "failed" && has("admin.plugins.manage") && /* @__PURE__ */ jsx12(
9565
- Button,
9566
- {
9567
- variant: "outline",
9568
- size: "sm",
9569
- className: "mt-1",
9570
- onClick: () => window.dispatchEvent(new CustomEvent(OPEN_PLUGINS_EVENT)),
9571
- children: t("plugins.manager.browsePlugins")
9572
- }
9573
- )
9574
- ]
9575
- }
9576
- );
9577
- }
9578
-
9579
- // src/ui/lib/adaptive-resource-budget.ts
9580
- init_define_import_meta_env();
9581
-
9582
- // src/ui/lib/local-adaptive-engine.ts
9583
- init_define_import_meta_env();
9584
- var LOCAL_ADAPTIVE_ENGINE_KEY = "termix.local.adaptive-engine.v1";
9585
- var VERSION = 1;
9586
- var HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
9587
- var MAX_SCOPES = 128;
9588
- var MAX_ACTIONS_PER_SCOPE = 12;
9589
- var DEFAULT_MIN_EVIDENCE = 2.5;
9590
- var DEFAULT_MIN_SHARE = 0.6;
9591
- var DEFAULT_MIN_MARGIN = 1;
9592
- var EWMA_ALPHA = 0.25;
9593
- function emptyStore() {
9594
- return { version: VERSION, scopes: {} };
9595
- }
9596
- function finiteNonNegative(value) {
9597
- return Number.isFinite(value) && Number(value) >= 0 ? Number(value) : 0;
9598
- }
9599
- function sanitizeAction(value) {
9600
- if (!value || typeof value !== "object") return null;
9601
- const stat = value;
9602
- if (!Number.isFinite(stat.weight) || Number(stat.weight) < 0 || !Number.isFinite(stat.updatedAt)) {
9603
- return null;
9604
- }
9605
- const latencyEwmaMs = finiteNonNegative(stat.latencyEwmaMs);
9606
- return {
9607
- weight: Number(stat.weight),
9608
- updatedAt: Number(stat.updatedAt),
9609
- observations: finiteNonNegative(stat.observations),
9610
- successes: finiteNonNegative(stat.successes),
9611
- cancellations: finiteNonNegative(stat.cancellations),
9612
- fallbacks: finiteNonNegative(stat.fallbacks),
9613
- ...latencyEwmaMs > 0 ? { latencyEwmaMs } : {}
9614
- };
9615
- }
9616
- function sanitizeStore(value) {
9617
- if (!value || typeof value !== "object") return emptyStore();
9618
- const candidate = value;
9619
- if (candidate.version !== VERSION || !candidate.scopes || typeof candidate.scopes !== "object" || Array.isArray(candidate.scopes)) {
9620
- return emptyStore();
9621
- }
9622
- const scopes2 = {};
9623
- for (const [scope, rawScope] of Object.entries(candidate.scopes)) {
9624
- if (!rawScope || typeof rawScope !== "object") continue;
9625
- const value2 = rawScope;
9626
- if (!Number.isFinite(value2.updatedAt) || !value2.actions || typeof value2.actions !== "object" || Array.isArray(value2.actions)) {
9627
- continue;
9628
- }
9629
- const actions4 = {};
9630
- for (const [action, rawStat] of Object.entries(value2.actions)) {
9631
- const stat = sanitizeAction(rawStat);
9632
- if (stat) actions4[action] = stat;
9633
- }
9634
- scopes2[scope] = { actions: actions4, updatedAt: Number(value2.updatedAt) };
9635
- }
9636
- return { version: VERSION, scopes: scopes2 };
9637
- }
9638
- function readStore() {
9639
- if (typeof localStorage === "undefined") return emptyStore();
9640
- try {
9641
- return sanitizeStore(
9642
- JSON.parse(localStorage.getItem(LOCAL_ADAPTIVE_ENGINE_KEY) ?? "null")
9643
- );
9644
- } catch {
9645
- return emptyStore();
9646
- }
9647
- }
9648
- function writeStore(store) {
9649
- if (typeof localStorage === "undefined") return;
9650
- try {
9651
- localStorage.setItem(LOCAL_ADAPTIVE_ENGINE_KEY, JSON.stringify(store));
9652
- } catch {
9653
- }
9654
- }
9655
- function decayedWeight(stat, now) {
9656
- const age = Math.max(0, now - stat.updatedAt);
9657
- return stat.weight * 2 ** (-age / HALF_LIFE_MS);
9658
- }
9659
- function trimStore(store, now) {
9660
- for (const scope of Object.values(store.scopes)) {
9661
- scope.actions = Object.fromEntries(
9662
- Object.entries(scope.actions).sort(
9663
- ([, a], [, b]) => decayedWeight(b, now) - decayedWeight(a, now) || b.updatedAt - a.updatedAt
9664
- ).slice(0, MAX_ACTIONS_PER_SCOPE)
9665
- );
9666
- }
9667
- store.scopes = Object.fromEntries(
9668
- Object.entries(store.scopes).sort(([, a], [, b]) => b.updatedAt - a.updatedAt).slice(0, MAX_SCOPES)
9669
- );
9670
- }
9671
- function updateEwma(previous, value) {
9672
- return previous === void 0 ? value : previous + EWMA_ALPHA * (value - previous);
9673
- }
9674
- function getActionStats(store, scope, action, now) {
9675
- const scopeStats = store.scopes[scope] ?? { actions: {}, updatedAt: now };
9676
- return [
9677
- scopeStats,
9678
- scopeStats.actions[action] ?? {
9679
- weight: 0,
9680
- updatedAt: now,
9681
- observations: 0,
9682
- successes: 0,
9683
- cancellations: 0,
9684
- fallbacks: 0
9685
- }
9686
- ];
9687
- }
9688
- function saveActionStats(store, scope, action, scopeStats, actionStats, now) {
9689
- scopeStats.actions[action] = actionStats;
9690
- scopeStats.updatedAt = now;
9691
- store.scopes[scope] = scopeStats;
9692
- trimStore(store, now);
9693
- writeStore(store);
9694
- }
9695
- function recordLocalAdaptiveChoice(scope, action, now = Date.now()) {
9696
- if (!scope || !action) return;
9697
- const store = readStore();
9698
- const [scopeStats, previous] = getActionStats(store, scope, action, now);
9699
- saveActionStats(
9700
- store,
9701
- scope,
9702
- action,
9703
- scopeStats,
9704
- {
9705
- ...previous,
9706
- weight: decayedWeight(previous, now) + 1,
9707
- updatedAt: now
9708
- },
9709
- now
9710
- );
9711
- }
9712
- function recordLocalAdaptiveOutcome(scope, action, outcome, now = Date.now()) {
9713
- if (!scope || !action || !Object.values(outcome).some((v) => v !== void 0)) {
9714
- return;
9715
- }
9716
- const store = readStore();
9717
- const [scopeStats, previous] = getActionStats(store, scope, action, now);
9718
- const latency = finiteNonNegative(outcome.latencyMs);
9719
- saveActionStats(
9720
- store,
9721
- scope,
9722
- action,
9723
- scopeStats,
9724
- {
9725
- ...previous,
9726
- updatedAt: now,
9727
- observations: previous.observations + 1,
9728
- successes: previous.successes + (outcome.success === true ? 1 : 0),
9729
- cancellations: previous.cancellations + (outcome.cancelled === true ? 1 : 0),
9730
- fallbacks: previous.fallbacks + (outcome.fallback === true ? 1 : 0),
9731
- ...latency > 0 ? { latencyEwmaMs: updateEwma(previous.latencyEwmaMs, latency) } : {}
9732
- },
9733
- now
9734
- );
9735
- }
9736
- function chooseLocalAdaptiveAction({
9737
- scope,
9738
- candidates,
9739
- fallback,
9740
- now = Date.now(),
9741
- ttlMs = 5 * 60 * 1e3,
9742
- minEvidence = DEFAULT_MIN_EVIDENCE,
9743
- minShare = DEFAULT_MIN_SHARE,
9744
- minMargin = DEFAULT_MIN_MARGIN
9745
- }) {
9746
- const expiresAt = now + Math.max(0, ttlMs);
9747
- const stats = readStore().scopes[scope]?.actions;
9748
- if (!stats || candidates.length === 0) {
9749
- return { action: fallback, confidence: 0, reason: "fallback", expiresAt };
9750
- }
9751
- const ranked = candidates.map((action) => ({
9752
- action,
9753
- weight: stats[action] ? decayedWeight(stats[action], now) : 0
9754
- })).sort((a, b) => b.weight - a.weight);
9755
- const total = ranked.reduce((sum, item) => sum + item.weight, 0);
9756
- const [best, second] = ranked;
9757
- if (!best || total <= 0) {
9758
- return { action: fallback, confidence: 0, reason: "fallback", expiresAt };
9759
- }
9760
- const share = best.weight / total;
9761
- const margin = best.weight - (second?.weight ?? 0);
9762
- const confidence = Math.min(
9763
- 1,
9764
- best.weight / Math.max(minEvidence, Number.EPSILON),
9765
- share / Math.max(minShare, Number.EPSILON),
9766
- margin / Math.max(minMargin, Number.EPSILON)
9767
- );
9768
- const learned = best.weight >= minEvidence && share >= minShare && margin >= minMargin;
9769
- return {
9770
- action: learned ? best.action : fallback,
9771
- confidence,
9772
- reason: learned ? "learned" : "insufficient-evidence",
9773
- expiresAt
9774
- };
9775
- }
9776
- function getLocalAdaptiveStats(scope) {
9777
- return readStore().scopes[scope]?.actions ?? {};
9778
- }
9779
- function clearLocalAdaptiveEngine() {
9780
- if (typeof localStorage === "undefined") return;
9781
- try {
9782
- localStorage.removeItem(LOCAL_ADAPTIVE_ENGINE_KEY);
9783
- } catch {
9784
- }
9785
- }
9786
-
9787
- // src/ui/lib/adaptive-resource-budget.ts
9788
- var scopes = {
9789
- module: "resource-budget:module",
9790
- network: "resource-budget:network"
9791
- };
9792
- var usefulnessScopes = {
9793
- module: "resource-usefulness:module",
9794
- network: "resource-usefulness:network"
9795
- };
9796
- var PRELOAD_USE_WINDOW_MS = 1e4;
9797
- var pendingPreloads = /* @__PURE__ */ new Map();
9798
- var activeTasks = {
9799
- module: 0,
9800
- network: 0
9801
- };
9802
- var budgets = {
9803
- paused: {
9804
- tier: "paused",
9805
- allowModulePreload: false,
9806
- allowNetworkPrefetch: false,
9807
- maxPrefetchBytes: 0,
9808
- maxConcurrentModulePreloads: 0,
9809
- maxConcurrentNetworkPrefetches: 0,
9810
- reason: "hidden"
9811
- },
9812
- constrained: {
9813
- tier: "constrained",
9814
- allowModulePreload: false,
9815
- allowNetworkPrefetch: false,
9816
- maxPrefetchBytes: 0,
9817
- maxConcurrentModulePreloads: 0,
9818
- maxConcurrentNetworkPrefetches: 0,
9819
- reason: "environment"
9820
- },
9821
- balanced: {
9822
- tier: "balanced",
9823
- allowModulePreload: true,
9824
- allowNetworkPrefetch: true,
9825
- maxPrefetchBytes: 128 * 1024,
9826
- maxConcurrentModulePreloads: 1,
9827
- maxConcurrentNetworkPrefetches: 1,
9828
- reason: "environment"
9829
- },
9830
- eager: {
9831
- tier: "eager",
9832
- allowModulePreload: true,
9833
- allowNetworkPrefetch: true,
9834
- maxPrefetchBytes: 512 * 1024,
9835
- maxConcurrentModulePreloads: 2,
9836
- maxConcurrentNetworkPrefetches: 2,
9837
- reason: "default"
9838
- }
9839
- };
9840
- function withReason(tier, reason) {
9841
- return { ...budgets[tier], reason };
9842
- }
9843
- function feedbackIsPoor(feedback) {
9844
- if (!feedback || feedback.observations < 4) return false;
9845
- const failureRate = 1 - feedback.successes / feedback.observations;
9846
- const abandoned = feedback.cancellations + feedback.fallbacks;
9847
- return failureRate > 0.25 || abandoned / feedback.observations > 0.25 || (feedback.latencyEwmaMs ?? 0) > 2e3 || (feedback.usefulnessObservations ?? 0) >= 4 && (feedback.usefulPreloads ?? 0) / (feedback.usefulnessObservations ?? 1) < 0.25;
9848
- }
9849
- function preloadKey(kind, action) {
9850
- return `${kind}:${action}`;
9851
- }
9852
- function flushExpiredPreloads(now = Date.now()) {
9853
- for (const [key, preload] of pendingPreloads) {
9854
- if (preload.expiresAt > now) continue;
9855
- recordLocalAdaptiveOutcome(usefulnessScopes[preload.kind], preload.action, {
9856
- success: false
9857
- });
9858
- pendingPreloads.delete(key);
9859
- }
9860
- }
9861
- function markAdaptiveResourceUsed(kind, action, now = Date.now()) {
9862
- flushExpiredPreloads(now);
9863
- const key = preloadKey(kind, action);
9864
- if (!pendingPreloads.has(key)) return;
9865
- pendingPreloads.delete(key);
9866
- recordLocalAdaptiveOutcome(
9867
- usefulnessScopes[kind],
9868
- action,
9869
- { success: true },
9870
- now
9871
- );
9872
- }
9873
- function computeAdaptiveResourceBudget(environment, feedback) {
9874
- if (!environment.visible) return withReason("paused", "hidden");
9875
- if (environment.saveData || ["slow-2g", "2g", "3g"].includes(environment.effectiveType ?? "")) {
9876
- return withReason("constrained", "environment");
9877
- }
9878
- const limitedDevice = (environment.deviceMemoryGb ?? 8) <= 4 || (environment.hardwareConcurrency ?? 8) <= 4;
9879
- const baseline = limitedDevice ? "balanced" : "eager";
9880
- if (!feedbackIsPoor(feedback)) {
9881
- return withReason(baseline, limitedDevice ? "environment" : "default");
9882
- }
9883
- return withReason(
9884
- baseline === "eager" ? "balanced" : "constrained",
9885
- "feedback"
9886
- );
9887
- }
9888
- function readEnvironment() {
9889
- const navigatorLike = typeof navigator === "undefined" ? void 0 : navigator;
9890
- return {
9891
- visible: typeof document === "undefined" || document.visibilityState !== "hidden",
9892
- saveData: navigatorLike?.connection?.saveData,
9893
- effectiveType: navigatorLike?.connection?.effectiveType,
9894
- deviceMemoryGb: navigatorLike?.deviceMemory,
9895
- hardwareConcurrency: navigatorLike?.hardwareConcurrency
9896
- };
9897
- }
9898
- function readFeedback(kind) {
9899
- flushExpiredPreloads();
9900
- const stats = Object.values(getLocalAdaptiveStats(scopes[kind]));
9901
- const usefulness = Object.values(
9902
- getLocalAdaptiveStats(usefulnessScopes[kind])
9903
- );
9904
- const observations = stats.reduce((sum, stat) => sum + stat.observations, 0);
9905
- const latencySamples = stats.filter(
9906
- (stat) => stat.latencyEwmaMs !== void 0 && stat.observations > 0
9907
- );
9908
- const latencyWeight = latencySamples.reduce(
9909
- (sum, stat) => sum + stat.observations,
9910
- 0
9911
- );
9912
- return {
9913
- observations,
9914
- successes: stats.reduce((sum, stat) => sum + stat.successes, 0),
9915
- cancellations: stats.reduce((sum, stat) => sum + stat.cancellations, 0),
9916
- fallbacks: stats.reduce((sum, stat) => sum + stat.fallbacks, 0),
9917
- usefulnessObservations: usefulness.reduce(
9918
- (sum, stat) => sum + stat.observations,
9919
- 0
9920
- ),
9921
- usefulPreloads: usefulness.reduce((sum, stat) => sum + stat.successes, 0),
9922
- ...latencyWeight > 0 ? {
9923
- latencyEwmaMs: latencySamples.reduce(
9924
- (sum, stat) => sum + (stat.latencyEwmaMs ?? 0) * stat.observations,
9925
- 0
9926
- ) / latencyWeight
9927
- } : {}
9928
- };
9929
- }
9930
- function getAdaptiveResourceBudget(kind) {
9931
- return computeAdaptiveResourceBudget(readEnvironment(), readFeedback(kind));
9932
- }
9933
- function runAdaptiveBackgroundTask(kind, action, task, options = {}) {
9934
- const budget = getAdaptiveResourceBudget(kind);
9935
- const allowed = kind === "module" ? budget.allowModulePreload : budget.allowNetworkPrefetch && (options.estimatedBytes === void 0 || options.estimatedBytes <= budget.maxPrefetchBytes);
9936
- const concurrency = kind === "module" ? budget.maxConcurrentModulePreloads : budget.maxConcurrentNetworkPrefetches;
9937
- if (!allowed || activeTasks[kind] >= concurrency) return false;
9938
- activeTasks[kind] += 1;
9939
- const key = preloadKey(kind, action);
9940
- pendingPreloads.set(key, {
9941
- kind,
9942
- action,
9943
- expiresAt: Date.now() + PRELOAD_USE_WINDOW_MS
9944
- });
9945
- const startedAt = performance.now();
9946
- void Promise.resolve().then(task).then(
9947
- () => {
9948
- activeTasks[kind] -= 1;
9949
- recordLocalAdaptiveOutcome(scopes[kind], action, {
9950
- success: true,
9951
- latencyMs: performance.now() - startedAt
9952
- });
9953
- },
9954
- () => {
9955
- activeTasks[kind] -= 1;
9956
- pendingPreloads.delete(key);
9957
- recordLocalAdaptiveOutcome(scopes[kind], action, {
9958
- success: false,
9959
- latencyMs: performance.now() - startedAt
9960
- });
9961
- }
9962
- );
9963
- return true;
9964
- }
9965
-
9966
- // src/ui/shell/tabUtils.tsx
9967
- init_define_import_meta_env();
9968
- import { lazy, memo as memo2, Suspense } from "react";
9969
- import { useTranslation as useTranslation9 } from "react-i18next";
9970
-
9971
8960
  // src/ui/lib/host-to-ssh-host.ts
9972
8961
  init_define_import_meta_env();
9973
8962
  function hostToSSHHost(h) {
@@ -10004,446 +8993,7 @@ function hostToSSHHost(h) {
10004
8993
  };
10005
8994
  }
10006
8995
 
10007
- // src/ui/shell/CompanionFrame.tsx
10008
- init_define_import_meta_env();
10009
- import {
10010
- memo,
10011
- useCallback as useCallback9,
10012
- useEffect as useEffect18,
10013
- useMemo as useMemo10,
10014
- useRef as useRef8,
10015
- useState as useState16
10016
- } from "react";
10017
- import { useTranslation as useTranslation8 } from "react-i18next";
10018
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
10019
- var DEFAULT_SIDE_SIZE = 360;
10020
- var DEFAULT_BOTTOM_SIZE = 280;
10021
- var MIN_SIZE = 200;
10022
- function DefaultsReader({
10023
- def,
10024
- onValue
10025
- }) {
10026
- const Reader = def.Defaults;
10027
- const report = useCallback9(
10028
- (value) => onValue(def.id, value),
10029
- [def.id, onValue]
10030
- );
10031
- return Reader ? /* @__PURE__ */ jsx13(Reader, { onValue: report }) : null;
10032
- }
10033
- function sideIcon(side) {
10034
- if (side === "left") return ArrowLeftToLine;
10035
- if (side === "bottom") return ArrowDownToLine;
10036
- return ArrowRightToLine;
10037
- }
10038
- var CompanionFrame = memo(function CompanionFrame2({
10039
- tab,
10040
- isVisible,
10041
- children
10042
- }) {
10043
- const defs = useCompanionDefs();
10044
- const layoutDefs = useCompanionLayoutDefs();
10045
- const applicable = useMemo10(
10046
- () => arrangeCompanions(
10047
- defs.filter((def) => companionApplies(def, tab)),
10048
- resolveCompanionLayout(tab.type, tab.host).order
10049
- ),
10050
- // tab.host and tab.type decide it
10051
- // eslint-disable-next-line react-hooks/exhaustive-deps
10052
- [defs, layoutDefs, tab.type, tab.host]
10053
- );
10054
- if (applicable.length === 0) return /* @__PURE__ */ jsx13(Fragment3, { children });
10055
- return /* @__PURE__ */ jsx13(CompanionLayout, { tab, defs: applicable, isVisible, children });
10056
- });
10057
- function CompanionLayout({
10058
- tab,
10059
- defs,
10060
- isVisible,
10061
- children
10062
- }) {
10063
- const { t } = useTranslation8();
10064
- const isMobile = useIsMobile();
10065
- const key = companionKey(tab);
10066
- const state2 = useCompanionState(key);
10067
- const [defaults3, setDefaults] = useState16(
10068
- {}
10069
- );
10070
- const [dragging, setDragging] = useState16(false);
10071
- const [mounted, setMounted] = useState16(/* @__PURE__ */ new Set());
10072
- const rootRef = useRef8(null);
10073
- const onDefaults = useCallback9((id, value) => {
10074
- setDefaults((prev) => {
10075
- const old = prev[id];
10076
- if (old && old.open === value.open && old.side === value.side && old.size === value.size && old.edgeButton === value.edgeButton) {
10077
- return prev;
10078
- }
10079
- return { ...prev, [id]: value };
10080
- });
10081
- }, []);
10082
- useEffect18(() => {
10083
- if (state2.seen || !isVisible) return;
10084
- const ready = defs.every(
10085
- (def) => !def.Defaults || defaults3[def.id] !== void 0
10086
- );
10087
- if (!ready) return;
10088
- const auto = defs.find((def) => defaults3[def.id]?.open);
10089
- patchCompanionState(key, { seen: true, openId: auto?.id ?? null });
10090
- }, [state2.seen, isVisible, defs, defaults3, key]);
10091
- const active = defs.find((def) => def.id === state2.openId) ?? null;
10092
- useEffect18(() => {
10093
- if (!active) return;
10094
- setMounted(
10095
- (prev) => prev.has(active.id) ? prev : new Set(prev).add(active.id)
10096
- );
10097
- }, [active]);
10098
- const activeDefaults = active ? defaults3[active.id] : void 0;
10099
- const layout = useMemo10(
10100
- () => resolveCompanionLayout(tab.type, tab.host),
10101
- [tab.type, tab.host]
10102
- );
10103
- const side = isMobile ? "bottom" : state2.side ?? layout.side ?? activeDefaults?.side ?? "right";
10104
- const baseSize = state2.size ?? activeDefaults?.size ?? (side === "bottom" ? DEFAULT_BOTTOM_SIZE : DEFAULT_SIDE_SIZE);
10105
- const setSide = useCallback9(
10106
- (next) => patchCompanionState(key, { side: next }),
10107
- [key]
10108
- );
10109
- const close = useCallback9(
10110
- () => patchCompanionState(key, { openId: null }),
10111
- [key]
10112
- );
10113
- const startResize = (event) => {
10114
- event.preventDefault();
10115
- const root = rootRef.current;
10116
- if (!root) return;
10117
- const rect = root.getBoundingClientRect();
10118
- const total = side === "bottom" ? rect.height : rect.width;
10119
- const max = Math.max(MIN_SIZE, total - MIN_SIZE);
10120
- setDragging(true);
10121
- const move = (e) => {
10122
- const raw = side === "right" ? rect.right - e.clientX : side === "left" ? e.clientX - rect.left : rect.bottom - e.clientY;
10123
- patchCompanionState(key, {
10124
- size: Math.round(Math.min(max, Math.max(MIN_SIZE, raw)))
10125
- });
10126
- };
10127
- const up = () => {
10128
- setDragging(false);
10129
- window.removeEventListener("mousemove", move);
10130
- window.removeEventListener("mouseup", up);
10131
- };
10132
- window.addEventListener("mousemove", move);
10133
- window.addEventListener("mouseup", up);
10134
- };
10135
- const getHandle = useCallback9(
10136
- () => tab.terminalRef?.current ?? null,
10137
- [tab.terminalRef]
10138
- );
10139
- const sshHost = useMemo10(
10140
- () => tab.host ? hostToSSHHost(tab.host) : void 0,
10141
- [tab.host]
10142
- );
10143
- const showEdge = defs.filter((def) => defaults3[def.id]?.edgeButton !== false);
10144
- const vertical = side === "bottom";
10145
- const clamped = vertical ? Math.min(baseSize, 2e3) : baseSize;
10146
- const paneStyle = isMobile ? { height: "55%" } : vertical ? { height: clamped } : { width: clamped };
10147
- const SideIcon = sideIcon(side);
10148
- return /* @__PURE__ */ jsxs6("div", { className: "flex h-full w-full min-h-0 min-w-0", children: [
10149
- defs.map(
10150
- (def) => def.Defaults ? /* @__PURE__ */ jsx13(DefaultsReader, { def, onValue: onDefaults }, def.id) : null
10151
- ),
10152
- /* @__PURE__ */ jsxs6(
10153
- "div",
10154
- {
10155
- ref: rootRef,
10156
- className: cn(
10157
- "relative flex flex-1 min-h-0 min-w-0",
10158
- vertical ? "flex-col" : side === "left" ? "flex-row-reverse" : "flex-row"
10159
- ),
10160
- children: [
10161
- /* @__PURE__ */ jsx13("div", { className: "relative flex-1 min-h-0 min-w-0", children }),
10162
- defs.map((def) => {
10163
- if (!mounted.has(def.id)) return null;
10164
- const open = active?.id === def.id;
10165
- const Component = def.component;
10166
- const Icon = def.icon;
10167
- return /* @__PURE__ */ jsxs6(
10168
- "div",
10169
- {
10170
- className: cn(
10171
- "relative flex-shrink-0 flex-col min-h-0 min-w-0 bg-background border-border",
10172
- open ? "flex" : "hidden",
10173
- vertical ? "border-t" : side === "left" ? "border-r" : "border-l",
10174
- isMobile && "absolute inset-x-0 bottom-0 z-20 shadow-lg"
10175
- ),
10176
- style: paneStyle,
10177
- children: [
10178
- !isMobile && /* @__PURE__ */ jsx13(
10179
- "div",
10180
- {
10181
- role: "separator",
10182
- "aria-label": t("companion.resize"),
10183
- onMouseDown: startResize,
10184
- className: cn(
10185
- "absolute z-10",
10186
- vertical ? "inset-x-0 -top-1 h-2 cursor-row-resize" : side === "left" ? "inset-y-0 -right-1 w-2 cursor-col-resize" : "inset-y-0 -left-1 w-2 cursor-col-resize",
10187
- dragging && "bg-primary/30"
10188
- )
10189
- }
10190
- ),
10191
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 h-8 px-2 border-b border-border flex-shrink-0", children: [
10192
- Icon && /* @__PURE__ */ jsx13(Icon, { className: "size-3.5 text-muted-foreground" }),
10193
- /* @__PURE__ */ jsx13("span", { className: "text-xs font-medium truncate flex-1", children: def.titleKey ? t(def.titleKey) : def.id }),
10194
- !isMobile && /* @__PURE__ */ jsx13(
10195
- "button",
10196
- {
10197
- type: "button",
10198
- className: "p-1 text-muted-foreground hover:text-foreground",
10199
- title: t("companion.moveSide"),
10200
- onClick: () => setSide(
10201
- side === "right" ? "bottom" : side === "bottom" ? "left" : "right"
10202
- ),
10203
- children: /* @__PURE__ */ jsx13(SideIcon, { className: "size-3.5" })
10204
- }
10205
- ),
10206
- /* @__PURE__ */ jsx13(
10207
- "button",
10208
- {
10209
- type: "button",
10210
- className: "p-1 text-muted-foreground hover:text-foreground",
10211
- title: t("companion.close"),
10212
- onClick: close,
10213
- children: /* @__PURE__ */ jsx13(X, { className: "size-3.5" })
10214
- }
10215
- )
10216
- ] }),
10217
- /* @__PURE__ */ jsx13("div", { className: "flex-1 min-h-0 min-w-0 overflow-hidden", children: /* @__PURE__ */ jsx13(
10218
- Component,
10219
- {
10220
- tab,
10221
- host: tab.host,
10222
- sshHost,
10223
- getHandle,
10224
- side,
10225
- isVisible: isVisible && open,
10226
- close,
10227
- setSide
10228
- }
10229
- ) })
10230
- ]
10231
- },
10232
- def.id
10233
- );
10234
- })
10235
- ]
10236
- }
10237
- ),
10238
- showEdge.length > 0 && !isMobile && /* @__PURE__ */ jsx13("div", { className: "flex flex-col flex-shrink-0 w-6 items-center gap-1 pt-1 border-l border-border bg-background", children: showEdge.map((def) => {
10239
- const Icon = def.icon ?? PanelRight;
10240
- const open = active?.id === def.id;
10241
- return /* @__PURE__ */ jsx13(
10242
- "button",
10243
- {
10244
- type: "button",
10245
- title: def.titleKey ? t(def.titleKey) : def.id,
10246
- "aria-pressed": open,
10247
- onClick: () => patchCompanionState(key, {
10248
- seen: true,
10249
- openId: open ? null : def.id
10250
- }),
10251
- className: cn(
10252
- "p-1 text-muted-foreground hover:text-foreground",
10253
- open && "text-foreground bg-accent"
10254
- ),
10255
- children: /* @__PURE__ */ jsx13(Icon, { className: "size-3.5" })
10256
- },
10257
- def.id
10258
- );
10259
- }) })
10260
- ] });
10261
- }
10262
-
10263
- // src/ui/shell/tabUtils.tsx
10264
- import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
10265
- var PluginsTab = lazy(
10266
- () => import("./PluginsTab-ZSMTJ5C5.js").then((m) => ({ default: m.PluginsTab }))
10267
- );
10268
- function preloadTabSurface(type) {
10269
- const loader = getTabType(type)?.preload;
10270
- if (loader) runAdaptiveBackgroundTask("module", `tab:${type}`, loader);
10271
- }
10272
- function markTabSurfaceUsed(type) {
10273
- markAdaptiveResourceUsed("module", `tab:${type}`);
10274
- }
10275
- function EmptyState({
10276
- icon: Icon,
10277
- messageKey
10278
- }) {
10279
- const { t } = useTranslation9();
10280
- return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-center justify-center flex-1 gap-3 p-6 text-center", children: [
10281
- /* @__PURE__ */ jsx14("div", { className: "size-10 rounded-full bg-muted/40 flex items-center justify-center", children: /* @__PURE__ */ jsx14(Icon, { className: "size-5 text-muted-foreground/30" }) }),
10282
- /* @__PURE__ */ jsx14("span", { className: "text-sm font-semibold text-muted-foreground/60", children: t(messageKey) })
10283
- ] });
10284
- }
10285
- function TabChunkFallback() {
10286
- return /* @__PURE__ */ jsx14("div", { className: "flex h-full w-full items-center justify-center bg-background", children: /* @__PURE__ */ jsx14("div", { className: "size-5 rounded-full border-2 border-muted-foreground/30 border-t-muted-foreground/70 animate-spin" }) });
10287
- }
10288
- function withTabSuspense(node) {
10289
- return /* @__PURE__ */ jsx14(Suspense, { fallback: /* @__PURE__ */ jsx14(TabChunkFallback, {}), children: node });
10290
- }
10291
- function PanelTabFrame({ children }) {
10292
- return /* @__PURE__ */ jsx14("div", { className: "flex h-full w-full justify-center overflow-y-auto bg-background", children: /* @__PURE__ */ jsx14("div", { className: "flex flex-col flex-1 min-h-0 w-full max-w-5xl", children }) });
10293
- }
10294
- function tabIcon(type) {
10295
- switch (type) {
10296
- case "user-profile":
10297
- case "admin-settings":
10298
- return /* @__PURE__ */ jsx14(Settings, { className: "size-3.5" });
10299
- case "split-screen":
10300
- return /* @__PURE__ */ jsx14(LayoutPanelLeft, { className: "size-3.5" });
10301
- case "plugins":
10302
- return /* @__PURE__ */ jsx14(Puzzle, { className: "size-3.5" });
10303
- default: {
10304
- const Icon = getTabType(type)?.icon;
10305
- return Icon ? /* @__PURE__ */ jsx14(Icon, { className: "size-3.5" }) : null;
10306
- }
10307
- }
10308
- }
10309
- var RegisteredTab = memo2(function RegisteredTab2({
10310
- def,
10311
- tab,
10312
- isVisible,
10313
- isFocusedPane,
10314
- inSplit,
10315
- shell: shell2
10316
- }) {
10317
- const { host, label } = tab;
10318
- if (def.requiresHost && !host) {
10319
- return /* @__PURE__ */ jsx14(
10320
- EmptyState,
10321
- {
10322
- icon: Server,
10323
- messageKey: def.noHostMessageKey ?? "hosts.noHostSelected"
10324
- }
10325
- );
10326
- }
10327
- const Component = def.component;
10328
- const content = /* @__PURE__ */ jsx14(
10329
- Component,
10330
- {
10331
- tab,
10332
- host,
10333
- sshHost: host ? hostToSSHHost(host) : void 0,
10334
- label,
10335
- isVisible,
10336
- isFocusedPane,
10337
- inSplit,
10338
- handleRef: trackTabHandle(
10339
- tab.terminalRef
10340
- ),
10341
- shell: shell2
10342
- }
10343
- );
10344
- return withTabSuspense(
10345
- /* @__PURE__ */ jsx14(CompanionFrame, { tab, isVisible, children: def.panelFrame ? /* @__PURE__ */ jsx14(PanelTabFrame, { children: content }) : content })
10346
- );
10347
- });
10348
- var PanelAsTab = memo2(function PanelAsTab2({
10349
- panel,
10350
- isVisible,
10351
- targetTab,
10352
- shell: shell2
10353
- }) {
10354
- const Panel = panel.component;
10355
- return withTabSuspense(
10356
- /* @__PURE__ */ jsx14(PanelTabFrame, { children: /* @__PURE__ */ jsx14(
10357
- Panel,
10358
- {
10359
- targetTab,
10360
- active: isVisible,
10361
- shell: shell2,
10362
- setEditing: noop,
10363
- placement: "tab"
10364
- }
10365
- ) })
10366
- );
10367
- });
10368
- function noop() {
10369
- }
10370
- function renderTabContent(tab, context) {
10371
- const {
10372
- shell: shell2,
10373
- isVisible = true,
10374
- isFocusedPane = true,
10375
- inSplit = false
10376
- } = context;
10377
- switch (tab.type) {
10378
- case "split-screen":
10379
- return null;
10380
- case "plugins":
10381
- return withTabSuspense(/* @__PURE__ */ jsx14(PluginsTab, {}));
10382
- case "user-profile":
10383
- case "admin-settings":
10384
- return null;
10385
- default: {
10386
- const def = getTabType(tab.type);
10387
- if (!def && tab.type === "dashboard") return null;
10388
- if (!def) {
10389
- const panel = getPanel(tab.type);
10390
- if (panel) {
10391
- return /* @__PURE__ */ jsx14(
10392
- PanelAsTab,
10393
- {
10394
- panel,
10395
- isVisible,
10396
- targetTab: context.panelTargetTab,
10397
- shell: shell2
10398
- }
10399
- );
10400
- }
10401
- return /* @__PURE__ */ jsx14(PluginViewPlaceholder, { kind: "tab", viewId: tab.type });
10402
- }
10403
- return /* @__PURE__ */ jsx14(
10404
- RegisteredTab,
10405
- {
10406
- def,
10407
- tab,
10408
- isVisible,
10409
- isFocusedPane,
10410
- inSplit,
10411
- shell: shell2
10412
- }
10413
- );
10414
- }
10415
- }
10416
- }
10417
-
10418
- // src/ui/lib/local-adaptive-preferences.ts
10419
- init_define_import_meta_env();
10420
- var LOCAL_ADAPTIVE_PREFERENCES_KEY = LOCAL_ADAPTIVE_ENGINE_KEY;
10421
- function recordLocalPreference(scope, action, now = Date.now()) {
10422
- recordLocalAdaptiveChoice(scope, action, now);
10423
- }
10424
- function getLocalPreference(scope, candidates, fallback, now = Date.now()) {
10425
- return chooseLocalAdaptiveAction({
10426
- scope,
10427
- candidates,
10428
- fallback,
10429
- now
10430
- }).action;
10431
- }
10432
- function clearLocalAdaptivePreferences() {
10433
- clearLocalAdaptiveEngine();
10434
- }
10435
- var hostActionScope = (hostId) => `host-action:${hostId}`;
10436
- function recordHostActionPreference(hostId, action) {
10437
- recordLocalPreference(hostActionScope(hostId), action);
10438
- }
10439
- function getPreferredHostAction(hostId, candidates, fallback) {
10440
- return getLocalPreference(hostActionScope(hostId), candidates, fallback);
10441
- }
10442
-
10443
8996
  export {
10444
- Checkbox2 as Checkbox,
10445
- PasswordInput,
10446
- Textarea,
10447
8997
  rem,
10448
8998
  remScale,
10449
8999
  useIsMobile,
@@ -10482,6 +9032,14 @@ export {
10482
9032
  isCoreRailView,
10483
9033
  railItemLabel,
10484
9034
  resetRegisteredRailItems,
9035
+ useCompanionDefs,
9036
+ useCompanionLayoutDefs,
9037
+ resolveCompanionLayout,
9038
+ companionApplies,
9039
+ arrangeCompanions,
9040
+ companionKey,
9041
+ patchCompanionState,
9042
+ useCompanionState,
10485
9043
  registerHostEditorSection,
10486
9044
  unregisterHostEditorSection,
10487
9045
  getHostEditorSection,
@@ -10535,8 +9093,10 @@ export {
10535
9093
  listExtensions,
10536
9094
  getSettingsComponent,
10537
9095
  listSettingsComponents,
9096
+ useSettingsComponents,
10538
9097
  registerAction,
10539
9098
  listActions,
9099
+ invokeAction,
10540
9100
  declareActionSlot,
10541
9101
  registerSlotContribution,
10542
9102
  getSlotContributions,
@@ -10621,25 +9181,5 @@ export {
10621
9181
  useLayouts,
10622
9182
  layoutLabel,
10623
9183
  createPluginApp,
10624
- useInstalledPlugins,
10625
- SettingsFieldRow,
10626
- usePluginHostSections,
10627
- HostPluginSections,
10628
- HostFeatureFields,
10629
- ActionSlot,
10630
- ComponentSlot,
10631
- trackTabHandle,
10632
- useTabHandlesVersion,
10633
- PluginViewPlaceholder,
10634
- markAdaptiveResourceUsed,
10635
- getAdaptiveResourceBudget,
10636
- runAdaptiveBackgroundTask,
10637
- preloadTabSurface,
10638
- markTabSurfaceUsed,
10639
- tabIcon,
10640
- renderTabContent,
10641
- LOCAL_ADAPTIVE_PREFERENCES_KEY,
10642
- clearLocalAdaptivePreferences,
10643
- recordHostActionPreference,
10644
- getPreferredHostAction
9184
+ hostToSSHHost
10645
9185
  };