@termix-ssh/plugin-sdk 1.0.21 → 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 (39) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/PLUGIN-GUIDE.md +62 -3
  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-HQGSDSHS.js → PluginsTab-CM2B33BD.js} +22 -14
  8. package/dist/host/chunk-7J4QNGFM.js +345 -0
  9. package/dist/host/{chunk-A5CULDWV.js → chunk-HQ6NNFSX.js} +109 -1574
  10. package/dist/host/chunk-I5F2PSMR.js +1526 -0
  11. package/dist/host/{chunk-FGX27MAA.js → chunk-JRV2HIDK.js} +1 -1
  12. package/dist/host/{chunk-JFQGI4OD.js → chunk-P26QKKU5.js} +38 -9
  13. package/dist/host/{chunk-WWFRBU2E.js → chunk-ROB2SH5Q.js} +76 -49
  14. package/dist/host/chunk-TK3Y437H.js +53 -0
  15. package/dist/host/{chunk-GTHINW3L.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-UDSFY3N7.js → linked-server-J5G457OA.js} +1 -1
  20. package/dist/host/{rbac-api-AAAPMF52.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/plugins-api.d.ts +2 -1
  25. package/dist/host/types/src/ui/api/rbac-api.d.ts +19 -1
  26. package/dist/host/types/src/ui/components/data-view.d.ts +1 -1
  27. package/dist/host/types/src/ui/main-axios.d.ts +1 -1
  28. package/dist/host/types/src/ui/plugin-host/loader.d.ts +6 -0
  29. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -1
  30. package/dist/host/types/src/ui/plugins/use-plugins-manager.d.ts +1 -1
  31. package/dist/host/types/src/ui/shell/tab-registry.d.ts +2 -0
  32. package/dist/host/ui.js +356 -369
  33. package/dist/host/{use-permissions-VAND5GEG.js → use-permissions-EWRI3WLT.js} +1 -1
  34. package/dist/host-commands.d.ts +7 -0
  35. package/dist/host-commands.js +35 -5
  36. package/dist/manifest.d.ts +28 -1
  37. package/dist/manifest.js +108 -1
  38. package/package.json +5 -1
  39. package/schema/manifest.schema.json +42 -0
@@ -3,65 +3,33 @@ import {
3
3
  hasPermission,
4
4
  resetPermissionsCache,
5
5
  usePermissions
6
- } from "./chunk-FGX27MAA.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-GTHINW3L.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-JFQGI4OD.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;
@@ -1499,6 +1386,8 @@ var en_default = {
1499
1386
  closeOnOpenDesc: "Close the sidebar after you open a host from it",
1500
1387
  showFolderPaths: "Show folder paths",
1501
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",
1502
1391
  rowDetailsTitle: "Row details",
1503
1392
  rowDetailsDesc: "Choose what shows on each host.",
1504
1393
  showAddress: "Address",
@@ -1684,6 +1573,8 @@ var en_default = {
1684
1573
  sharedBadge: "Shared",
1685
1574
  sharedBadgeTooltip: "Shared by {{owner}} ({{level}} access)",
1686
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.",
1687
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.",
1688
1579
  ownerOnlyControl: "Only the host owner can change this",
1689
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.",
@@ -2862,7 +2753,7 @@ function resetRegisteredRailItems() {
2862
2753
 
2863
2754
  // src/ui/sidebar/HostManagerTabs.tsx
2864
2755
  init_define_import_meta_env();
2865
- import { jsx as jsx4 } from "react/jsx-runtime";
2756
+ import { jsx } from "react/jsx-runtime";
2866
2757
  var sections = createRegistry(byOrderThenId);
2867
2758
  var registerHostEditorSection = sections.register;
2868
2759
  var unregisterHostEditorSection = sections.unregister;
@@ -2886,7 +2777,7 @@ function mergeByOrder(core, group, t, protocols2, defaultsMode = false) {
2886
2777
  return {
2887
2778
  id: section.id,
2888
2779
  label: t(section.labelKey, { defaultValue: section.label }),
2889
- icon: Icon ? /* @__PURE__ */ jsx4(Icon, { className: "size-3" }) : null,
2780
+ icon: Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-3" }) : null,
2890
2781
  order: section.order ?? 1e3
2891
2782
  };
2892
2783
  });
@@ -2898,13 +2789,13 @@ function makeHostTabs(t, protocols2, defaultsMode = false) {
2898
2789
  {
2899
2790
  id: "general",
2900
2791
  label: t("hosts.tabGeneral"),
2901
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2792
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2902
2793
  order: 0
2903
2794
  },
2904
2795
  {
2905
2796
  id: "ssh",
2906
2797
  label: t("hosts.tabSsh"),
2907
- icon: /* @__PURE__ */ jsx4(Terminal, { className: "size-3" }),
2798
+ icon: /* @__PURE__ */ jsx(Terminal, { className: "size-3" }),
2908
2799
  order: 10
2909
2800
  }
2910
2801
  ],
@@ -2920,7 +2811,7 @@ function makeHostSshSubTabs(t, protocols2, defaultsMode = false) {
2920
2811
  {
2921
2812
  id: "ssh",
2922
2813
  label: t("hosts.tabGeneral"),
2923
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2814
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2924
2815
  order: 0
2925
2816
  }
2926
2817
  ],
@@ -3394,7 +3285,7 @@ async function pluginWsUrl(pluginId, path2, options = {}) {
3394
3285
  }
3395
3286
  const token = await fetchSocketTicket() ?? localStorage.getItem("jwt");
3396
3287
  const protocols2 = websocketAuthProtocols(token);
3397
- const devProxy = !define_import_meta_env_default.VITE_API_HOST;
3288
+ const devProxy = false;
3398
3289
  const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
3399
3290
  if (devProxy) {
3400
3291
  return {
@@ -3470,7 +3361,7 @@ function sectionPosition(section, order2 = 0, position) {
3470
3361
 
3471
3362
  // src/ui/hooks/use-status-color-scheme.ts
3472
3363
  init_define_import_meta_env();
3473
- import { useState as useState3, useEffect as useEffect2 } from "react";
3364
+ import { useState as useState2, useEffect as useEffect2 } from "react";
3474
3365
  var PREFS_KEY = "hostSidebarPreferences";
3475
3366
  var PREFS_EVENT = "hostSidebarPreferencesChanged";
3476
3367
  function readStatusColorScheme() {
@@ -3484,7 +3375,7 @@ function readStatusColorScheme() {
3484
3375
  }
3485
3376
  }
3486
3377
  function useStatusColorScheme() {
3487
- const [scheme, setScheme] = useState3(
3378
+ const [scheme, setScheme] = useState2(
3488
3379
  readStatusColorScheme
3489
3380
  );
3490
3381
  useEffect2(() => {
@@ -3524,10 +3415,6 @@ function getStatusClasses(status, scheme, variant, loading = false) {
3524
3415
  return online ? "border-accent-brand/40 text-accent-brand bg-accent-brand/10" : "border-border/50 text-muted-foreground/60 bg-muted/30";
3525
3416
  }
3526
3417
 
3527
- // src/ui/components/theme-provider.tsx
3528
- init_define_import_meta_env();
3529
- import { createContext, useContext, useEffect as useEffect3, useState as useState4 } from "react";
3530
-
3531
3418
  // src/ui/look/theme-registry.ts
3532
3419
  init_define_import_meta_env();
3533
3420
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
@@ -3611,6 +3498,10 @@ function arePluginThemesKnown() {
3611
3498
  return pluginThemesKnown;
3612
3499
  }
3613
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
+
3614
3505
  // src/ui/look/theme-css.ts
3615
3506
  init_define_import_meta_env();
3616
3507
  var LINK_ID = "termix-theme-css";
@@ -3661,7 +3552,7 @@ function applyThemeCss(id, theme) {
3661
3552
  }
3662
3553
 
3663
3554
  // src/ui/components/theme-provider.tsx
3664
- import { jsx as jsx5 } from "react/jsx-runtime";
3555
+ import { jsx as jsx2 } from "react/jsx-runtime";
3665
3556
  var initialState = {
3666
3557
  theme: "dark",
3667
3558
  setTheme: () => null,
@@ -3675,10 +3566,10 @@ function ThemeProvider({
3675
3566
  storageKey = "vite-ui-theme",
3676
3567
  ...props
3677
3568
  }) {
3678
- const [theme, setTheme] = useState4(
3569
+ const [theme, setTheme] = useState3(
3679
3570
  () => localStorage.getItem(storageKey) || defaultTheme
3680
3571
  );
3681
- const [previewTheme, setPreviewTheme] = useState4(null);
3572
+ const [previewTheme, setPreviewTheme] = useState3(null);
3682
3573
  const themes = useThemes();
3683
3574
  const shownTheme = effectiveThemeId(theme);
3684
3575
  const activeTheme = previewTheme ?? shownTheme;
@@ -3701,7 +3592,7 @@ function ThemeProvider({
3701
3592
  setPreviewTheme(preview);
3702
3593
  }
3703
3594
  };
3704
- return /* @__PURE__ */ jsx5(ThemeProviderContext.Provider, { ...props, value, children });
3595
+ return /* @__PURE__ */ jsx2(ThemeProviderContext.Provider, { ...props, value, children });
3705
3596
  }
3706
3597
  var useTheme = () => {
3707
3598
  const context = useContext(ThemeProviderContext);
@@ -3712,7 +3603,7 @@ var useTheme = () => {
3712
3603
 
3713
3604
  // src/ui/hooks/useSshAuthProviders.ts
3714
3605
  init_define_import_meta_env();
3715
- 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";
3716
3607
  var BUILTIN_TYPES = ["password", "key", "credential", "agent", "none"];
3717
3608
  function builtinSummary(type) {
3718
3609
  return {
@@ -3730,7 +3621,7 @@ function builtinSummary(type) {
3730
3621
  var cache = null;
3731
3622
  function useSshAuthProviders() {
3732
3623
  const editors = useSshAuthEditors();
3733
- const [summaries, setSummaries] = useState5(
3624
+ const [summaries, setSummaries] = useState4(
3734
3625
  null
3735
3626
  );
3736
3627
  useEffect4(() => {
@@ -3812,14 +3703,14 @@ import {
3812
3703
  useContext as useContext2,
3813
3704
  useEffect as useEffect5,
3814
3705
  useMemo as useMemo4,
3815
- useState as useState6
3706
+ useState as useState5
3816
3707
  } from "react";
3817
3708
 
3818
3709
  // src/ui/contexts/branding-document.ts
3819
3710
  init_define_import_meta_env();
3820
3711
 
3821
3712
  // src/ui/contexts/BrandingContext.tsx
3822
- import { jsx as jsx6 } from "react/jsx-runtime";
3713
+ import { jsx as jsx3 } from "react/jsx-runtime";
3823
3714
  var DEFAULT_APP_NAME = "Termix";
3824
3715
  var BrandingContext = createContext2({
3825
3716
  ready: false,
@@ -5210,7 +5101,7 @@ import {
5210
5101
  useLayoutEffect as useLayoutEffect2,
5211
5102
  useMemo as useMemo5,
5212
5103
  useRef as useRef6,
5213
- useState as useState12
5104
+ useState as useState11
5214
5105
  } from "react";
5215
5106
 
5216
5107
  // src/ui/shell/stage/tab-stage-store.ts
@@ -5218,14 +5109,14 @@ init_define_import_meta_env();
5218
5109
  function createTabStageStore() {
5219
5110
  const nodes = /* @__PURE__ */ new Map();
5220
5111
  const paneEls = /* @__PURE__ */ new Map();
5221
- const listeners9 = /* @__PURE__ */ new Set();
5112
+ const listeners8 = /* @__PURE__ */ new Set();
5222
5113
  let normalView = null;
5223
- let version4 = 0;
5114
+ let version3 = 0;
5224
5115
  let order2 = [];
5225
5116
  let lastAnimatedTabId = null;
5226
5117
  const bump = () => {
5227
- version4 += 1;
5228
- for (const listener of listeners9) listener();
5118
+ version3 += 1;
5119
+ for (const listener of listeners8) listener();
5229
5120
  };
5230
5121
  function getNode(tabId, ownBackground) {
5231
5122
  let el = nodes.get(tabId);
@@ -5327,18 +5218,18 @@ function createTabStageStore() {
5327
5218
  setNormalView,
5328
5219
  place,
5329
5220
  subscribe: (listener) => {
5330
- listeners9.add(listener);
5221
+ listeners8.add(listener);
5331
5222
  return () => {
5332
- listeners9.delete(listener);
5223
+ listeners8.delete(listener);
5333
5224
  };
5334
5225
  },
5335
- version: () => version4
5226
+ version: () => version3
5336
5227
  };
5337
5228
  }
5338
5229
 
5339
5230
  // src/ui/shell/controller/use-regions.ts
5340
5231
  init_define_import_meta_env();
5341
- 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";
5342
5233
  var MIN_WIDTH = 160;
5343
5234
  var MAX_WIDTH = 480;
5344
5235
  function clampWidth(width) {
@@ -5371,15 +5262,15 @@ function startDockDrag(e, startW, sign, setWidth, setDragging) {
5371
5262
  }
5372
5263
  var DOCK_DEFAULT_WIDTH = 291;
5373
5264
  function useRegions(isMobile) {
5374
- const [sidebarOpen, setSidebarOpen] = useState7(true);
5375
- const [railView, setRailView] = useState7("hosts");
5376
- const [sidebarWidth, setSidebarWidth] = useState7(() => {
5265
+ const [sidebarOpen, setSidebarOpen] = useState6(true);
5266
+ const [railView, setRailView] = useState6("hosts");
5267
+ const [sidebarWidth, setSidebarWidth] = useState6(() => {
5377
5268
  const saved = localStorage.getItem("termix_sidebarWidth");
5378
5269
  return saved ? parseInt(saved, 10) : 291;
5379
5270
  });
5380
- const [sidebarDragging, setSidebarDragging] = useState7(false);
5381
- const [panelEditing, setPanelEditing] = useState7({});
5382
- const [scopeEditing, setScopeEditing] = useState7({});
5271
+ const [sidebarDragging, setSidebarDragging] = useState6(false);
5272
+ const [panelEditing, setPanelEditing] = useState6({});
5273
+ const [scopeEditing, setScopeEditing] = useState6({});
5383
5274
  const editingSetters = useRef(
5384
5275
  /* @__PURE__ */ new Map()
5385
5276
  );
@@ -5405,15 +5296,15 @@ function useRegions(isMobile) {
5405
5296
  );
5406
5297
  return editingSetters.current.get(key);
5407
5298
  }, []);
5408
- const [rightRailView, setRightRailView] = useState7(() => {
5299
+ const [rightRailView, setRightRailView] = useState6(() => {
5409
5300
  const saved = localStorage.getItem("termix_rightRailView");
5410
5301
  return saved && rightDockableIds().includes(saved) ? saved : null;
5411
5302
  });
5412
- const [rightSidebarWidth, setRightSidebarWidth] = useState7(() => {
5303
+ const [rightSidebarWidth, setRightSidebarWidth] = useState6(() => {
5413
5304
  const saved = localStorage.getItem("termix_rightSidebarWidth");
5414
5305
  return saved ? parseInt(saved, 10) : 291;
5415
5306
  });
5416
- const [rightSidebarDragging, setRightSidebarDragging] = useState7(false);
5307
+ const [rightSidebarDragging, setRightSidebarDragging] = useState6(false);
5417
5308
  const lastRightRailViewRef = useRef(
5418
5309
  localStorage.getItem("termix_lastRightRailView")
5419
5310
  );
@@ -5524,7 +5415,7 @@ function useRegions(isMobile) {
5524
5415
 
5525
5416
  // src/ui/shell/controller/use-hosts.ts
5526
5417
  init_define_import_meta_env();
5527
- 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";
5528
5419
 
5529
5420
  // src/ui/sidebar/build-host-tree.ts
5530
5421
  init_define_import_meta_env();
@@ -5596,10 +5487,10 @@ function buildHostTree(hosts, folderMeta) {
5596
5487
 
5597
5488
  // src/ui/shell/controller/use-hosts.ts
5598
5489
  function useHostList() {
5599
- const [tree, setTree] = useState8(null);
5600
- const [loading, setLoading] = useState8(true);
5601
- const [loaded, setLoaded] = useState8(false);
5602
- 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([]);
5603
5494
  const listRef = useRef2(list);
5604
5495
  listRef.current = list;
5605
5496
  const reload = useCallback3(async () => {
@@ -5656,7 +5547,7 @@ import {
5656
5547
  useCallback as useCallback4,
5657
5548
  useEffect as useEffect8,
5658
5549
  useRef as useRef3,
5659
- useState as useState9
5550
+ useState as useState8
5660
5551
  } from "react";
5661
5552
  import { toast } from "sonner";
5662
5553
  import { useTranslation } from "react-i18next";
@@ -5708,10 +5599,10 @@ function useTabs({
5708
5599
  }) {
5709
5600
  const { t } = useTranslation();
5710
5601
  const splitLabel = useSplitLabel();
5711
- const [tabs, setTabs] = useState9([dashboardTab(t("nav.dashboard"))]);
5712
- const [activeTabId, setActiveTabId] = useState9("dashboard");
5713
- const [tabsReady, setTabsReady] = useState9(false);
5714
- 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([]);
5715
5606
  const paneLayoutRestoredRef = useRef3(false);
5716
5607
  const splitTabsRestoredRef = useRef3(false);
5717
5608
  const splitOpsRef = useRef3({
@@ -5800,14 +5691,14 @@ function useTabs({
5800
5691
  if (!isSplitTab(active)) return activeTabId;
5801
5692
  return findPane(active.split, active.split.focusedPaneId)?.tabId ?? "";
5802
5693
  })();
5803
- const [lastTerminalTabId, setLastTerminalTabId] = useState9(activeTabId);
5694
+ const [lastTerminalTabId, setLastTerminalTabId] = useState8(activeTabId);
5804
5695
  useEffect8(() => {
5805
5696
  const working = tabs.find((t2) => t2.id === workingTabId);
5806
5697
  if (working && getTabType(working.type)?.commandTarget) {
5807
5698
  setLastTerminalTabId(working.id);
5808
5699
  }
5809
5700
  }, [workingTabId, tabs]);
5810
- const [showTabNumbers, setShowTabNumbers] = useState9(
5701
+ const [showTabNumbers, setShowTabNumbers] = useState8(
5811
5702
  () => localStorage.getItem("showTabNumbers") === "true"
5812
5703
  );
5813
5704
  useEffect8(() => {
@@ -6361,7 +6252,7 @@ function useSplit({
6361
6252
  tabsState,
6362
6253
  isMobile,
6363
6254
  paneEls,
6364
- callbacks: callbacks2,
6255
+ callbacks,
6365
6256
  resizeAllTerminals
6366
6257
  }) {
6367
6258
  const { t } = useTranslation2();
@@ -6760,8 +6651,8 @@ function useSplit({
6760
6651
  };
6761
6652
  }
6762
6653
  const pickerShell = {
6763
- ...callbacks2,
6764
- 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 })
6765
6656
  };
6766
6657
  const split = {
6767
6658
  active: activeSplit,
@@ -6927,7 +6818,7 @@ function useWorkspaceLayout(tabsState, regions) {
6927
6818
 
6928
6819
  // src/ui/shell/controller/use-shell-keybindings.ts
6929
6820
  init_define_import_meta_env();
6930
- 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";
6931
6822
 
6932
6823
  // src/ui/lib/pane-nav-hotkey.ts
6933
6824
  init_define_import_meta_env();
@@ -7031,7 +6922,7 @@ function useShellKeybindings({
7031
6922
  closeActiveTabRef.current();
7032
6923
  });
7033
6924
  }, []);
7034
- const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState10(() => {
6925
+ const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState9(() => {
7035
6926
  const v = localStorage.getItem("commandPaletteShortcutEnabled");
7036
6927
  return v !== null ? v === "true" : true;
7037
6928
  });
@@ -7098,7 +6989,7 @@ function useShellKeybindings({
7098
6989
  const tabDigit = getTabJumpDigit(e);
7099
6990
  if (tabDigit !== null) {
7100
6991
  const currentTabs = tabsRef.current.filter(
7101
- (tab) => !tab.parentSplitTabId
6992
+ (tab) => !tab.parentSplitTabId && tab.type !== "dashboard"
7102
6993
  );
7103
6994
  const index = tabDigit - 1;
7104
6995
  if (index < currentTabs.length) {
@@ -7144,7 +7035,7 @@ function useShellKeybindings({
7144
7035
 
7145
7036
  // src/ui/shell/controller/use-shell-lifecycle.ts
7146
7037
  init_define_import_meta_env();
7147
- 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";
7148
7039
  import { toast as toast3 } from "sonner";
7149
7040
  import { useTranslation as useTranslation3 } from "react-i18next";
7150
7041
  var SNAPSHOT_KEYS = [
@@ -7223,12 +7114,12 @@ function useShellLifecycle({
7223
7114
  }) {
7224
7115
  const { t, i18n: i18n2 } = useTranslation3();
7225
7116
  const { setTheme } = useTheme();
7226
- const [userPrefs, setUserPrefs] = useState11({
7117
+ const [userPrefs, setUserPrefs] = useState10({
7227
7118
  reopenTabsOnLogin: false
7228
7119
  });
7229
- const [userPrefsLoaded, setUserPrefsLoaded] = useState11(false);
7230
- const [isAdmin, setIsAdmin] = useState11(false);
7231
- const [isAppFullscreen, setIsAppFullscreen] = useState11(
7120
+ const [userPrefsLoaded, setUserPrefsLoaded] = useState10(false);
7121
+ const [isAdmin, setIsAdmin] = useState10(false);
7122
+ const [isAppFullscreen, setIsAppFullscreen] = useState10(
7232
7123
  () => !!document.fullscreenElement
7233
7124
  );
7234
7125
  useEffect12(() => {
@@ -7359,7 +7250,7 @@ function useShellSnapshot() {
7359
7250
  }
7360
7251
 
7361
7252
  // src/ui/shell/controller/ShellControllerProvider.tsx
7362
- import { jsx as jsx7 } from "react/jsx-runtime";
7253
+ import { jsx as jsx4 } from "react/jsx-runtime";
7363
7254
  var ShellControllerContext = createContext3(null);
7364
7255
  var TabStageContext = createContext3(null);
7365
7256
  function useShellController() {
@@ -7391,7 +7282,7 @@ function ShellControllerProvider({
7391
7282
  useTabTypes();
7392
7283
  const { settled: pluginsSettled } = usePluginStore();
7393
7284
  const isMobile = useIsMobile();
7394
- const [stageStore] = useState12(createTabStageStore);
7285
+ const [stageStore] = useState11(createTabStageStore);
7395
7286
  const confirmRef = useRef6(null);
7396
7287
  const openSingletonRef = useRef6(
7397
7288
  () => void 0
@@ -7402,7 +7293,7 @@ function ShellControllerProvider({
7402
7293
  });
7403
7294
  const regions = useRegions(isMobile);
7404
7295
  const hosts = useHostList();
7405
- const [paletteOpen, setPaletteOpen] = useState12(false);
7296
+ const [paletteOpen, setPaletteOpen] = useState11(false);
7406
7297
  const tabsState = useTabs({
7407
7298
  hosts: hosts.list,
7408
7299
  hostsRef: hosts.listRef,
@@ -7459,6 +7350,10 @@ function ShellControllerProvider({
7459
7350
  regions.right.setView((prev) => prev === id ? null : prev);
7460
7351
  },
7461
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
+ },
7462
7357
  saveQuickConnect: tabsState.saveQuickConnectHost,
7463
7358
  confirm: (options) => confirmRef.current ? confirmRef.current(options) : Promise.resolve(false)
7464
7359
  };
@@ -7466,7 +7361,7 @@ function ShellControllerProvider({
7466
7361
  useLayoutEffect2(() => {
7467
7362
  shellImplRef.current = shellCallbacksImpl;
7468
7363
  });
7469
- const callbacks2 = useMemo5(
7364
+ const callbacks = useMemo5(
7470
7365
  () => ({
7471
7366
  openTab: (...args) => shellImplRef.current.openTab(...args),
7472
7367
  openSingletonTab: (...args) => shellImplRef.current.openSingletonTab(...args),
@@ -7476,6 +7371,7 @@ function ShellControllerProvider({
7476
7371
  openRailView: (...args) => shellImplRef.current.openRailView(...args),
7477
7372
  closeRailView: (...args) => shellImplRef.current.closeRailView(...args),
7478
7373
  openHostEditor: (...args) => shellImplRef.current.openHostEditor?.(...args),
7374
+ editHost: (...args) => shellImplRef.current.editHost?.(...args),
7479
7375
  saveQuickConnect: (...args) => shellImplRef.current.saveQuickConnect(...args),
7480
7376
  confirm: (...args) => shellImplRef.current.confirm(...args)
7481
7377
  }),
@@ -7483,13 +7379,13 @@ function ShellControllerProvider({
7483
7379
  );
7484
7380
  const panelCallbacks = useMemo5(
7485
7381
  () => ({
7486
- ...callbacks2,
7382
+ ...callbacks,
7487
7383
  openTab: (...args) => {
7488
- callbacks2.openTab(...args);
7384
+ callbacks.openTab(...args);
7489
7385
  if (isMobile) regions.left.setOpen(false);
7490
7386
  }
7491
7387
  }),
7492
- [callbacks2, isMobile]
7388
+ [callbacks, isMobile]
7493
7389
  );
7494
7390
  const resizeAllTerminals = useCallback6(() => {
7495
7391
  const id = requestAnimationFrame(() => {
@@ -7506,7 +7402,7 @@ function ShellControllerProvider({
7506
7402
  tabsState,
7507
7403
  isMobile,
7508
7404
  paneEls: stageStore.paneEls,
7509
- callbacks: callbacks2,
7405
+ callbacks,
7510
7406
  resizeAllTerminals
7511
7407
  });
7512
7408
  useEffect13(() => {
@@ -7531,7 +7427,7 @@ function ShellControllerProvider({
7531
7427
  toggleFullscreen: lifecycle.toggleAppFullscreen
7532
7428
  });
7533
7429
  useEffect13(() => {
7534
- setShellCallbacks(callbacks2);
7430
+ setShellCallbacks(callbacks);
7535
7431
  });
7536
7432
  const activeTab = tabs.find((tab) => tab.id === activeTabId);
7537
7433
  const controller = {
@@ -7580,7 +7476,7 @@ function ShellControllerProvider({
7580
7476
  active: lifecycle.isAppFullscreen,
7581
7477
  toggle: lifecycle.toggleAppFullscreen
7582
7478
  },
7583
- callbacks: callbacks2,
7479
+ callbacks,
7584
7480
  panelCallbacks,
7585
7481
  settings: {
7586
7482
  username,
@@ -7597,7 +7493,7 @@ function ShellControllerProvider({
7597
7493
  publishShellController(controller);
7598
7494
  });
7599
7495
  useEffect13(() => () => publishShellController(null), []);
7600
- 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 }) });
7601
7497
  }
7602
7498
 
7603
7499
  // src/ui/lib/adaptive-polling.ts
@@ -7706,7 +7602,7 @@ import {
7706
7602
  useRef as useRef7,
7707
7603
  useMemo as useMemo6,
7708
7604
  useSyncExternalStore as useSyncExternalStore8,
7709
- useState as useState13
7605
+ useState as useState12
7710
7606
  } from "react";
7711
7607
 
7712
7608
  // src/ui/lib/server-status-store.ts
@@ -7800,7 +7696,7 @@ var ServerStatusStore = class {
7800
7696
  };
7801
7697
 
7802
7698
  // src/ui/lib/ServerStatusContext.tsx
7803
- import { jsx as jsx8 } from "react/jsx-runtime";
7699
+ import { jsx as jsx5 } from "react/jsx-runtime";
7804
7700
  var StatusStoreContext = createContext4(null);
7805
7701
  var ServerStatusContext = createContext4(null);
7806
7702
  var activeStore = null;
@@ -7817,7 +7713,7 @@ function ServerStatusProvider({
7817
7713
  storeRef.current = new ServerStatusStore();
7818
7714
  }
7819
7715
  const store = storeRef.current;
7820
- const [version4, setVersion] = useState13(0);
7716
+ const [version3, setVersion] = useState12(0);
7821
7717
  const mountedRef = useRef7(true);
7822
7718
  const refreshInFlightRef = useRef7(null);
7823
7719
  useEffect14(() => store.subscribeAll(() => setVersion((v) => v + 1)), [store]);
@@ -7900,9 +7796,9 @@ function ServerStatusProvider({
7900
7796
  }),
7901
7797
  // version refreshes the statuses/initialLoadComplete snapshots
7902
7798
  // eslint-disable-next-line react-hooks/exhaustive-deps
7903
- [version4, refreshStatuses, getStatus]
7799
+ [version3, refreshStatuses, getStatus]
7904
7800
  );
7905
- 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 }) });
7906
7802
  }
7907
7803
  function useStatusStore() {
7908
7804
  const store = useContext4(StatusStoreContext);
@@ -7952,7 +7848,7 @@ function useOptionalHostStatusEntry(hostId) {
7952
7848
 
7953
7849
  // src/ui/plugin-host/bridge.ts
7954
7850
  init_define_import_meta_env();
7955
- 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";
7956
7852
 
7957
7853
  // src/ui/shell/companions.ts
7958
7854
  init_define_import_meta_env();
@@ -8272,7 +8168,7 @@ var pluginHostBridge = {
8272
8168
  },
8273
8169
  useSettings(pluginId, scope, hostId) {
8274
8170
  const numericHostId = hostId === void 0 ? void 0 : Number.parseInt(String(hostId), 10);
8275
- const [state2, setState] = useState14({ values: {}, loaded: false });
8171
+ const [state2, setState] = useState13({ values: {}, loaded: false });
8276
8172
  useEffect15(() => {
8277
8173
  let cancelled = false;
8278
8174
  if (scope === "host" && !Number.isFinite(numericHostId)) return;
@@ -8328,7 +8224,7 @@ var pluginHostBridge = {
8328
8224
  );
8329
8225
  },
8330
8226
  useCurrentUser() {
8331
- const [user, setUser] = useState14(null);
8227
+ const [user, setUser] = useState13(null);
8332
8228
  useEffect15(() => {
8333
8229
  let cancelled = false;
8334
8230
  loadCurrentUser().then((next) => {
@@ -8912,8 +8808,8 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8912
8808
  },
8913
8809
  registerOnboardingStep(step) {
8914
8810
  requireStepId(step.id, "onboarding step");
8915
- const version4 = step.version ?? 1;
8916
- if (!Number.isInteger(version4) || version4 < 1) {
8811
+ const version3 = step.version ?? 1;
8812
+ if (!Number.isInteger(version3) || version3 < 1) {
8917
8813
  throw new Error(
8918
8814
  `${pluginId}: onboarding step "${step.id}" needs a whole version of 1 or more`
8919
8815
  );
@@ -8923,7 +8819,7 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8923
8819
  onboardingSteps.register({
8924
8820
  id: pluginStepKey(pluginId, step.id),
8925
8821
  pluginId,
8926
- version: version4,
8822
+ version: version3,
8927
8823
  titleKey: key(step.titleKey),
8928
8824
  descriptionKey: step.descriptionKey ? key(step.descriptionKey) : void 0,
8929
8825
  icon: step.icon ? withIconBoundary(pluginId, step.icon) : void 0,
@@ -9061,918 +8957,6 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
9061
8957
  };
9062
8958
  }
9063
8959
 
9064
- // src/ui/hooks/use-installed-plugins.ts
9065
- init_define_import_meta_env();
9066
- import { useEffect as useEffect17, useState as useState15 } from "react";
9067
- function useInstalledPlugins() {
9068
- const [plugins, setPlugins] = useState15([]);
9069
- useEffect17(() => {
9070
- let cancelled = false;
9071
- let request = 0;
9072
- const load2 = () => {
9073
- const current2 = ++request;
9074
- void getPlugins().then((loaded) => {
9075
- if (!cancelled && current2 === request) setPlugins(loaded);
9076
- }).catch(() => {
9077
- });
9078
- };
9079
- load2();
9080
- window.addEventListener(PLUGINS_CHANGED_EVENT, load2);
9081
- return () => {
9082
- cancelled = true;
9083
- window.removeEventListener(PLUGINS_CHANGED_EVENT, load2);
9084
- };
9085
- }, []);
9086
- return plugins;
9087
- }
9088
-
9089
- // src/ui/settings/SettingsFields.tsx
9090
- init_define_import_meta_env();
9091
- import { useTranslation as useTranslation4 } from "react-i18next";
9092
- import { Fragment, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
9093
- function SettingsFieldRow({
9094
- pluginId,
9095
- field,
9096
- values,
9097
- setValue,
9098
- running,
9099
- error,
9100
- disabled = false,
9101
- defaultKey
9102
- }) {
9103
- const { t } = useTranslation4();
9104
- useSettingsComponents();
9105
- const label = (key) => key ? t(pluginKey(pluginId, key)) : "";
9106
- if (field.type === "custom") {
9107
- const Component = getSettingsComponent(pluginId, field.component);
9108
- if (!Component) return null;
9109
- return /* @__PURE__ */ jsx9(
9110
- Component,
9111
- {
9112
- pluginId,
9113
- values,
9114
- setValue,
9115
- running
9116
- }
9117
- );
9118
- }
9119
- const value = values[field.key];
9120
- const readOnly = disabled || !running;
9121
- const description = field.descriptionKey ? /* @__PURE__ */ jsxs2(Fragment, { children: [
9122
- label(field.descriptionKey),
9123
- error && /* @__PURE__ */ jsx9("span", { className: "block text-destructive mt-0.5", children: error })
9124
- ] }) : error ? /* @__PURE__ */ jsx9("span", { className: "text-destructive", children: error }) : void 0;
9125
- const control = /* @__PURE__ */ jsx9(
9126
- FieldControl,
9127
- {
9128
- pluginId,
9129
- field,
9130
- value,
9131
- setValue,
9132
- readOnly
9133
- }
9134
- );
9135
- if (field.type === "boolean") {
9136
- return /* @__PURE__ */ jsx9(
9137
- SettingRow,
9138
- {
9139
- label: label(field.labelKey),
9140
- description,
9141
- badge: error ? t("common.error") : void 0,
9142
- defaultKey,
9143
- children: control
9144
- }
9145
- );
9146
- }
9147
- 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: [
9148
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
9149
- /* @__PURE__ */ jsx9("span", { className: "text-sm font-medium leading-snug", children: label(field.labelKey) }),
9150
- error && /* @__PURE__ */ jsx9("span", { className: "text-[10px] font-bold text-warning border border-warning/40 px-1 shrink-0", children: t("common.error") }),
9151
- defaultKey && /* @__PURE__ */ jsx9(HostDefaultBadge, { settingKey: defaultKey })
9152
- ] }),
9153
- description && /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground leading-snug", children: description }),
9154
- /* @__PURE__ */ jsx9("div", { className: "min-w-0", children: control })
9155
- ] }) });
9156
- }
9157
- function FieldControl({
9158
- pluginId,
9159
- field,
9160
- value,
9161
- setValue,
9162
- readOnly
9163
- }) {
9164
- const { t } = useTranslation4();
9165
- const label = (key) => key ? t(pluginKey(pluginId, key)) : "";
9166
- const placeholder = field.placeholderKey ? label(field.placeholderKey) : void 0;
9167
- switch (field.type) {
9168
- case "boolean":
9169
- return /* @__PURE__ */ jsx9(
9170
- FakeSwitch,
9171
- {
9172
- checked: value === true,
9173
- disabled: readOnly,
9174
- onChange: (next) => setValue(field.key, next)
9175
- }
9176
- );
9177
- case "number":
9178
- return /* @__PURE__ */ jsx9(
9179
- Input,
9180
- {
9181
- type: "number",
9182
- className: "h-8 w-full max-w-40 text-sm",
9183
- disabled: readOnly,
9184
- min: field.min,
9185
- max: field.max,
9186
- placeholder,
9187
- value: value === void 0 || value === null ? "" : String(value),
9188
- onChange: (event) => setValue(
9189
- field.key,
9190
- event.target.value === "" ? null : Number(event.target.value)
9191
- )
9192
- }
9193
- );
9194
- case "select":
9195
- return /* @__PURE__ */ jsxs2(
9196
- Select,
9197
- {
9198
- disabled: readOnly,
9199
- value: typeof value === "string" ? value : "",
9200
- onValueChange: (next) => setValue(field.key, next),
9201
- children: [
9202
- /* @__PURE__ */ jsx9(SelectTrigger, { className: "h-8 w-full min-w-0 text-sm", children: /* @__PURE__ */ jsx9(SelectValue, { placeholder }) }),
9203
- /* @__PURE__ */ jsx9(SelectContent, { children: (field.options ?? []).map((option) => /* @__PURE__ */ jsx9(SelectItem, { value: option.value, children: label(option.labelKey) }, option.value)) })
9204
- ]
9205
- }
9206
- );
9207
- case "multiselect": {
9208
- const selected = Array.isArray(value) ? value : [];
9209
- return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-1.5", children: (field.options ?? []).map((option) => /* @__PURE__ */ jsxs2(
9210
- "label",
9211
- {
9212
- className: "flex items-center gap-2 text-xs text-muted-foreground",
9213
- children: [
9214
- /* @__PURE__ */ jsx9(
9215
- Checkbox2,
9216
- {
9217
- disabled: readOnly,
9218
- checked: selected.includes(option.value),
9219
- onCheckedChange: (checked) => setValue(
9220
- field.key,
9221
- checked ? [...selected, option.value] : selected.filter((entry) => entry !== option.value)
9222
- )
9223
- }
9224
- ),
9225
- /* @__PURE__ */ jsx9("span", { children: label(option.labelKey) })
9226
- ]
9227
- },
9228
- option.value
9229
- )) });
9230
- }
9231
- case "secret":
9232
- return /* @__PURE__ */ jsx9(
9233
- SecretControl,
9234
- {
9235
- field,
9236
- value,
9237
- setValue,
9238
- readOnly,
9239
- placeholder
9240
- }
9241
- );
9242
- case "textarea":
9243
- case "json":
9244
- return /* @__PURE__ */ jsx9(
9245
- Textarea,
9246
- {
9247
- className: "w-full text-sm font-mono min-h-16",
9248
- disabled: readOnly,
9249
- placeholder,
9250
- value: typeof value === "string" ? value : value === void 0 || value === null ? "" : JSON.stringify(value, null, 2),
9251
- onChange: (event) => setValue(field.key, event.target.value)
9252
- }
9253
- );
9254
- default:
9255
- return /* @__PURE__ */ jsx9(
9256
- Input,
9257
- {
9258
- className: "h-8 w-full text-sm",
9259
- disabled: readOnly,
9260
- placeholder,
9261
- value: typeof value === "string" ? value : "",
9262
- onChange: (event) => setValue(field.key, event.target.value)
9263
- }
9264
- );
9265
- }
9266
- }
9267
- function SecretControl({
9268
- field,
9269
- value,
9270
- setValue,
9271
- readOnly,
9272
- placeholder
9273
- }) {
9274
- const { t } = useTranslation4();
9275
- const redacted = isRedactedSecret(value);
9276
- const stored = redacted && value.set;
9277
- if (redacted) {
9278
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-2", children: [
9279
- /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: stored ? t("settings.secretSet") : t("settings.secretNotSet") }),
9280
- /* @__PURE__ */ jsx9(
9281
- Button,
9282
- {
9283
- variant: "outline",
9284
- size: "sm",
9285
- className: "h-7 text-xs",
9286
- disabled: readOnly,
9287
- onClick: () => setValue(field.key, ""),
9288
- children: stored ? t("settings.secretReplace") : t("common.edit")
9289
- }
9290
- )
9291
- ] });
9292
- }
9293
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 min-w-0", children: [
9294
- /* @__PURE__ */ jsx9("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx9(
9295
- PasswordInput,
9296
- {
9297
- className: "h-8 w-full text-sm",
9298
- disabled: readOnly,
9299
- placeholder,
9300
- value: typeof value === "string" ? value : "",
9301
- onChange: (event) => setValue(field.key, event.target.value)
9302
- }
9303
- ) }),
9304
- /* @__PURE__ */ jsx9(
9305
- Button,
9306
- {
9307
- variant: "ghost",
9308
- size: "sm",
9309
- className: "h-7 text-xs",
9310
- disabled: readOnly,
9311
- onClick: () => setValue(field.key, { set: stored }),
9312
- children: t("common.cancel")
9313
- }
9314
- )
9315
- ] });
9316
- }
9317
-
9318
- // src/ui/settings/HostPluginSections.tsx
9319
- init_define_import_meta_env();
9320
- import { useMemo as useMemo9 } from "react";
9321
- import { useTranslation as useTranslation5 } from "react-i18next";
9322
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
9323
- function usePluginHostSections() {
9324
- const plugins = useInstalledPlugins();
9325
- return useMemo9(
9326
- () => plugins.filter((plugin) => {
9327
- if (!plugin.enabled) return false;
9328
- const host = plugin.contributes?.settings?.host;
9329
- return !!host && (hasVisibleFields(host.fields) || !!host.enableKey);
9330
- }),
9331
- [plugins]
9332
- );
9333
- }
9334
- function hostFieldDefaults(plugin) {
9335
- const host = plugin.contributes?.settings?.host;
9336
- if (!host) return {};
9337
- const defaults3 = {};
9338
- if (host.enableKey) defaults3[host.enableKey] = host.enableDefault ?? false;
9339
- for (const field of host.fields) {
9340
- if (field.default !== void 0) defaults3[field.key] = field.default;
9341
- }
9342
- return defaults3;
9343
- }
9344
- function HostPluginSections({
9345
- plugins,
9346
- values,
9347
- setValue
9348
- }) {
9349
- if (plugins.length === 0) return null;
9350
- return /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-2", children: plugins.map((plugin) => /* @__PURE__ */ jsx10(
9351
- HostPluginSection,
9352
- {
9353
- plugin,
9354
- values: values[plugin.id] ?? {},
9355
- setValue: (key, value) => setValue(plugin.id, key, value)
9356
- },
9357
- plugin.id
9358
- )) });
9359
- }
9360
- function HostFeatureFields({
9361
- form,
9362
- updateForm
9363
- }) {
9364
- const pluginId = usePluginScope();
9365
- const plugin = usePluginHostSections().find((p) => p.id === pluginId);
9366
- if (!plugin) return null;
9367
- const values = form?.pluginSettings ?? {};
9368
- return /* @__PURE__ */ jsx10(
9369
- HostPluginSection,
9370
- {
9371
- plugin,
9372
- values: values[plugin.id] ?? {},
9373
- setValue: (key, value) => updateForm((current2) => {
9374
- const all = current2.pluginSettings ?? {};
9375
- return {
9376
- ...current2,
9377
- pluginSettings: {
9378
- ...all,
9379
- [plugin.id]: { ...all[plugin.id] ?? {}, [key]: value }
9380
- }
9381
- };
9382
- })
9383
- }
9384
- );
9385
- }
9386
- function HostPluginSection({
9387
- plugin,
9388
- values,
9389
- setValue
9390
- }) {
9391
- const { t } = useTranslation5();
9392
- const host = plugin.contributes?.settings?.host;
9393
- if (!host) return null;
9394
- const shown = { ...hostFieldDefaults(plugin), ...values };
9395
- const running = plugin.enabled && plugin.state !== "failed";
9396
- const enableKey = host.enableKey;
9397
- const enabled = enableKey ? shown[enableKey] === true : true;
9398
- return /* @__PURE__ */ jsxs3(
9399
- SectionCard,
9400
- {
9401
- title: pluginName(t, plugin),
9402
- icon: /* @__PURE__ */ jsx10(PluginIcon, { name: plugin.icon, className: "size-3.5" }),
9403
- action: /* @__PURE__ */ jsx10(DocsLink, { href: plugin.docs }),
9404
- children: [
9405
- !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", {
9406
- name: pluginName(t, plugin)
9407
- }) }),
9408
- enableKey && /* @__PURE__ */ jsx10(
9409
- SettingsFieldRow,
9410
- {
9411
- pluginId: plugin.id,
9412
- field: {
9413
- key: enableKey,
9414
- type: "boolean",
9415
- labelKey: host.enableLabelKey,
9416
- descriptionKey: host.enableDescriptionKey
9417
- },
9418
- values: shown,
9419
- setValue,
9420
- running,
9421
- defaultKey: `${plugin.id}.${enableKey}`
9422
- }
9423
- ),
9424
- enabled && host.fields.filter((field) => isFieldShown(field, shown)).map((field) => /* @__PURE__ */ jsx10(
9425
- SettingsFieldRow,
9426
- {
9427
- pluginId: plugin.id,
9428
- field,
9429
- values: shown,
9430
- setValue,
9431
- running,
9432
- defaultKey: `${plugin.id}.${field.key}`
9433
- },
9434
- field.key
9435
- ))
9436
- ]
9437
- }
9438
- );
9439
- }
9440
-
9441
- // src/ui/shell/ActionSlot.tsx
9442
- init_define_import_meta_env();
9443
- import React5 from "react";
9444
- import { useTranslation as useTranslation6 } from "react-i18next";
9445
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
9446
- function ActionSlot({
9447
- slotId,
9448
- context,
9449
- className,
9450
- enabled = true,
9451
- renderItem,
9452
- hideLabels,
9453
- when
9454
- }) {
9455
- const { t } = useTranslation6();
9456
- const contributions2 = useActionSlot(slotId, when).filter(
9457
- (contribution) => contribution.kind !== "component"
9458
- );
9459
- if (!enabled || contributions2.length === 0) return null;
9460
- return /* @__PURE__ */ jsx11(Fragment2, { children: contributions2.map((contribution) => {
9461
- const invoke = () => {
9462
- void invokeAction(contribution.actionId, ...context?.() ?? []);
9463
- };
9464
- if (renderItem) {
9465
- return /* @__PURE__ */ jsx11(React5.Fragment, { children: renderItem(contribution, invoke) }, contribution.actionId);
9466
- }
9467
- const Icon = contribution.icon;
9468
- const label = t(contribution.titleKey);
9469
- return /* @__PURE__ */ jsxs4(
9470
- "button",
9471
- {
9472
- type: "button",
9473
- className,
9474
- "aria-label": label,
9475
- title: label,
9476
- onClick: invoke,
9477
- children: [
9478
- Icon && /* @__PURE__ */ jsx11(Icon, { className: "size-4 shrink-0" }),
9479
- !hideLabels && label
9480
- ]
9481
- },
9482
- contribution.actionId
9483
- );
9484
- }) });
9485
- }
9486
- function ComponentSlot({
9487
- slotId,
9488
- props,
9489
- when
9490
- }) {
9491
- const contributions2 = useActionSlot(slotId, when).filter(
9492
- (contribution) => contribution.kind === "component" && contribution.component
9493
- );
9494
- if (contributions2.length === 0) return null;
9495
- return /* @__PURE__ */ jsx11(Fragment2, { children: contributions2.map((contribution) => {
9496
- const Contributed = contribution.component;
9497
- return /* @__PURE__ */ jsx11(Contributed, { ...props ?? {} }, contribution.actionId);
9498
- }) });
9499
- }
9500
-
9501
- // src/ui/shell/tab-handles.ts
9502
- init_define_import_meta_env();
9503
- import { useSyncExternalStore as useSyncExternalStore11 } from "react";
9504
- var version3 = 0;
9505
- var listeners8 = /* @__PURE__ */ new Set();
9506
- var callbacks = /* @__PURE__ */ new WeakMap();
9507
- function subscribe5(listener) {
9508
- listeners8.add(listener);
9509
- return () => listeners8.delete(listener);
9510
- }
9511
- function trackTabHandle(ref) {
9512
- if (!ref) return void 0;
9513
- let callback = callbacks.get(ref);
9514
- if (!callback) {
9515
- callback = (value) => {
9516
- if (ref.current === value) return;
9517
- ref.current = value;
9518
- version3 += 1;
9519
- for (const listener of listeners8) listener();
9520
- };
9521
- callbacks.set(ref, callback);
9522
- }
9523
- return callback;
9524
- }
9525
- function useTabHandlesVersion() {
9526
- return useSyncExternalStore11(
9527
- subscribe5,
9528
- () => version3,
9529
- () => version3
9530
- );
9531
- }
9532
-
9533
- // src/ui/plugin-host/PluginViewPlaceholder.tsx
9534
- init_define_import_meta_env();
9535
- import { useTranslation as useTranslation7 } from "react-i18next";
9536
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
9537
- function PluginViewPlaceholder({
9538
- kind,
9539
- viewId,
9540
- compact = false
9541
- }) {
9542
- const { t } = useTranslation7();
9543
- usePluginStore();
9544
- const { has } = usePermissions();
9545
- const { status, owner } = unregisteredViewStatus(kind, viewId);
9546
- const name = owner ? pluginName(t, owner.summary) : viewId;
9547
- if (status === "loading") {
9548
- return /* @__PURE__ */ jsx12(
9549
- "div",
9550
- {
9551
- className: "flex h-full w-full items-center justify-center",
9552
- "aria-label": t("plugins.runtime.loading", { name }),
9553
- children: /* @__PURE__ */ jsx12("div", { className: "size-5 rounded-full border-2 border-muted-foreground/30 border-t-muted-foreground/70 animate-spin" })
9554
- }
9555
- );
9556
- }
9557
- const title = status === "failed" ? t("plugins.runtime.failed", { name }) : owner ? t("plugins.runtime.needsPlugin", { name }) : t("plugins.runtime.needsUnknownPlugin");
9558
- const hint = status === "failed" ? t("plugins.runtime.failedHint") : status === "disabled" ? t("plugins.runtime.disabledHint") : t("plugins.runtime.missingHint");
9559
- return /* @__PURE__ */ jsxs5(
9560
- "div",
9561
- {
9562
- "data-testid": "plugin-view-placeholder",
9563
- "data-status": status,
9564
- className: `flex h-full w-full flex-col items-center justify-center gap-2 text-center ${compact ? "p-3" : "p-6"}`,
9565
- children: [
9566
- /* @__PURE__ */ jsx12(CircleSlash, { className: "size-6 text-muted-foreground/40" }),
9567
- /* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold text-muted-foreground", children: title }),
9568
- /* @__PURE__ */ jsx12("span", { className: "text-xs text-muted-foreground/70", children: hint }),
9569
- !compact && status !== "failed" && has("admin.plugins.manage") && /* @__PURE__ */ jsx12(
9570
- Button,
9571
- {
9572
- variant: "outline",
9573
- size: "sm",
9574
- className: "mt-1",
9575
- onClick: () => window.dispatchEvent(new CustomEvent(OPEN_PLUGINS_EVENT)),
9576
- children: t("plugins.manager.browsePlugins")
9577
- }
9578
- )
9579
- ]
9580
- }
9581
- );
9582
- }
9583
-
9584
- // src/ui/lib/adaptive-resource-budget.ts
9585
- init_define_import_meta_env();
9586
-
9587
- // src/ui/lib/local-adaptive-engine.ts
9588
- init_define_import_meta_env();
9589
- var LOCAL_ADAPTIVE_ENGINE_KEY = "termix.local.adaptive-engine.v1";
9590
- var VERSION = 1;
9591
- var HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
9592
- var MAX_SCOPES = 128;
9593
- var MAX_ACTIONS_PER_SCOPE = 12;
9594
- var DEFAULT_MIN_EVIDENCE = 2.5;
9595
- var DEFAULT_MIN_SHARE = 0.6;
9596
- var DEFAULT_MIN_MARGIN = 1;
9597
- var EWMA_ALPHA = 0.25;
9598
- function emptyStore() {
9599
- return { version: VERSION, scopes: {} };
9600
- }
9601
- function finiteNonNegative(value) {
9602
- return Number.isFinite(value) && Number(value) >= 0 ? Number(value) : 0;
9603
- }
9604
- function sanitizeAction(value) {
9605
- if (!value || typeof value !== "object") return null;
9606
- const stat = value;
9607
- if (!Number.isFinite(stat.weight) || Number(stat.weight) < 0 || !Number.isFinite(stat.updatedAt)) {
9608
- return null;
9609
- }
9610
- const latencyEwmaMs = finiteNonNegative(stat.latencyEwmaMs);
9611
- return {
9612
- weight: Number(stat.weight),
9613
- updatedAt: Number(stat.updatedAt),
9614
- observations: finiteNonNegative(stat.observations),
9615
- successes: finiteNonNegative(stat.successes),
9616
- cancellations: finiteNonNegative(stat.cancellations),
9617
- fallbacks: finiteNonNegative(stat.fallbacks),
9618
- ...latencyEwmaMs > 0 ? { latencyEwmaMs } : {}
9619
- };
9620
- }
9621
- function sanitizeStore(value) {
9622
- if (!value || typeof value !== "object") return emptyStore();
9623
- const candidate = value;
9624
- if (candidate.version !== VERSION || !candidate.scopes || typeof candidate.scopes !== "object" || Array.isArray(candidate.scopes)) {
9625
- return emptyStore();
9626
- }
9627
- const scopes2 = {};
9628
- for (const [scope, rawScope] of Object.entries(candidate.scopes)) {
9629
- if (!rawScope || typeof rawScope !== "object") continue;
9630
- const value2 = rawScope;
9631
- if (!Number.isFinite(value2.updatedAt) || !value2.actions || typeof value2.actions !== "object" || Array.isArray(value2.actions)) {
9632
- continue;
9633
- }
9634
- const actions4 = {};
9635
- for (const [action, rawStat] of Object.entries(value2.actions)) {
9636
- const stat = sanitizeAction(rawStat);
9637
- if (stat) actions4[action] = stat;
9638
- }
9639
- scopes2[scope] = { actions: actions4, updatedAt: Number(value2.updatedAt) };
9640
- }
9641
- return { version: VERSION, scopes: scopes2 };
9642
- }
9643
- function readStore() {
9644
- if (typeof localStorage === "undefined") return emptyStore();
9645
- try {
9646
- return sanitizeStore(
9647
- JSON.parse(localStorage.getItem(LOCAL_ADAPTIVE_ENGINE_KEY) ?? "null")
9648
- );
9649
- } catch {
9650
- return emptyStore();
9651
- }
9652
- }
9653
- function writeStore(store) {
9654
- if (typeof localStorage === "undefined") return;
9655
- try {
9656
- localStorage.setItem(LOCAL_ADAPTIVE_ENGINE_KEY, JSON.stringify(store));
9657
- } catch {
9658
- }
9659
- }
9660
- function decayedWeight(stat, now) {
9661
- const age = Math.max(0, now - stat.updatedAt);
9662
- return stat.weight * 2 ** (-age / HALF_LIFE_MS);
9663
- }
9664
- function trimStore(store, now) {
9665
- for (const scope of Object.values(store.scopes)) {
9666
- scope.actions = Object.fromEntries(
9667
- Object.entries(scope.actions).sort(
9668
- ([, a], [, b]) => decayedWeight(b, now) - decayedWeight(a, now) || b.updatedAt - a.updatedAt
9669
- ).slice(0, MAX_ACTIONS_PER_SCOPE)
9670
- );
9671
- }
9672
- store.scopes = Object.fromEntries(
9673
- Object.entries(store.scopes).sort(([, a], [, b]) => b.updatedAt - a.updatedAt).slice(0, MAX_SCOPES)
9674
- );
9675
- }
9676
- function updateEwma(previous, value) {
9677
- return previous === void 0 ? value : previous + EWMA_ALPHA * (value - previous);
9678
- }
9679
- function getActionStats(store, scope, action, now) {
9680
- const scopeStats = store.scopes[scope] ?? { actions: {}, updatedAt: now };
9681
- return [
9682
- scopeStats,
9683
- scopeStats.actions[action] ?? {
9684
- weight: 0,
9685
- updatedAt: now,
9686
- observations: 0,
9687
- successes: 0,
9688
- cancellations: 0,
9689
- fallbacks: 0
9690
- }
9691
- ];
9692
- }
9693
- function saveActionStats(store, scope, action, scopeStats, actionStats, now) {
9694
- scopeStats.actions[action] = actionStats;
9695
- scopeStats.updatedAt = now;
9696
- store.scopes[scope] = scopeStats;
9697
- trimStore(store, now);
9698
- writeStore(store);
9699
- }
9700
- function recordLocalAdaptiveChoice(scope, action, now = Date.now()) {
9701
- if (!scope || !action) return;
9702
- const store = readStore();
9703
- const [scopeStats, previous] = getActionStats(store, scope, action, now);
9704
- saveActionStats(
9705
- store,
9706
- scope,
9707
- action,
9708
- scopeStats,
9709
- {
9710
- ...previous,
9711
- weight: decayedWeight(previous, now) + 1,
9712
- updatedAt: now
9713
- },
9714
- now
9715
- );
9716
- }
9717
- function recordLocalAdaptiveOutcome(scope, action, outcome, now = Date.now()) {
9718
- if (!scope || !action || !Object.values(outcome).some((v) => v !== void 0)) {
9719
- return;
9720
- }
9721
- const store = readStore();
9722
- const [scopeStats, previous] = getActionStats(store, scope, action, now);
9723
- const latency = finiteNonNegative(outcome.latencyMs);
9724
- saveActionStats(
9725
- store,
9726
- scope,
9727
- action,
9728
- scopeStats,
9729
- {
9730
- ...previous,
9731
- updatedAt: now,
9732
- observations: previous.observations + 1,
9733
- successes: previous.successes + (outcome.success === true ? 1 : 0),
9734
- cancellations: previous.cancellations + (outcome.cancelled === true ? 1 : 0),
9735
- fallbacks: previous.fallbacks + (outcome.fallback === true ? 1 : 0),
9736
- ...latency > 0 ? { latencyEwmaMs: updateEwma(previous.latencyEwmaMs, latency) } : {}
9737
- },
9738
- now
9739
- );
9740
- }
9741
- function chooseLocalAdaptiveAction({
9742
- scope,
9743
- candidates,
9744
- fallback,
9745
- now = Date.now(),
9746
- ttlMs = 5 * 60 * 1e3,
9747
- minEvidence = DEFAULT_MIN_EVIDENCE,
9748
- minShare = DEFAULT_MIN_SHARE,
9749
- minMargin = DEFAULT_MIN_MARGIN
9750
- }) {
9751
- const expiresAt = now + Math.max(0, ttlMs);
9752
- const stats = readStore().scopes[scope]?.actions;
9753
- if (!stats || candidates.length === 0) {
9754
- return { action: fallback, confidence: 0, reason: "fallback", expiresAt };
9755
- }
9756
- const ranked = candidates.map((action) => ({
9757
- action,
9758
- weight: stats[action] ? decayedWeight(stats[action], now) : 0
9759
- })).sort((a, b) => b.weight - a.weight);
9760
- const total = ranked.reduce((sum, item) => sum + item.weight, 0);
9761
- const [best, second] = ranked;
9762
- if (!best || total <= 0) {
9763
- return { action: fallback, confidence: 0, reason: "fallback", expiresAt };
9764
- }
9765
- const share = best.weight / total;
9766
- const margin = best.weight - (second?.weight ?? 0);
9767
- const confidence = Math.min(
9768
- 1,
9769
- best.weight / Math.max(minEvidence, Number.EPSILON),
9770
- share / Math.max(minShare, Number.EPSILON),
9771
- margin / Math.max(minMargin, Number.EPSILON)
9772
- );
9773
- const learned = best.weight >= minEvidence && share >= minShare && margin >= minMargin;
9774
- return {
9775
- action: learned ? best.action : fallback,
9776
- confidence,
9777
- reason: learned ? "learned" : "insufficient-evidence",
9778
- expiresAt
9779
- };
9780
- }
9781
- function getLocalAdaptiveStats(scope) {
9782
- return readStore().scopes[scope]?.actions ?? {};
9783
- }
9784
- function clearLocalAdaptiveEngine() {
9785
- if (typeof localStorage === "undefined") return;
9786
- try {
9787
- localStorage.removeItem(LOCAL_ADAPTIVE_ENGINE_KEY);
9788
- } catch {
9789
- }
9790
- }
9791
-
9792
- // src/ui/lib/adaptive-resource-budget.ts
9793
- var scopes = {
9794
- module: "resource-budget:module",
9795
- network: "resource-budget:network"
9796
- };
9797
- var usefulnessScopes = {
9798
- module: "resource-usefulness:module",
9799
- network: "resource-usefulness:network"
9800
- };
9801
- var PRELOAD_USE_WINDOW_MS = 1e4;
9802
- var pendingPreloads = /* @__PURE__ */ new Map();
9803
- var activeTasks = {
9804
- module: 0,
9805
- network: 0
9806
- };
9807
- var budgets = {
9808
- paused: {
9809
- tier: "paused",
9810
- allowModulePreload: false,
9811
- allowNetworkPrefetch: false,
9812
- maxPrefetchBytes: 0,
9813
- maxConcurrentModulePreloads: 0,
9814
- maxConcurrentNetworkPrefetches: 0,
9815
- reason: "hidden"
9816
- },
9817
- constrained: {
9818
- tier: "constrained",
9819
- allowModulePreload: false,
9820
- allowNetworkPrefetch: false,
9821
- maxPrefetchBytes: 0,
9822
- maxConcurrentModulePreloads: 0,
9823
- maxConcurrentNetworkPrefetches: 0,
9824
- reason: "environment"
9825
- },
9826
- balanced: {
9827
- tier: "balanced",
9828
- allowModulePreload: true,
9829
- allowNetworkPrefetch: true,
9830
- maxPrefetchBytes: 128 * 1024,
9831
- maxConcurrentModulePreloads: 1,
9832
- maxConcurrentNetworkPrefetches: 1,
9833
- reason: "environment"
9834
- },
9835
- eager: {
9836
- tier: "eager",
9837
- allowModulePreload: true,
9838
- allowNetworkPrefetch: true,
9839
- maxPrefetchBytes: 512 * 1024,
9840
- maxConcurrentModulePreloads: 2,
9841
- maxConcurrentNetworkPrefetches: 2,
9842
- reason: "default"
9843
- }
9844
- };
9845
- function withReason(tier, reason) {
9846
- return { ...budgets[tier], reason };
9847
- }
9848
- function feedbackIsPoor(feedback) {
9849
- if (!feedback || feedback.observations < 4) return false;
9850
- const failureRate = 1 - feedback.successes / feedback.observations;
9851
- const abandoned = feedback.cancellations + feedback.fallbacks;
9852
- 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;
9853
- }
9854
- function preloadKey(kind, action) {
9855
- return `${kind}:${action}`;
9856
- }
9857
- function flushExpiredPreloads(now = Date.now()) {
9858
- for (const [key, preload] of pendingPreloads) {
9859
- if (preload.expiresAt > now) continue;
9860
- recordLocalAdaptiveOutcome(usefulnessScopes[preload.kind], preload.action, {
9861
- success: false
9862
- });
9863
- pendingPreloads.delete(key);
9864
- }
9865
- }
9866
- function markAdaptiveResourceUsed(kind, action, now = Date.now()) {
9867
- flushExpiredPreloads(now);
9868
- const key = preloadKey(kind, action);
9869
- if (!pendingPreloads.has(key)) return;
9870
- pendingPreloads.delete(key);
9871
- recordLocalAdaptiveOutcome(
9872
- usefulnessScopes[kind],
9873
- action,
9874
- { success: true },
9875
- now
9876
- );
9877
- }
9878
- function computeAdaptiveResourceBudget(environment, feedback) {
9879
- if (!environment.visible) return withReason("paused", "hidden");
9880
- if (environment.saveData || ["slow-2g", "2g", "3g"].includes(environment.effectiveType ?? "")) {
9881
- return withReason("constrained", "environment");
9882
- }
9883
- const limitedDevice = (environment.deviceMemoryGb ?? 8) <= 4 || (environment.hardwareConcurrency ?? 8) <= 4;
9884
- const baseline = limitedDevice ? "balanced" : "eager";
9885
- if (!feedbackIsPoor(feedback)) {
9886
- return withReason(baseline, limitedDevice ? "environment" : "default");
9887
- }
9888
- return withReason(
9889
- baseline === "eager" ? "balanced" : "constrained",
9890
- "feedback"
9891
- );
9892
- }
9893
- function readEnvironment() {
9894
- const navigatorLike = typeof navigator === "undefined" ? void 0 : navigator;
9895
- return {
9896
- visible: typeof document === "undefined" || document.visibilityState !== "hidden",
9897
- saveData: navigatorLike?.connection?.saveData,
9898
- effectiveType: navigatorLike?.connection?.effectiveType,
9899
- deviceMemoryGb: navigatorLike?.deviceMemory,
9900
- hardwareConcurrency: navigatorLike?.hardwareConcurrency
9901
- };
9902
- }
9903
- function readFeedback(kind) {
9904
- flushExpiredPreloads();
9905
- const stats = Object.values(getLocalAdaptiveStats(scopes[kind]));
9906
- const usefulness = Object.values(
9907
- getLocalAdaptiveStats(usefulnessScopes[kind])
9908
- );
9909
- const observations = stats.reduce((sum, stat) => sum + stat.observations, 0);
9910
- const latencySamples = stats.filter(
9911
- (stat) => stat.latencyEwmaMs !== void 0 && stat.observations > 0
9912
- );
9913
- const latencyWeight = latencySamples.reduce(
9914
- (sum, stat) => sum + stat.observations,
9915
- 0
9916
- );
9917
- return {
9918
- observations,
9919
- successes: stats.reduce((sum, stat) => sum + stat.successes, 0),
9920
- cancellations: stats.reduce((sum, stat) => sum + stat.cancellations, 0),
9921
- fallbacks: stats.reduce((sum, stat) => sum + stat.fallbacks, 0),
9922
- usefulnessObservations: usefulness.reduce(
9923
- (sum, stat) => sum + stat.observations,
9924
- 0
9925
- ),
9926
- usefulPreloads: usefulness.reduce((sum, stat) => sum + stat.successes, 0),
9927
- ...latencyWeight > 0 ? {
9928
- latencyEwmaMs: latencySamples.reduce(
9929
- (sum, stat) => sum + (stat.latencyEwmaMs ?? 0) * stat.observations,
9930
- 0
9931
- ) / latencyWeight
9932
- } : {}
9933
- };
9934
- }
9935
- function getAdaptiveResourceBudget(kind) {
9936
- return computeAdaptiveResourceBudget(readEnvironment(), readFeedback(kind));
9937
- }
9938
- function runAdaptiveBackgroundTask(kind, action, task, options = {}) {
9939
- const budget = getAdaptiveResourceBudget(kind);
9940
- const allowed = kind === "module" ? budget.allowModulePreload : budget.allowNetworkPrefetch && (options.estimatedBytes === void 0 || options.estimatedBytes <= budget.maxPrefetchBytes);
9941
- const concurrency = kind === "module" ? budget.maxConcurrentModulePreloads : budget.maxConcurrentNetworkPrefetches;
9942
- if (!allowed || activeTasks[kind] >= concurrency) return false;
9943
- activeTasks[kind] += 1;
9944
- const key = preloadKey(kind, action);
9945
- pendingPreloads.set(key, {
9946
- kind,
9947
- action,
9948
- expiresAt: Date.now() + PRELOAD_USE_WINDOW_MS
9949
- });
9950
- const startedAt = performance.now();
9951
- void Promise.resolve().then(task).then(
9952
- () => {
9953
- activeTasks[kind] -= 1;
9954
- recordLocalAdaptiveOutcome(scopes[kind], action, {
9955
- success: true,
9956
- latencyMs: performance.now() - startedAt
9957
- });
9958
- },
9959
- () => {
9960
- activeTasks[kind] -= 1;
9961
- pendingPreloads.delete(key);
9962
- recordLocalAdaptiveOutcome(scopes[kind], action, {
9963
- success: false,
9964
- latencyMs: performance.now() - startedAt
9965
- });
9966
- }
9967
- );
9968
- return true;
9969
- }
9970
-
9971
- // src/ui/shell/tabUtils.tsx
9972
- init_define_import_meta_env();
9973
- import { lazy, memo as memo2, Suspense } from "react";
9974
- import { useTranslation as useTranslation9 } from "react-i18next";
9975
-
9976
8960
  // src/ui/lib/host-to-ssh-host.ts
9977
8961
  init_define_import_meta_env();
9978
8962
  function hostToSSHHost(h) {
@@ -10009,446 +8993,7 @@ function hostToSSHHost(h) {
10009
8993
  };
10010
8994
  }
10011
8995
 
10012
- // src/ui/shell/CompanionFrame.tsx
10013
- init_define_import_meta_env();
10014
- import {
10015
- memo,
10016
- useCallback as useCallback9,
10017
- useEffect as useEffect18,
10018
- useMemo as useMemo10,
10019
- useRef as useRef8,
10020
- useState as useState16
10021
- } from "react";
10022
- import { useTranslation as useTranslation8 } from "react-i18next";
10023
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
10024
- var DEFAULT_SIDE_SIZE = 360;
10025
- var DEFAULT_BOTTOM_SIZE = 280;
10026
- var MIN_SIZE = 200;
10027
- function DefaultsReader({
10028
- def,
10029
- onValue
10030
- }) {
10031
- const Reader = def.Defaults;
10032
- const report = useCallback9(
10033
- (value) => onValue(def.id, value),
10034
- [def.id, onValue]
10035
- );
10036
- return Reader ? /* @__PURE__ */ jsx13(Reader, { onValue: report }) : null;
10037
- }
10038
- function sideIcon(side) {
10039
- if (side === "left") return ArrowLeftToLine;
10040
- if (side === "bottom") return ArrowDownToLine;
10041
- return ArrowRightToLine;
10042
- }
10043
- var CompanionFrame = memo(function CompanionFrame2({
10044
- tab,
10045
- isVisible,
10046
- children
10047
- }) {
10048
- const defs = useCompanionDefs();
10049
- const layoutDefs = useCompanionLayoutDefs();
10050
- const applicable = useMemo10(
10051
- () => arrangeCompanions(
10052
- defs.filter((def) => companionApplies(def, tab)),
10053
- resolveCompanionLayout(tab.type, tab.host).order
10054
- ),
10055
- // tab.host and tab.type decide it
10056
- // eslint-disable-next-line react-hooks/exhaustive-deps
10057
- [defs, layoutDefs, tab.type, tab.host]
10058
- );
10059
- if (applicable.length === 0) return /* @__PURE__ */ jsx13(Fragment3, { children });
10060
- return /* @__PURE__ */ jsx13(CompanionLayout, { tab, defs: applicable, isVisible, children });
10061
- });
10062
- function CompanionLayout({
10063
- tab,
10064
- defs,
10065
- isVisible,
10066
- children
10067
- }) {
10068
- const { t } = useTranslation8();
10069
- const isMobile = useIsMobile();
10070
- const key = companionKey(tab);
10071
- const state2 = useCompanionState(key);
10072
- const [defaults3, setDefaults] = useState16(
10073
- {}
10074
- );
10075
- const [dragging, setDragging] = useState16(false);
10076
- const [mounted, setMounted] = useState16(/* @__PURE__ */ new Set());
10077
- const rootRef = useRef8(null);
10078
- const onDefaults = useCallback9((id, value) => {
10079
- setDefaults((prev) => {
10080
- const old = prev[id];
10081
- if (old && old.open === value.open && old.side === value.side && old.size === value.size && old.edgeButton === value.edgeButton) {
10082
- return prev;
10083
- }
10084
- return { ...prev, [id]: value };
10085
- });
10086
- }, []);
10087
- useEffect18(() => {
10088
- if (state2.seen || !isVisible) return;
10089
- const ready = defs.every(
10090
- (def) => !def.Defaults || defaults3[def.id] !== void 0
10091
- );
10092
- if (!ready) return;
10093
- const auto = defs.find((def) => defaults3[def.id]?.open);
10094
- patchCompanionState(key, { seen: true, openId: auto?.id ?? null });
10095
- }, [state2.seen, isVisible, defs, defaults3, key]);
10096
- const active = defs.find((def) => def.id === state2.openId) ?? null;
10097
- useEffect18(() => {
10098
- if (!active) return;
10099
- setMounted(
10100
- (prev) => prev.has(active.id) ? prev : new Set(prev).add(active.id)
10101
- );
10102
- }, [active]);
10103
- const activeDefaults = active ? defaults3[active.id] : void 0;
10104
- const layout = useMemo10(
10105
- () => resolveCompanionLayout(tab.type, tab.host),
10106
- [tab.type, tab.host]
10107
- );
10108
- const side = isMobile ? "bottom" : state2.side ?? layout.side ?? activeDefaults?.side ?? "right";
10109
- const baseSize = state2.size ?? activeDefaults?.size ?? (side === "bottom" ? DEFAULT_BOTTOM_SIZE : DEFAULT_SIDE_SIZE);
10110
- const setSide = useCallback9(
10111
- (next) => patchCompanionState(key, { side: next }),
10112
- [key]
10113
- );
10114
- const close = useCallback9(
10115
- () => patchCompanionState(key, { openId: null }),
10116
- [key]
10117
- );
10118
- const startResize = (event) => {
10119
- event.preventDefault();
10120
- const root = rootRef.current;
10121
- if (!root) return;
10122
- const rect = root.getBoundingClientRect();
10123
- const total = side === "bottom" ? rect.height : rect.width;
10124
- const max = Math.max(MIN_SIZE, total - MIN_SIZE);
10125
- setDragging(true);
10126
- const move = (e) => {
10127
- const raw = side === "right" ? rect.right - e.clientX : side === "left" ? e.clientX - rect.left : rect.bottom - e.clientY;
10128
- patchCompanionState(key, {
10129
- size: Math.round(Math.min(max, Math.max(MIN_SIZE, raw)))
10130
- });
10131
- };
10132
- const up = () => {
10133
- setDragging(false);
10134
- window.removeEventListener("mousemove", move);
10135
- window.removeEventListener("mouseup", up);
10136
- };
10137
- window.addEventListener("mousemove", move);
10138
- window.addEventListener("mouseup", up);
10139
- };
10140
- const getHandle = useCallback9(
10141
- () => tab.terminalRef?.current ?? null,
10142
- [tab.terminalRef]
10143
- );
10144
- const sshHost = useMemo10(
10145
- () => tab.host ? hostToSSHHost(tab.host) : void 0,
10146
- [tab.host]
10147
- );
10148
- const showEdge = defs.filter((def) => defaults3[def.id]?.edgeButton !== false);
10149
- const vertical = side === "bottom";
10150
- const clamped = vertical ? Math.min(baseSize, 2e3) : baseSize;
10151
- const paneStyle = isMobile ? { height: "55%" } : vertical ? { height: clamped } : { width: clamped };
10152
- const SideIcon = sideIcon(side);
10153
- return /* @__PURE__ */ jsxs6("div", { className: "flex h-full w-full min-h-0 min-w-0", children: [
10154
- defs.map(
10155
- (def) => def.Defaults ? /* @__PURE__ */ jsx13(DefaultsReader, { def, onValue: onDefaults }, def.id) : null
10156
- ),
10157
- /* @__PURE__ */ jsxs6(
10158
- "div",
10159
- {
10160
- ref: rootRef,
10161
- className: cn(
10162
- "relative flex flex-1 min-h-0 min-w-0",
10163
- vertical ? "flex-col" : side === "left" ? "flex-row-reverse" : "flex-row"
10164
- ),
10165
- children: [
10166
- /* @__PURE__ */ jsx13("div", { className: "relative flex-1 min-h-0 min-w-0", children }),
10167
- defs.map((def) => {
10168
- if (!mounted.has(def.id)) return null;
10169
- const open = active?.id === def.id;
10170
- const Component = def.component;
10171
- const Icon = def.icon;
10172
- return /* @__PURE__ */ jsxs6(
10173
- "div",
10174
- {
10175
- className: cn(
10176
- "relative flex-shrink-0 flex-col min-h-0 min-w-0 bg-background border-border",
10177
- open ? "flex" : "hidden",
10178
- vertical ? "border-t" : side === "left" ? "border-r" : "border-l",
10179
- isMobile && "absolute inset-x-0 bottom-0 z-20 shadow-lg"
10180
- ),
10181
- style: paneStyle,
10182
- children: [
10183
- !isMobile && /* @__PURE__ */ jsx13(
10184
- "div",
10185
- {
10186
- role: "separator",
10187
- "aria-label": t("companion.resize"),
10188
- onMouseDown: startResize,
10189
- className: cn(
10190
- "absolute z-10",
10191
- 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",
10192
- dragging && "bg-primary/30"
10193
- )
10194
- }
10195
- ),
10196
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 h-8 px-2 border-b border-border flex-shrink-0", children: [
10197
- Icon && /* @__PURE__ */ jsx13(Icon, { className: "size-3.5 text-muted-foreground" }),
10198
- /* @__PURE__ */ jsx13("span", { className: "text-xs font-medium truncate flex-1", children: def.titleKey ? t(def.titleKey) : def.id }),
10199
- !isMobile && /* @__PURE__ */ jsx13(
10200
- "button",
10201
- {
10202
- type: "button",
10203
- className: "p-1 text-muted-foreground hover:text-foreground",
10204
- title: t("companion.moveSide"),
10205
- onClick: () => setSide(
10206
- side === "right" ? "bottom" : side === "bottom" ? "left" : "right"
10207
- ),
10208
- children: /* @__PURE__ */ jsx13(SideIcon, { className: "size-3.5" })
10209
- }
10210
- ),
10211
- /* @__PURE__ */ jsx13(
10212
- "button",
10213
- {
10214
- type: "button",
10215
- className: "p-1 text-muted-foreground hover:text-foreground",
10216
- title: t("companion.close"),
10217
- onClick: close,
10218
- children: /* @__PURE__ */ jsx13(X, { className: "size-3.5" })
10219
- }
10220
- )
10221
- ] }),
10222
- /* @__PURE__ */ jsx13("div", { className: "flex-1 min-h-0 min-w-0 overflow-hidden", children: /* @__PURE__ */ jsx13(
10223
- Component,
10224
- {
10225
- tab,
10226
- host: tab.host,
10227
- sshHost,
10228
- getHandle,
10229
- side,
10230
- isVisible: isVisible && open,
10231
- close,
10232
- setSide
10233
- }
10234
- ) })
10235
- ]
10236
- },
10237
- def.id
10238
- );
10239
- })
10240
- ]
10241
- }
10242
- ),
10243
- 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) => {
10244
- const Icon = def.icon ?? PanelRight;
10245
- const open = active?.id === def.id;
10246
- return /* @__PURE__ */ jsx13(
10247
- "button",
10248
- {
10249
- type: "button",
10250
- title: def.titleKey ? t(def.titleKey) : def.id,
10251
- "aria-pressed": open,
10252
- onClick: () => patchCompanionState(key, {
10253
- seen: true,
10254
- openId: open ? null : def.id
10255
- }),
10256
- className: cn(
10257
- "p-1 text-muted-foreground hover:text-foreground",
10258
- open && "text-foreground bg-accent"
10259
- ),
10260
- children: /* @__PURE__ */ jsx13(Icon, { className: "size-3.5" })
10261
- },
10262
- def.id
10263
- );
10264
- }) })
10265
- ] });
10266
- }
10267
-
10268
- // src/ui/shell/tabUtils.tsx
10269
- import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
10270
- var PluginsTab = lazy(
10271
- () => import("./PluginsTab-HQGSDSHS.js").then((m) => ({ default: m.PluginsTab }))
10272
- );
10273
- function preloadTabSurface(type) {
10274
- const loader = getTabType(type)?.preload;
10275
- if (loader) runAdaptiveBackgroundTask("module", `tab:${type}`, loader);
10276
- }
10277
- function markTabSurfaceUsed(type) {
10278
- markAdaptiveResourceUsed("module", `tab:${type}`);
10279
- }
10280
- function EmptyState({
10281
- icon: Icon,
10282
- messageKey
10283
- }) {
10284
- const { t } = useTranslation9();
10285
- return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-center justify-center flex-1 gap-3 p-6 text-center", children: [
10286
- /* @__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" }) }),
10287
- /* @__PURE__ */ jsx14("span", { className: "text-sm font-semibold text-muted-foreground/60", children: t(messageKey) })
10288
- ] });
10289
- }
10290
- function TabChunkFallback() {
10291
- 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" }) });
10292
- }
10293
- function withTabSuspense(node) {
10294
- return /* @__PURE__ */ jsx14(Suspense, { fallback: /* @__PURE__ */ jsx14(TabChunkFallback, {}), children: node });
10295
- }
10296
- function PanelTabFrame({ children }) {
10297
- 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 }) });
10298
- }
10299
- function tabIcon(type) {
10300
- switch (type) {
10301
- case "user-profile":
10302
- case "admin-settings":
10303
- return /* @__PURE__ */ jsx14(Settings, { className: "size-3.5" });
10304
- case "split-screen":
10305
- return /* @__PURE__ */ jsx14(LayoutPanelLeft, { className: "size-3.5" });
10306
- case "plugins":
10307
- return /* @__PURE__ */ jsx14(Puzzle, { className: "size-3.5" });
10308
- default: {
10309
- const Icon = getTabType(type)?.icon;
10310
- return Icon ? /* @__PURE__ */ jsx14(Icon, { className: "size-3.5" }) : null;
10311
- }
10312
- }
10313
- }
10314
- var RegisteredTab = memo2(function RegisteredTab2({
10315
- def,
10316
- tab,
10317
- isVisible,
10318
- isFocusedPane,
10319
- inSplit,
10320
- shell: shell2
10321
- }) {
10322
- const { host, label } = tab;
10323
- if (def.requiresHost && !host) {
10324
- return /* @__PURE__ */ jsx14(
10325
- EmptyState,
10326
- {
10327
- icon: Server,
10328
- messageKey: def.noHostMessageKey ?? "hosts.noHostSelected"
10329
- }
10330
- );
10331
- }
10332
- const Component = def.component;
10333
- const content = /* @__PURE__ */ jsx14(
10334
- Component,
10335
- {
10336
- tab,
10337
- host,
10338
- sshHost: host ? hostToSSHHost(host) : void 0,
10339
- label,
10340
- isVisible,
10341
- isFocusedPane,
10342
- inSplit,
10343
- handleRef: trackTabHandle(
10344
- tab.terminalRef
10345
- ),
10346
- shell: shell2
10347
- }
10348
- );
10349
- return withTabSuspense(
10350
- /* @__PURE__ */ jsx14(CompanionFrame, { tab, isVisible, children: def.panelFrame ? /* @__PURE__ */ jsx14(PanelTabFrame, { children: content }) : content })
10351
- );
10352
- });
10353
- var PanelAsTab = memo2(function PanelAsTab2({
10354
- panel,
10355
- isVisible,
10356
- targetTab,
10357
- shell: shell2
10358
- }) {
10359
- const Panel = panel.component;
10360
- return withTabSuspense(
10361
- /* @__PURE__ */ jsx14(PanelTabFrame, { children: /* @__PURE__ */ jsx14(
10362
- Panel,
10363
- {
10364
- targetTab,
10365
- active: isVisible,
10366
- shell: shell2,
10367
- setEditing: noop,
10368
- placement: "tab"
10369
- }
10370
- ) })
10371
- );
10372
- });
10373
- function noop() {
10374
- }
10375
- function renderTabContent(tab, context) {
10376
- const {
10377
- shell: shell2,
10378
- isVisible = true,
10379
- isFocusedPane = true,
10380
- inSplit = false
10381
- } = context;
10382
- switch (tab.type) {
10383
- case "split-screen":
10384
- return null;
10385
- case "plugins":
10386
- return withTabSuspense(/* @__PURE__ */ jsx14(PluginsTab, {}));
10387
- case "user-profile":
10388
- case "admin-settings":
10389
- return null;
10390
- default: {
10391
- const def = getTabType(tab.type);
10392
- if (!def && tab.type === "dashboard") return null;
10393
- if (!def) {
10394
- const panel = getPanel(tab.type);
10395
- if (panel) {
10396
- return /* @__PURE__ */ jsx14(
10397
- PanelAsTab,
10398
- {
10399
- panel,
10400
- isVisible,
10401
- targetTab: context.panelTargetTab,
10402
- shell: shell2
10403
- }
10404
- );
10405
- }
10406
- return /* @__PURE__ */ jsx14(PluginViewPlaceholder, { kind: "tab", viewId: tab.type });
10407
- }
10408
- return /* @__PURE__ */ jsx14(
10409
- RegisteredTab,
10410
- {
10411
- def,
10412
- tab,
10413
- isVisible,
10414
- isFocusedPane,
10415
- inSplit,
10416
- shell: shell2
10417
- }
10418
- );
10419
- }
10420
- }
10421
- }
10422
-
10423
- // src/ui/lib/local-adaptive-preferences.ts
10424
- init_define_import_meta_env();
10425
- var LOCAL_ADAPTIVE_PREFERENCES_KEY = LOCAL_ADAPTIVE_ENGINE_KEY;
10426
- function recordLocalPreference(scope, action, now = Date.now()) {
10427
- recordLocalAdaptiveChoice(scope, action, now);
10428
- }
10429
- function getLocalPreference(scope, candidates, fallback, now = Date.now()) {
10430
- return chooseLocalAdaptiveAction({
10431
- scope,
10432
- candidates,
10433
- fallback,
10434
- now
10435
- }).action;
10436
- }
10437
- function clearLocalAdaptivePreferences() {
10438
- clearLocalAdaptiveEngine();
10439
- }
10440
- var hostActionScope = (hostId) => `host-action:${hostId}`;
10441
- function recordHostActionPreference(hostId, action) {
10442
- recordLocalPreference(hostActionScope(hostId), action);
10443
- }
10444
- function getPreferredHostAction(hostId, candidates, fallback) {
10445
- return getLocalPreference(hostActionScope(hostId), candidates, fallback);
10446
- }
10447
-
10448
8996
  export {
10449
- Checkbox2 as Checkbox,
10450
- PasswordInput,
10451
- Textarea,
10452
8997
  rem,
10453
8998
  remScale,
10454
8999
  useIsMobile,
@@ -10487,6 +9032,14 @@ export {
10487
9032
  isCoreRailView,
10488
9033
  railItemLabel,
10489
9034
  resetRegisteredRailItems,
9035
+ useCompanionDefs,
9036
+ useCompanionLayoutDefs,
9037
+ resolveCompanionLayout,
9038
+ companionApplies,
9039
+ arrangeCompanions,
9040
+ companionKey,
9041
+ patchCompanionState,
9042
+ useCompanionState,
10490
9043
  registerHostEditorSection,
10491
9044
  unregisterHostEditorSection,
10492
9045
  getHostEditorSection,
@@ -10540,8 +9093,10 @@ export {
10540
9093
  listExtensions,
10541
9094
  getSettingsComponent,
10542
9095
  listSettingsComponents,
9096
+ useSettingsComponents,
10543
9097
  registerAction,
10544
9098
  listActions,
9099
+ invokeAction,
10545
9100
  declareActionSlot,
10546
9101
  registerSlotContribution,
10547
9102
  getSlotContributions,
@@ -10626,25 +9181,5 @@ export {
10626
9181
  useLayouts,
10627
9182
  layoutLabel,
10628
9183
  createPluginApp,
10629
- useInstalledPlugins,
10630
- SettingsFieldRow,
10631
- usePluginHostSections,
10632
- HostPluginSections,
10633
- HostFeatureFields,
10634
- ActionSlot,
10635
- ComponentSlot,
10636
- trackTabHandle,
10637
- useTabHandlesVersion,
10638
- PluginViewPlaceholder,
10639
- markAdaptiveResourceUsed,
10640
- getAdaptiveResourceBudget,
10641
- runAdaptiveBackgroundTask,
10642
- preloadTabSurface,
10643
- markTabSurfaceUsed,
10644
- tabIcon,
10645
- renderTabContent,
10646
- LOCAL_ADAPTIVE_PREFERENCES_KEY,
10647
- clearLocalAdaptivePreferences,
10648
- recordHostActionPreference,
10649
- getPreferredHostAction
9184
+ hostToSSHHost
10650
9185
  };