@termix-ssh/plugin-sdk 1.0.21 → 1.0.23

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 (45) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/PLUGIN-GUIDE.md +130 -3
  3. package/cli/commands/build.mjs +55 -36
  4. package/cli/commands/validate.mjs +4 -0
  5. package/dist/backend.d.ts +139 -7
  6. package/dist/capabilities.js +3 -0
  7. package/dist/frontend.d.ts +7 -0
  8. package/dist/host/{PluginsTab-HQGSDSHS.js → PluginsTab-MNQU5OJ3.js} +22 -14
  9. package/dist/host/{chunk-JFQGI4OD.js → chunk-6WLECSNB.js} +71 -12
  10. package/dist/host/chunk-BP42N374.js +53 -0
  11. package/dist/host/{chunk-WWFRBU2E.js → chunk-C52NRTA6.js} +82 -51
  12. package/dist/host/{chunk-GTHINW3L.js → chunk-DN6YKMFJ.js} +34650 -34968
  13. package/dist/host/chunk-GBBKHJRQ.js +1526 -0
  14. package/dist/host/{chunk-FGX27MAA.js → chunk-GRH7GSKG.js} +1 -1
  15. package/dist/host/chunk-IMPLAAUM.js +345 -0
  16. package/dist/host/{chunk-A5CULDWV.js → chunk-X65V7ZRW.js} +121 -1574
  17. package/dist/host/core-classes.json +1 -1
  18. package/dist/host/frame-host.css +2 -0
  19. package/dist/host/frame-host.js +242 -0
  20. package/dist/host/{linked-server-UDSFY3N7.js → linked-server-YCAJ2NX3.js} +1 -1
  21. package/dist/host/{rbac-api-AAAPMF52.js → rbac-api-A64TXDL5.js} +5 -1
  22. package/dist/host/testing-host.js +9 -6
  23. package/dist/host/types/src/types/host-sidebar-preferences.d.ts +2 -0
  24. package/dist/host/types/src/types/index.d.ts +8 -0
  25. package/dist/host/types/src/ui/api/plugins-api.d.ts +2 -1
  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/db-health-monitor.d.ts +5 -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/use-plugins-manager.d.ts +1 -1
  33. package/dist/host/types/src/ui/shell/tab-registry.d.ts +2 -0
  34. package/dist/host/ui.js +357 -370
  35. package/dist/host/{use-permissions-VAND5GEG.js → use-permissions-FLQAN6I7.js} +1 -1
  36. package/dist/host-commands.d.ts +7 -0
  37. package/dist/host-commands.js +35 -5
  38. package/dist/manifest.d.ts +33 -1
  39. package/dist/manifest.js +114 -1
  40. package/dist/operations.d.ts +222 -0
  41. package/dist/operations.js +650 -0
  42. package/dist/testing.d.ts +9 -1
  43. package/dist/testing.js +45 -0
  44. package/package.json +9 -1
  45. package/schema/manifest.schema.json +67 -0
@@ -3,65 +3,33 @@ import {
3
3
  hasPermission,
4
4
  resetPermissionsCache,
5
5
  usePermissions
6
- } from "./chunk-FGX27MAA.js";
6
+ } from "./chunk-GRH7GSKG.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-DN6YKMFJ.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-6WLECSNB.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.",
@@ -2119,6 +2010,18 @@ var en_default = {
2119
2010
  title: "Send alerts",
2120
2011
  consequence: "To you or other users, and through the channels they set up."
2121
2012
  },
2013
+ "operations:provide": {
2014
+ title: "Offer actions",
2015
+ consequence: "Its actions can be run from Automations, the CLI, the phone app and other places, by people allowed to use them."
2016
+ },
2017
+ "operations:invoke": {
2018
+ title: "Run other plugins' actions",
2019
+ consequence: "Acts for the signed-in user on the actions its manifest lists. Approval rules still apply."
2020
+ },
2021
+ "operations:approve": {
2022
+ title: "Decide what needs approval",
2023
+ consequence: "Can let actions run with no approval, or block them, for every user."
2024
+ },
2122
2025
  "notify:hub": {
2123
2026
  title: "Receive every alert",
2124
2027
  consequence: "Stores and delivers the alerts all plugins send, including their contents."
@@ -2862,7 +2765,7 @@ function resetRegisteredRailItems() {
2862
2765
 
2863
2766
  // src/ui/sidebar/HostManagerTabs.tsx
2864
2767
  init_define_import_meta_env();
2865
- import { jsx as jsx4 } from "react/jsx-runtime";
2768
+ import { jsx } from "react/jsx-runtime";
2866
2769
  var sections = createRegistry(byOrderThenId);
2867
2770
  var registerHostEditorSection = sections.register;
2868
2771
  var unregisterHostEditorSection = sections.unregister;
@@ -2886,7 +2789,7 @@ function mergeByOrder(core, group, t, protocols2, defaultsMode = false) {
2886
2789
  return {
2887
2790
  id: section.id,
2888
2791
  label: t(section.labelKey, { defaultValue: section.label }),
2889
- icon: Icon ? /* @__PURE__ */ jsx4(Icon, { className: "size-3" }) : null,
2792
+ icon: Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-3" }) : null,
2890
2793
  order: section.order ?? 1e3
2891
2794
  };
2892
2795
  });
@@ -2898,13 +2801,13 @@ function makeHostTabs(t, protocols2, defaultsMode = false) {
2898
2801
  {
2899
2802
  id: "general",
2900
2803
  label: t("hosts.tabGeneral"),
2901
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2804
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2902
2805
  order: 0
2903
2806
  },
2904
2807
  {
2905
2808
  id: "ssh",
2906
2809
  label: t("hosts.tabSsh"),
2907
- icon: /* @__PURE__ */ jsx4(Terminal, { className: "size-3" }),
2810
+ icon: /* @__PURE__ */ jsx(Terminal, { className: "size-3" }),
2908
2811
  order: 10
2909
2812
  }
2910
2813
  ],
@@ -2920,7 +2823,7 @@ function makeHostSshSubTabs(t, protocols2, defaultsMode = false) {
2920
2823
  {
2921
2824
  id: "ssh",
2922
2825
  label: t("hosts.tabGeneral"),
2923
- icon: /* @__PURE__ */ jsx4(Settings, { className: "size-3" }),
2826
+ icon: /* @__PURE__ */ jsx(Settings, { className: "size-3" }),
2924
2827
  order: 0
2925
2828
  }
2926
2829
  ],
@@ -3394,7 +3297,7 @@ async function pluginWsUrl(pluginId, path2, options = {}) {
3394
3297
  }
3395
3298
  const token = await fetchSocketTicket() ?? localStorage.getItem("jwt");
3396
3299
  const protocols2 = websocketAuthProtocols(token);
3397
- const devProxy = !define_import_meta_env_default.VITE_API_HOST;
3300
+ const devProxy = false;
3398
3301
  const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
3399
3302
  if (devProxy) {
3400
3303
  return {
@@ -3470,7 +3373,7 @@ function sectionPosition(section, order2 = 0, position) {
3470
3373
 
3471
3374
  // src/ui/hooks/use-status-color-scheme.ts
3472
3375
  init_define_import_meta_env();
3473
- import { useState as useState3, useEffect as useEffect2 } from "react";
3376
+ import { useState as useState2, useEffect as useEffect2 } from "react";
3474
3377
  var PREFS_KEY = "hostSidebarPreferences";
3475
3378
  var PREFS_EVENT = "hostSidebarPreferencesChanged";
3476
3379
  function readStatusColorScheme() {
@@ -3484,7 +3387,7 @@ function readStatusColorScheme() {
3484
3387
  }
3485
3388
  }
3486
3389
  function useStatusColorScheme() {
3487
- const [scheme, setScheme] = useState3(
3390
+ const [scheme, setScheme] = useState2(
3488
3391
  readStatusColorScheme
3489
3392
  );
3490
3393
  useEffect2(() => {
@@ -3524,10 +3427,6 @@ function getStatusClasses(status, scheme, variant, loading = false) {
3524
3427
  return online ? "border-accent-brand/40 text-accent-brand bg-accent-brand/10" : "border-border/50 text-muted-foreground/60 bg-muted/30";
3525
3428
  }
3526
3429
 
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
3430
  // src/ui/look/theme-registry.ts
3532
3431
  init_define_import_meta_env();
3533
3432
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
@@ -3611,6 +3510,10 @@ function arePluginThemesKnown() {
3611
3510
  return pluginThemesKnown;
3612
3511
  }
3613
3512
 
3513
+ // src/ui/components/theme-provider.tsx
3514
+ init_define_import_meta_env();
3515
+ import { createContext, useContext, useEffect as useEffect3, useState as useState3 } from "react";
3516
+
3614
3517
  // src/ui/look/theme-css.ts
3615
3518
  init_define_import_meta_env();
3616
3519
  var LINK_ID = "termix-theme-css";
@@ -3661,7 +3564,7 @@ function applyThemeCss(id, theme) {
3661
3564
  }
3662
3565
 
3663
3566
  // src/ui/components/theme-provider.tsx
3664
- import { jsx as jsx5 } from "react/jsx-runtime";
3567
+ import { jsx as jsx2 } from "react/jsx-runtime";
3665
3568
  var initialState = {
3666
3569
  theme: "dark",
3667
3570
  setTheme: () => null,
@@ -3675,10 +3578,10 @@ function ThemeProvider({
3675
3578
  storageKey = "vite-ui-theme",
3676
3579
  ...props
3677
3580
  }) {
3678
- const [theme, setTheme] = useState4(
3581
+ const [theme, setTheme] = useState3(
3679
3582
  () => localStorage.getItem(storageKey) || defaultTheme
3680
3583
  );
3681
- const [previewTheme, setPreviewTheme] = useState4(null);
3584
+ const [previewTheme, setPreviewTheme] = useState3(null);
3682
3585
  const themes = useThemes();
3683
3586
  const shownTheme = effectiveThemeId(theme);
3684
3587
  const activeTheme = previewTheme ?? shownTheme;
@@ -3701,7 +3604,7 @@ function ThemeProvider({
3701
3604
  setPreviewTheme(preview);
3702
3605
  }
3703
3606
  };
3704
- return /* @__PURE__ */ jsx5(ThemeProviderContext.Provider, { ...props, value, children });
3607
+ return /* @__PURE__ */ jsx2(ThemeProviderContext.Provider, { ...props, value, children });
3705
3608
  }
3706
3609
  var useTheme = () => {
3707
3610
  const context = useContext(ThemeProviderContext);
@@ -3712,7 +3615,7 @@ var useTheme = () => {
3712
3615
 
3713
3616
  // src/ui/hooks/useSshAuthProviders.ts
3714
3617
  init_define_import_meta_env();
3715
- import { useEffect as useEffect4, useMemo as useMemo2, useState as useState5 } from "react";
3618
+ import { useEffect as useEffect4, useMemo as useMemo2, useState as useState4 } from "react";
3716
3619
  var BUILTIN_TYPES = ["password", "key", "credential", "agent", "none"];
3717
3620
  function builtinSummary(type) {
3718
3621
  return {
@@ -3730,7 +3633,7 @@ function builtinSummary(type) {
3730
3633
  var cache = null;
3731
3634
  function useSshAuthProviders() {
3732
3635
  const editors = useSshAuthEditors();
3733
- const [summaries, setSummaries] = useState5(
3636
+ const [summaries, setSummaries] = useState4(
3734
3637
  null
3735
3638
  );
3736
3639
  useEffect4(() => {
@@ -3812,14 +3715,14 @@ import {
3812
3715
  useContext as useContext2,
3813
3716
  useEffect as useEffect5,
3814
3717
  useMemo as useMemo4,
3815
- useState as useState6
3718
+ useState as useState5
3816
3719
  } from "react";
3817
3720
 
3818
3721
  // src/ui/contexts/branding-document.ts
3819
3722
  init_define_import_meta_env();
3820
3723
 
3821
3724
  // src/ui/contexts/BrandingContext.tsx
3822
- import { jsx as jsx6 } from "react/jsx-runtime";
3725
+ import { jsx as jsx3 } from "react/jsx-runtime";
3823
3726
  var DEFAULT_APP_NAME = "Termix";
3824
3727
  var BrandingContext = createContext2({
3825
3728
  ready: false,
@@ -5210,7 +5113,7 @@ import {
5210
5113
  useLayoutEffect as useLayoutEffect2,
5211
5114
  useMemo as useMemo5,
5212
5115
  useRef as useRef6,
5213
- useState as useState12
5116
+ useState as useState11
5214
5117
  } from "react";
5215
5118
 
5216
5119
  // src/ui/shell/stage/tab-stage-store.ts
@@ -5218,14 +5121,14 @@ init_define_import_meta_env();
5218
5121
  function createTabStageStore() {
5219
5122
  const nodes = /* @__PURE__ */ new Map();
5220
5123
  const paneEls = /* @__PURE__ */ new Map();
5221
- const listeners9 = /* @__PURE__ */ new Set();
5124
+ const listeners8 = /* @__PURE__ */ new Set();
5222
5125
  let normalView = null;
5223
- let version4 = 0;
5126
+ let version3 = 0;
5224
5127
  let order2 = [];
5225
5128
  let lastAnimatedTabId = null;
5226
5129
  const bump = () => {
5227
- version4 += 1;
5228
- for (const listener of listeners9) listener();
5130
+ version3 += 1;
5131
+ for (const listener of listeners8) listener();
5229
5132
  };
5230
5133
  function getNode(tabId, ownBackground) {
5231
5134
  let el = nodes.get(tabId);
@@ -5327,18 +5230,18 @@ function createTabStageStore() {
5327
5230
  setNormalView,
5328
5231
  place,
5329
5232
  subscribe: (listener) => {
5330
- listeners9.add(listener);
5233
+ listeners8.add(listener);
5331
5234
  return () => {
5332
- listeners9.delete(listener);
5235
+ listeners8.delete(listener);
5333
5236
  };
5334
5237
  },
5335
- version: () => version4
5238
+ version: () => version3
5336
5239
  };
5337
5240
  }
5338
5241
 
5339
5242
  // src/ui/shell/controller/use-regions.ts
5340
5243
  init_define_import_meta_env();
5341
- import { useCallback as useCallback2, useEffect as useEffect6, useRef, useState as useState7 } from "react";
5244
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef, useState as useState6 } from "react";
5342
5245
  var MIN_WIDTH = 160;
5343
5246
  var MAX_WIDTH = 480;
5344
5247
  function clampWidth(width) {
@@ -5371,15 +5274,15 @@ function startDockDrag(e, startW, sign, setWidth, setDragging) {
5371
5274
  }
5372
5275
  var DOCK_DEFAULT_WIDTH = 291;
5373
5276
  function useRegions(isMobile) {
5374
- const [sidebarOpen, setSidebarOpen] = useState7(true);
5375
- const [railView, setRailView] = useState7("hosts");
5376
- const [sidebarWidth, setSidebarWidth] = useState7(() => {
5277
+ const [sidebarOpen, setSidebarOpen] = useState6(true);
5278
+ const [railView, setRailView] = useState6("hosts");
5279
+ const [sidebarWidth, setSidebarWidth] = useState6(() => {
5377
5280
  const saved = localStorage.getItem("termix_sidebarWidth");
5378
5281
  return saved ? parseInt(saved, 10) : 291;
5379
5282
  });
5380
- const [sidebarDragging, setSidebarDragging] = useState7(false);
5381
- const [panelEditing, setPanelEditing] = useState7({});
5382
- const [scopeEditing, setScopeEditing] = useState7({});
5283
+ const [sidebarDragging, setSidebarDragging] = useState6(false);
5284
+ const [panelEditing, setPanelEditing] = useState6({});
5285
+ const [scopeEditing, setScopeEditing] = useState6({});
5383
5286
  const editingSetters = useRef(
5384
5287
  /* @__PURE__ */ new Map()
5385
5288
  );
@@ -5405,15 +5308,15 @@ function useRegions(isMobile) {
5405
5308
  );
5406
5309
  return editingSetters.current.get(key);
5407
5310
  }, []);
5408
- const [rightRailView, setRightRailView] = useState7(() => {
5311
+ const [rightRailView, setRightRailView] = useState6(() => {
5409
5312
  const saved = localStorage.getItem("termix_rightRailView");
5410
5313
  return saved && rightDockableIds().includes(saved) ? saved : null;
5411
5314
  });
5412
- const [rightSidebarWidth, setRightSidebarWidth] = useState7(() => {
5315
+ const [rightSidebarWidth, setRightSidebarWidth] = useState6(() => {
5413
5316
  const saved = localStorage.getItem("termix_rightSidebarWidth");
5414
5317
  return saved ? parseInt(saved, 10) : 291;
5415
5318
  });
5416
- const [rightSidebarDragging, setRightSidebarDragging] = useState7(false);
5319
+ const [rightSidebarDragging, setRightSidebarDragging] = useState6(false);
5417
5320
  const lastRightRailViewRef = useRef(
5418
5321
  localStorage.getItem("termix_lastRightRailView")
5419
5322
  );
@@ -5524,7 +5427,7 @@ function useRegions(isMobile) {
5524
5427
 
5525
5428
  // src/ui/shell/controller/use-hosts.ts
5526
5429
  init_define_import_meta_env();
5527
- import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef2, useState as useState8 } from "react";
5430
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef2, useState as useState7 } from "react";
5528
5431
 
5529
5432
  // src/ui/sidebar/build-host-tree.ts
5530
5433
  init_define_import_meta_env();
@@ -5596,10 +5499,10 @@ function buildHostTree(hosts, folderMeta) {
5596
5499
 
5597
5500
  // src/ui/shell/controller/use-hosts.ts
5598
5501
  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([]);
5502
+ const [tree, setTree] = useState7(null);
5503
+ const [loading, setLoading] = useState7(true);
5504
+ const [loaded, setLoaded] = useState7(false);
5505
+ const [list, setList] = useState7([]);
5603
5506
  const listRef = useRef2(list);
5604
5507
  listRef.current = list;
5605
5508
  const reload = useCallback3(async () => {
@@ -5656,7 +5559,7 @@ import {
5656
5559
  useCallback as useCallback4,
5657
5560
  useEffect as useEffect8,
5658
5561
  useRef as useRef3,
5659
- useState as useState9
5562
+ useState as useState8
5660
5563
  } from "react";
5661
5564
  import { toast } from "sonner";
5662
5565
  import { useTranslation } from "react-i18next";
@@ -5708,10 +5611,10 @@ function useTabs({
5708
5611
  }) {
5709
5612
  const { t } = useTranslation();
5710
5613
  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([]);
5614
+ const [tabs, setTabs] = useState8([dashboardTab(t("nav.dashboard"))]);
5615
+ const [activeTabId, setActiveTabId] = useState8("dashboard");
5616
+ const [tabsReady, setTabsReady] = useState8(false);
5617
+ const [backgroundTabRecords, setBackgroundTabRecords] = useState8([]);
5715
5618
  const paneLayoutRestoredRef = useRef3(false);
5716
5619
  const splitTabsRestoredRef = useRef3(false);
5717
5620
  const splitOpsRef = useRef3({
@@ -5800,14 +5703,14 @@ function useTabs({
5800
5703
  if (!isSplitTab(active)) return activeTabId;
5801
5704
  return findPane(active.split, active.split.focusedPaneId)?.tabId ?? "";
5802
5705
  })();
5803
- const [lastTerminalTabId, setLastTerminalTabId] = useState9(activeTabId);
5706
+ const [lastTerminalTabId, setLastTerminalTabId] = useState8(activeTabId);
5804
5707
  useEffect8(() => {
5805
5708
  const working = tabs.find((t2) => t2.id === workingTabId);
5806
5709
  if (working && getTabType(working.type)?.commandTarget) {
5807
5710
  setLastTerminalTabId(working.id);
5808
5711
  }
5809
5712
  }, [workingTabId, tabs]);
5810
- const [showTabNumbers, setShowTabNumbers] = useState9(
5713
+ const [showTabNumbers, setShowTabNumbers] = useState8(
5811
5714
  () => localStorage.getItem("showTabNumbers") === "true"
5812
5715
  );
5813
5716
  useEffect8(() => {
@@ -6361,7 +6264,7 @@ function useSplit({
6361
6264
  tabsState,
6362
6265
  isMobile,
6363
6266
  paneEls,
6364
- callbacks: callbacks2,
6267
+ callbacks,
6365
6268
  resizeAllTerminals
6366
6269
  }) {
6367
6270
  const { t } = useTranslation2();
@@ -6760,8 +6663,8 @@ function useSplit({
6760
6663
  };
6761
6664
  }
6762
6665
  const pickerShell = {
6763
- ...callbacks2,
6764
- openTab: (host, type, options) => callbacks2.openTab(host, type, { ...options, forceNewTab: true })
6666
+ ...callbacks,
6667
+ openTab: (host, type, options) => callbacks.openTab(host, type, { ...options, forceNewTab: true })
6765
6668
  };
6766
6669
  const split = {
6767
6670
  active: activeSplit,
@@ -6927,7 +6830,7 @@ function useWorkspaceLayout(tabsState, regions) {
6927
6830
 
6928
6831
  // src/ui/shell/controller/use-shell-keybindings.ts
6929
6832
  init_define_import_meta_env();
6930
- import { useEffect as useEffect11, useRef as useRef5, useState as useState10 } from "react";
6833
+ import { useEffect as useEffect11, useRef as useRef5, useState as useState9 } from "react";
6931
6834
 
6932
6835
  // src/ui/lib/pane-nav-hotkey.ts
6933
6836
  init_define_import_meta_env();
@@ -7031,7 +6934,7 @@ function useShellKeybindings({
7031
6934
  closeActiveTabRef.current();
7032
6935
  });
7033
6936
  }, []);
7034
- const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState10(() => {
6937
+ const [commandPaletteShortcutEnabled, setCommandPaletteShortcutEnabled] = useState9(() => {
7035
6938
  const v = localStorage.getItem("commandPaletteShortcutEnabled");
7036
6939
  return v !== null ? v === "true" : true;
7037
6940
  });
@@ -7098,7 +7001,7 @@ function useShellKeybindings({
7098
7001
  const tabDigit = getTabJumpDigit(e);
7099
7002
  if (tabDigit !== null) {
7100
7003
  const currentTabs = tabsRef.current.filter(
7101
- (tab) => !tab.parentSplitTabId
7004
+ (tab) => !tab.parentSplitTabId && tab.type !== "dashboard"
7102
7005
  );
7103
7006
  const index = tabDigit - 1;
7104
7007
  if (index < currentTabs.length) {
@@ -7144,7 +7047,7 @@ function useShellKeybindings({
7144
7047
 
7145
7048
  // src/ui/shell/controller/use-shell-lifecycle.ts
7146
7049
  init_define_import_meta_env();
7147
- import { useCallback as useCallback5, useEffect as useEffect12, useState as useState11 } from "react";
7050
+ import { useCallback as useCallback5, useEffect as useEffect12, useState as useState10 } from "react";
7148
7051
  import { toast as toast3 } from "sonner";
7149
7052
  import { useTranslation as useTranslation3 } from "react-i18next";
7150
7053
  var SNAPSHOT_KEYS = [
@@ -7223,12 +7126,12 @@ function useShellLifecycle({
7223
7126
  }) {
7224
7127
  const { t, i18n: i18n2 } = useTranslation3();
7225
7128
  const { setTheme } = useTheme();
7226
- const [userPrefs, setUserPrefs] = useState11({
7129
+ const [userPrefs, setUserPrefs] = useState10({
7227
7130
  reopenTabsOnLogin: false
7228
7131
  });
7229
- const [userPrefsLoaded, setUserPrefsLoaded] = useState11(false);
7230
- const [isAdmin, setIsAdmin] = useState11(false);
7231
- const [isAppFullscreen, setIsAppFullscreen] = useState11(
7132
+ const [userPrefsLoaded, setUserPrefsLoaded] = useState10(false);
7133
+ const [isAdmin, setIsAdmin] = useState10(false);
7134
+ const [isAppFullscreen, setIsAppFullscreen] = useState10(
7232
7135
  () => !!document.fullscreenElement
7233
7136
  );
7234
7137
  useEffect12(() => {
@@ -7359,7 +7262,7 @@ function useShellSnapshot() {
7359
7262
  }
7360
7263
 
7361
7264
  // src/ui/shell/controller/ShellControllerProvider.tsx
7362
- import { jsx as jsx7 } from "react/jsx-runtime";
7265
+ import { jsx as jsx4 } from "react/jsx-runtime";
7363
7266
  var ShellControllerContext = createContext3(null);
7364
7267
  var TabStageContext = createContext3(null);
7365
7268
  function useShellController() {
@@ -7391,7 +7294,7 @@ function ShellControllerProvider({
7391
7294
  useTabTypes();
7392
7295
  const { settled: pluginsSettled } = usePluginStore();
7393
7296
  const isMobile = useIsMobile();
7394
- const [stageStore] = useState12(createTabStageStore);
7297
+ const [stageStore] = useState11(createTabStageStore);
7395
7298
  const confirmRef = useRef6(null);
7396
7299
  const openSingletonRef = useRef6(
7397
7300
  () => void 0
@@ -7402,7 +7305,7 @@ function ShellControllerProvider({
7402
7305
  });
7403
7306
  const regions = useRegions(isMobile);
7404
7307
  const hosts = useHostList();
7405
- const [paletteOpen, setPaletteOpen] = useState12(false);
7308
+ const [paletteOpen, setPaletteOpen] = useState11(false);
7406
7309
  const tabsState = useTabs({
7407
7310
  hosts: hosts.list,
7408
7311
  hostsRef: hosts.listRef,
@@ -7459,6 +7362,10 @@ function ShellControllerProvider({
7459
7362
  regions.right.setView((prev) => prev === id ? null : prev);
7460
7363
  },
7461
7364
  openHostEditor: (draft) => requestManage({ kind: "host", hostId: null, draft }),
7365
+ editHost: (hostId) => {
7366
+ requestManage({ kind: "host", hostId: String(hostId) });
7367
+ if (isMobile) regions.left.setOpen(false);
7368
+ },
7462
7369
  saveQuickConnect: tabsState.saveQuickConnectHost,
7463
7370
  confirm: (options) => confirmRef.current ? confirmRef.current(options) : Promise.resolve(false)
7464
7371
  };
@@ -7466,7 +7373,7 @@ function ShellControllerProvider({
7466
7373
  useLayoutEffect2(() => {
7467
7374
  shellImplRef.current = shellCallbacksImpl;
7468
7375
  });
7469
- const callbacks2 = useMemo5(
7376
+ const callbacks = useMemo5(
7470
7377
  () => ({
7471
7378
  openTab: (...args) => shellImplRef.current.openTab(...args),
7472
7379
  openSingletonTab: (...args) => shellImplRef.current.openSingletonTab(...args),
@@ -7476,6 +7383,7 @@ function ShellControllerProvider({
7476
7383
  openRailView: (...args) => shellImplRef.current.openRailView(...args),
7477
7384
  closeRailView: (...args) => shellImplRef.current.closeRailView(...args),
7478
7385
  openHostEditor: (...args) => shellImplRef.current.openHostEditor?.(...args),
7386
+ editHost: (...args) => shellImplRef.current.editHost?.(...args),
7479
7387
  saveQuickConnect: (...args) => shellImplRef.current.saveQuickConnect(...args),
7480
7388
  confirm: (...args) => shellImplRef.current.confirm(...args)
7481
7389
  }),
@@ -7483,13 +7391,13 @@ function ShellControllerProvider({
7483
7391
  );
7484
7392
  const panelCallbacks = useMemo5(
7485
7393
  () => ({
7486
- ...callbacks2,
7394
+ ...callbacks,
7487
7395
  openTab: (...args) => {
7488
- callbacks2.openTab(...args);
7396
+ callbacks.openTab(...args);
7489
7397
  if (isMobile) regions.left.setOpen(false);
7490
7398
  }
7491
7399
  }),
7492
- [callbacks2, isMobile]
7400
+ [callbacks, isMobile]
7493
7401
  );
7494
7402
  const resizeAllTerminals = useCallback6(() => {
7495
7403
  const id = requestAnimationFrame(() => {
@@ -7506,7 +7414,7 @@ function ShellControllerProvider({
7506
7414
  tabsState,
7507
7415
  isMobile,
7508
7416
  paneEls: stageStore.paneEls,
7509
- callbacks: callbacks2,
7417
+ callbacks,
7510
7418
  resizeAllTerminals
7511
7419
  });
7512
7420
  useEffect13(() => {
@@ -7531,7 +7439,7 @@ function ShellControllerProvider({
7531
7439
  toggleFullscreen: lifecycle.toggleAppFullscreen
7532
7440
  });
7533
7441
  useEffect13(() => {
7534
- setShellCallbacks(callbacks2);
7442
+ setShellCallbacks(callbacks);
7535
7443
  });
7536
7444
  const activeTab = tabs.find((tab) => tab.id === activeTabId);
7537
7445
  const controller = {
@@ -7580,7 +7488,7 @@ function ShellControllerProvider({
7580
7488
  active: lifecycle.isAppFullscreen,
7581
7489
  toggle: lifecycle.toggleAppFullscreen
7582
7490
  },
7583
- callbacks: callbacks2,
7491
+ callbacks,
7584
7492
  panelCallbacks,
7585
7493
  settings: {
7586
7494
  username,
@@ -7597,7 +7505,7 @@ function ShellControllerProvider({
7597
7505
  publishShellController(controller);
7598
7506
  });
7599
7507
  useEffect13(() => () => publishShellController(null), []);
7600
- return /* @__PURE__ */ jsx7(TabStageContext.Provider, { value: stageStore, children: /* @__PURE__ */ jsx7(ShellControllerContext.Provider, { value: controller, children }) });
7508
+ return /* @__PURE__ */ jsx4(TabStageContext.Provider, { value: stageStore, children: /* @__PURE__ */ jsx4(ShellControllerContext.Provider, { value: controller, children }) });
7601
7509
  }
7602
7510
 
7603
7511
  // src/ui/lib/adaptive-polling.ts
@@ -7706,7 +7614,7 @@ import {
7706
7614
  useRef as useRef7,
7707
7615
  useMemo as useMemo6,
7708
7616
  useSyncExternalStore as useSyncExternalStore8,
7709
- useState as useState13
7617
+ useState as useState12
7710
7618
  } from "react";
7711
7619
 
7712
7620
  // src/ui/lib/server-status-store.ts
@@ -7800,7 +7708,7 @@ var ServerStatusStore = class {
7800
7708
  };
7801
7709
 
7802
7710
  // src/ui/lib/ServerStatusContext.tsx
7803
- import { jsx as jsx8 } from "react/jsx-runtime";
7711
+ import { jsx as jsx5 } from "react/jsx-runtime";
7804
7712
  var StatusStoreContext = createContext4(null);
7805
7713
  var ServerStatusContext = createContext4(null);
7806
7714
  var activeStore = null;
@@ -7817,7 +7725,7 @@ function ServerStatusProvider({
7817
7725
  storeRef.current = new ServerStatusStore();
7818
7726
  }
7819
7727
  const store = storeRef.current;
7820
- const [version4, setVersion] = useState13(0);
7728
+ const [version3, setVersion] = useState12(0);
7821
7729
  const mountedRef = useRef7(true);
7822
7730
  const refreshInFlightRef = useRef7(null);
7823
7731
  useEffect14(() => store.subscribeAll(() => setVersion((v) => v + 1)), [store]);
@@ -7900,9 +7808,9 @@ function ServerStatusProvider({
7900
7808
  }),
7901
7809
  // version refreshes the statuses/initialLoadComplete snapshots
7902
7810
  // eslint-disable-next-line react-hooks/exhaustive-deps
7903
- [version4, refreshStatuses, getStatus]
7811
+ [version3, refreshStatuses, getStatus]
7904
7812
  );
7905
- return /* @__PURE__ */ jsx8(StatusStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx8(ServerStatusContext.Provider, { value: contextValue, children }) });
7813
+ return /* @__PURE__ */ jsx5(StatusStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx5(ServerStatusContext.Provider, { value: contextValue, children }) });
7906
7814
  }
7907
7815
  function useStatusStore() {
7908
7816
  const store = useContext4(StatusStoreContext);
@@ -7952,7 +7860,7 @@ function useOptionalHostStatusEntry(hostId) {
7952
7860
 
7953
7861
  // src/ui/plugin-host/bridge.ts
7954
7862
  init_define_import_meta_env();
7955
- import { useCallback as useCallback8, useEffect as useEffect15, useMemo as useMemo8, useState as useState14 } from "react";
7863
+ import { useCallback as useCallback8, useEffect as useEffect15, useMemo as useMemo8, useState as useState13 } from "react";
7956
7864
 
7957
7865
  // src/ui/shell/companions.ts
7958
7866
  init_define_import_meta_env();
@@ -8272,7 +8180,7 @@ var pluginHostBridge = {
8272
8180
  },
8273
8181
  useSettings(pluginId, scope, hostId) {
8274
8182
  const numericHostId = hostId === void 0 ? void 0 : Number.parseInt(String(hostId), 10);
8275
- const [state2, setState] = useState14({ values: {}, loaded: false });
8183
+ const [state2, setState] = useState13({ values: {}, loaded: false });
8276
8184
  useEffect15(() => {
8277
8185
  let cancelled = false;
8278
8186
  if (scope === "host" && !Number.isFinite(numericHostId)) return;
@@ -8328,7 +8236,7 @@ var pluginHostBridge = {
8328
8236
  );
8329
8237
  },
8330
8238
  useCurrentUser() {
8331
- const [user, setUser] = useState14(null);
8239
+ const [user, setUser] = useState13(null);
8332
8240
  useEffect15(() => {
8333
8241
  let cancelled = false;
8334
8242
  loadCurrentUser().then((next) => {
@@ -8912,8 +8820,8 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8912
8820
  },
8913
8821
  registerOnboardingStep(step) {
8914
8822
  requireStepId(step.id, "onboarding step");
8915
- const version4 = step.version ?? 1;
8916
- if (!Number.isInteger(version4) || version4 < 1) {
8823
+ const version3 = step.version ?? 1;
8824
+ if (!Number.isInteger(version3) || version3 < 1) {
8917
8825
  throw new Error(
8918
8826
  `${pluginId}: onboarding step "${step.id}" needs a whole version of 1 or more`
8919
8827
  );
@@ -8923,7 +8831,7 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8923
8831
  onboardingSteps.register({
8924
8832
  id: pluginStepKey(pluginId, step.id),
8925
8833
  pluginId,
8926
- version: version4,
8834
+ version: version3,
8927
8835
  titleKey: key(step.titleKey),
8928
8836
  descriptionKey: step.descriptionKey ? key(step.descriptionKey) : void 0,
8929
8837
  icon: step.icon ? withIconBoundary(pluginId, step.icon) : void 0,
@@ -9061,918 +8969,6 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
9061
8969
  };
9062
8970
  }
9063
8971
 
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
8972
  // src/ui/lib/host-to-ssh-host.ts
9977
8973
  init_define_import_meta_env();
9978
8974
  function hostToSSHHost(h) {
@@ -10009,446 +9005,7 @@ function hostToSSHHost(h) {
10009
9005
  };
10010
9006
  }
10011
9007
 
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
9008
  export {
10449
- Checkbox2 as Checkbox,
10450
- PasswordInput,
10451
- Textarea,
10452
9009
  rem,
10453
9010
  remScale,
10454
9011
  useIsMobile,
@@ -10487,6 +9044,14 @@ export {
10487
9044
  isCoreRailView,
10488
9045
  railItemLabel,
10489
9046
  resetRegisteredRailItems,
9047
+ useCompanionDefs,
9048
+ useCompanionLayoutDefs,
9049
+ resolveCompanionLayout,
9050
+ companionApplies,
9051
+ arrangeCompanions,
9052
+ companionKey,
9053
+ patchCompanionState,
9054
+ useCompanionState,
10490
9055
  registerHostEditorSection,
10491
9056
  unregisterHostEditorSection,
10492
9057
  getHostEditorSection,
@@ -10540,8 +9105,10 @@ export {
10540
9105
  listExtensions,
10541
9106
  getSettingsComponent,
10542
9107
  listSettingsComponents,
9108
+ useSettingsComponents,
10543
9109
  registerAction,
10544
9110
  listActions,
9111
+ invokeAction,
10545
9112
  declareActionSlot,
10546
9113
  registerSlotContribution,
10547
9114
  getSlotContributions,
@@ -10626,25 +9193,5 @@ export {
10626
9193
  useLayouts,
10627
9194
  layoutLabel,
10628
9195
  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
9196
+ hostToSSHHost
10650
9197
  };