@termix-ssh/plugin-sdk 1.0.15 → 1.0.17

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 (37) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/PLUGIN-GUIDE.md +92 -72
  3. package/dist/backend.d.ts +370 -0
  4. package/dist/capabilities.js +2 -0
  5. package/dist/frontend.d.ts +45 -0
  6. package/dist/host/{PluginsTab-S33WIIGC.js → PluginsTab-YAGYRBIE.js} +3 -3
  7. package/dist/host/{chunk-57DJU2ZA.js → chunk-2MU6WNSX.js} +1 -1
  8. package/dist/host/{chunk-VFTB2RDP.js → chunk-446UEDSK.js} +4 -2
  9. package/dist/host/{chunk-3L45NNT5.js → chunk-7LEJ3HYF.js} +20 -3
  10. package/dist/host/{chunk-V44FCJBQ.js → chunk-J2AILBXM.js} +540 -400
  11. package/dist/host/{chunk-2IV3VX7F.js → chunk-LQKK2JJA.js} +5 -1
  12. package/dist/host/core-classes.json +1 -1
  13. package/dist/host/{linked-server-SCJH7DSL.js → linked-server-OUF3POT7.js} +5 -3
  14. package/dist/host/{rbac-api-KA5IPQQL.js → rbac-api-LZVENREG.js} +1 -1
  15. package/dist/host/testing-host.js +4 -4
  16. package/dist/host/types/src/types/onboarding.d.ts +0 -1
  17. package/dist/host/types/src/ui/auth/Auth.d.ts +1 -0
  18. package/dist/host/types/src/ui/lib/linked-server.d.ts +2 -0
  19. package/dist/host/types/src/ui/plugin-host/NativeView.d.ts +19 -0
  20. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +2 -6
  21. package/dist/host/types/src/ui/shell/CompanionFrame.d.ts +11 -0
  22. package/dist/host/types/src/ui/shell/companions.d.ts +63 -0
  23. package/dist/host/ui.js +149 -815
  24. package/dist/host/{use-permissions-LLN6TLAL.js → use-permissions-BYCDS5PD.js} +1 -1
  25. package/dist/manifest.d.ts +1 -1
  26. package/dist/manifest.js +1 -1
  27. package/dist/testing.d.ts +5 -1
  28. package/dist/testing.js +28 -0
  29. package/package.json +1 -1
  30. package/schema/manifest.schema.json +2 -0
  31. package/dist/host/types/src/ui/api/sync-api.d.ts +0 -93
  32. package/dist/host/types/src/ui/auth/ElectronLoginForm.d.ts +0 -9
  33. package/dist/host/types/src/ui/hooks/use-sync-status.d.ts +0 -4
  34. package/dist/host/types/src/ui/lib/sync-status.d.ts +0 -6
  35. package/dist/host/types/src/ui/settings/sync/LinkServerDialog.d.ts +0 -12
  36. package/dist/host/types/src/ui/settings/sync/ProxySettingsFields.d.ts +0 -15
  37. package/dist/host/types/src/ui/settings/sync/saved-servers.d.ts +0 -3
@@ -3,14 +3,16 @@ import {
3
3
  hasPermission,
4
4
  resetPermissionsCache,
5
5
  usePermissions
6
- } from "./chunk-57DJU2ZA.js";
6
+ } from "./chunk-2MU6WNSX.js";
7
7
  import {
8
+ ArrowDownToLine,
9
+ ArrowLeftToLine,
10
+ ArrowRightToLine,
8
11
  Button,
9
12
  Check,
10
13
  Checkbox,
11
14
  CheckboxIndicator,
12
15
  CircleSlash,
13
- Cloud,
14
16
  Columns2,
15
17
  DefaultsOnly,
16
18
  DocsLink,
@@ -27,6 +29,7 @@ import {
27
29
  OPEN_PLUGIN_SETTINGS_EVENT,
28
30
  PLUGINS_CHANGED_EVENT,
29
31
  PLUGIN_SETTINGS_CHANGED_EVENT,
32
+ PanelRight,
30
33
  Plug,
31
34
  PluginIcon,
32
35
  Puzzle,
@@ -80,7 +83,7 @@ import {
80
83
  withIconBoundary,
81
84
  withPluginContext,
82
85
  withPluginScope
83
- } from "./chunk-2IV3VX7F.js";
86
+ } from "./chunk-LQKK2JJA.js";
84
87
  import {
85
88
  SYNC_CHANGED_EVENT,
86
89
  TRUST_TOKEN_KEY,
@@ -118,7 +121,6 @@ import {
118
121
  logActivity,
119
122
  manifestDeclares,
120
123
  markUserAuthenticated,
121
- notifySyncChanged,
122
124
  parseCustomKeybindings,
123
125
  patchOpenTab,
124
126
  pluginStepKey,
@@ -130,7 +132,7 @@ import {
130
132
  usePluginStore,
131
133
  useTabTypes,
132
134
  websocketAuthProtocols
133
- } from "./chunk-3L45NNT5.js";
135
+ } from "./chunk-7LEJ3HYF.js";
134
136
  import {
135
137
  define_import_meta_env_default,
136
138
  init_define_import_meta_env
@@ -1122,13 +1124,6 @@ var en_default = {
1122
1124
  pluginsLoadFailed: "The plugin list could not be loaded. You can choose plugins later from the Plugins tab.",
1123
1125
  securityTitle: "Secure your account",
1124
1126
  securityIntro: "Add a passkey or an authenticator app to your account. You can also do this later in Settings, under Security.",
1125
- desktopTitle: "How do you want to use Termix?",
1126
- desktopBody: "You can change this any time from Sync in the sidebar.",
1127
- desktopLocalTitle: "On this device only",
1128
- desktopLocalBody: "Everything is stored here. Nothing to set up.",
1129
- desktopLinkTitle: "With my Termix server",
1130
- desktopLinkBody: "Sign in to your server to use the same hosts and settings on every device.",
1131
- desktopLinked: "Linked to {{server}}",
1132
1127
  pluginsDocsLink: "Read about each one"
1133
1128
  },
1134
1129
  desktopUpdates: {
@@ -1149,6 +1144,11 @@ var en_default = {
1149
1144
  ready: "Termix {{version}} is ready to install",
1150
1145
  readyHint: "It installs the next time Termix restarts."
1151
1146
  },
1147
+ companion: {
1148
+ close: "Close panel",
1149
+ moveSide: "Move panel",
1150
+ resize: "Resize panel"
1151
+ },
1152
1152
  nav: {
1153
1153
  home: "Home",
1154
1154
  terminal: "Terminal",
@@ -1159,7 +1159,6 @@ var en_default = {
1159
1159
  hosts: "Hosts",
1160
1160
  credentials: "Credentials",
1161
1161
  connections: "Connections",
1162
- sync: "Sync",
1163
1162
  reconnectDisconnectedTerminals: "Reconnect all disconnected terminals",
1164
1163
  reconnectingTerminals_one: "Reconnecting {{count}} terminal",
1165
1164
  reconnectingTerminals_other: "Reconnecting {{count}} terminals",
@@ -2058,6 +2057,14 @@ var en_default = {
2058
2057
  title: "Set sign in rules",
2059
2058
  consequence: "It can sign people out when idle, limit session length, turn off remember me and require a second factor."
2060
2059
  },
2060
+ "ssh:transport": {
2061
+ title: "Choose how hosts are reached",
2062
+ consequence: "It can carry the SSH connection to a host over its own route, so it sees everything sent to and from those hosts, passwords included."
2063
+ },
2064
+ "sync:provide": {
2065
+ title: "Run sync",
2066
+ consequence: "It can answer all sync requests, so it sees the data that devices and servers sync."
2067
+ },
2061
2068
  "system:tls": {
2062
2069
  title: "Manage certificates",
2063
2070
  consequence: "It can replace the certificate Termix serves over HTTPS and answer domain checks from certificate authorities."
@@ -2526,87 +2533,6 @@ var en_default = {
2526
2533
  noData: "No data",
2527
2534
  filter: "Filter..."
2528
2535
  },
2529
- sync: {
2530
- minutesAgo_one: "{{count}} minute ago",
2531
- minutesAgo_other: "{{count}} minutes ago",
2532
- hoursAgo_one: "{{count}} hour ago",
2533
- hoursAgo_other: "{{count}} hours ago",
2534
- pending_one: "{{count}} change waiting to sync",
2535
- pending_other: "{{count}} changes waiting to sync",
2536
- proxy: {
2537
- headersTitle: "Extra headers",
2538
- headersHint: "Sent with every request to the server, for example a Cloudflare Access service token.",
2539
- headerName: "Header name",
2540
- headerValue: "Value",
2541
- savedValue: "Saved, type to change",
2542
- addHeader: "Add header",
2543
- removeHeader: "Remove header",
2544
- basicAuthTitle: "Username and password",
2545
- basicAuthHint: "For a proxy that asks for a username and password before Termix.",
2546
- username: "Username",
2547
- password: "Password",
2548
- allowInvalidCertificate: "Trust a self-signed certificate",
2549
- allowInvalidCertificateHint: "Only for your own server, when its certificate is not from a known authority."
2550
- },
2551
- problems: {
2552
- invalidUrl: "That does not look like a web address.",
2553
- unreachable: "Could not reach the server. Check the address and that the server is running.",
2554
- unreachableWithReason: "Could not reach the server: {{reason}}",
2555
- tlsUntrusted: "The server certificate is not trusted. If this is your own server with a self-signed certificate, turn on the option below.",
2556
- basicAuthRequired: "Something in front of the server asks for a username and password. Enter them below.",
2557
- proxyLogin: "A login page or proxy answered instead of Termix. Add the headers it needs below, or check the address.",
2558
- notTermix: "There is no Termix server at that address. If it runs under a path, include it, like https://example.com/termix.",
2559
- versionMismatch: "That server runs an older Termix. Update it to 2.9 or newer to link.",
2560
- differentAccount: "You signed in to a different account. Sign in to the account this device is linked to, or unlink first."
2561
- },
2562
- wizard: {
2563
- title: "Link to a Termix server",
2564
- reloginTitle: "Sign in again",
2565
- savedServers: "Recent servers",
2566
- removeServer: "Remove from the list",
2567
- serverDescription: "Enter the address you use to open Termix in your browser.",
2568
- signinDescription: "Sign in to {{url}}",
2569
- chooseDescription: "This device already has data. What should happen to it?",
2570
- finishingDescription: "Linking this device.",
2571
- serverUrl: "Server address",
2572
- serverUrlHint: "For example https://termix.example.com",
2573
- showProxySettings: "Reverse proxy settings",
2574
- hideProxySettings: "Hide reverse proxy settings",
2575
- continue: "Continue",
2576
- back: "Back",
2577
- mergeTitle: "Add it to my account",
2578
- mergeBody_one: "The item on this device is added to your account. Items your account already has stay as they are on the server.",
2579
- mergeBody_other: "The {{count}} items on this device are added to your account. Items your account already has stay as they are on the server.",
2580
- replaceTitle: "Replace it with my account",
2581
- replaceBody_one: "The item on this device is removed, and this device takes everything from your account. Hosts kept on this device only stay.",
2582
- replaceBody_other: "The {{count}} items on this device are removed, and this device takes everything from your account. Hosts kept on this device only stay.",
2583
- link: "Link",
2584
- linking: "Linking and running the first sync...",
2585
- linked: "Linked. Your data is syncing now.",
2586
- signedInAgain: "Signed in. Sync is running again."
2587
- },
2588
- entities: {
2589
- hosts: "Hosts",
2590
- sshCredentials: "Credentials",
2591
- sshFolders: "Folders",
2592
- userPreferences: "Preferences",
2593
- pluginUserSettings: "Plugin settings",
2594
- sharedHosts: "Hosts shared with you",
2595
- sharedCredentials: "Credentials shared with you",
2596
- vaultProfiles: "Vault profiles",
2597
- snippetFolders: "Snippet folders",
2598
- snippets: "Snippets",
2599
- dashboardServiceLinks: "Service links",
2600
- homepageItems: "Homepage items",
2601
- networkTopology: "Network map",
2602
- workspaces: "Workspaces",
2603
- hostDefaults: "Host defaults",
2604
- hostDefaultsAdmin: "Server host defaults",
2605
- aiProviders: "AI providers",
2606
- homepageLayouts: "Homepage layouts",
2607
- accountProfile: "Account profile"
2608
- }
2609
- },
2610
2536
  hostDefaults: {
2611
2537
  source: {
2612
2538
  folder: "Folder {{name}}",
@@ -2667,8 +2593,8 @@ var localeLoaders = Object.fromEntries(
2667
2593
  Object.entries(LOCALE_FILES).map(([code, file]) => [
2668
2594
  code,
2669
2595
  () => {
2670
- const load = translatedFiles[`../locales/translated/${file}.json`];
2671
- return load ? load() : Promise.reject(new Error(`no ${file}`));
2596
+ const load2 = translatedFiles[`../locales/translated/${file}.json`];
2597
+ return load2 ? load2() : Promise.reject(new Error(`no ${file}`));
2672
2598
  }
2673
2599
  ])
2674
2600
  );
@@ -2771,130 +2697,6 @@ function consumeLoginLanguage() {
2771
2697
  }
2772
2698
  var i18n_default = i18n;
2773
2699
 
2774
- // src/ui/api/sync-api.ts
2775
- init_define_import_meta_env();
2776
- async function getSyncStatus() {
2777
- return (await authApi.get("/sync/link/status")).data;
2778
- }
2779
- async function probeServer(target) {
2780
- return (await authApi.post("/sync/link/probe", target)).data;
2781
- }
2782
- async function getLinkPreview() {
2783
- return (await authApi.get("/sync/link/preview")).data?.counts ?? {};
2784
- }
2785
- async function completeLink(input) {
2786
- return (await authApi.post("/sync/link/complete", input)).data;
2787
- }
2788
- async function reloginLink(token) {
2789
- return (await authApi.post("/sync/link/relogin", { token })).data;
2790
- }
2791
- async function unlinkServer(keepData) {
2792
- return (await authApi.post("/sync/link/unlink", { keepData })).data;
2793
- }
2794
- async function syncNow() {
2795
- return (await authApi.post("/sync/link/sync", {}, { timeout: 3e5 })).data;
2796
- }
2797
- async function updateSyncSettings(settings) {
2798
- return (await authApi.put("/sync/link/settings", settings)).data;
2799
- }
2800
- async function getSyncConflicts() {
2801
- return (await authApi.get("/sync/link/conflicts")).data?.conflicts ?? [];
2802
- }
2803
- async function settleSyncConflict(id, keep) {
2804
- await authApi.post(`/sync/link/conflicts/${id}`, { keep });
2805
- }
2806
- async function getSyncErrors() {
2807
- return (await authApi.get("/sync/link/errors")).data?.errors ?? [];
2808
- }
2809
- async function retrySyncErrors() {
2810
- return (await authApi.post("/sync/link/errors/retry", {}, { timeout: 3e5 })).data;
2811
- }
2812
-
2813
- // src/ui/lib/sync-status.ts
2814
- init_define_import_meta_env();
2815
- var IDLE_MS = 15e3;
2816
- var BUSY_MS = 2e3;
2817
- var snapshot = null;
2818
- var timer = null;
2819
- var lastSyncAt;
2820
- var listeners = /* @__PURE__ */ new Set();
2821
- function emit() {
2822
- for (const listener of listeners) listener();
2823
- }
2824
- function announce() {
2825
- notifySyncChanged();
2826
- window.dispatchEvent(new CustomEvent("hosts:refresh"));
2827
- window.dispatchEvent(new CustomEvent("termix:hosts-changed"));
2828
- window.dispatchEvent(new CustomEvent("termix:credentials-changed"));
2829
- window.dispatchEvent(new CustomEvent("termix:plugins-changed"));
2830
- }
2831
- async function poll() {
2832
- timer = null;
2833
- try {
2834
- const next = await getSyncStatus();
2835
- const syncedAt = next.lastSyncAt ?? null;
2836
- if (lastSyncAt !== void 0 && syncedAt !== lastSyncAt) announce();
2837
- if (snapshot && snapshot.linked !== next.linked) notifySyncChanged();
2838
- lastSyncAt = syncedAt;
2839
- snapshot = next;
2840
- emit();
2841
- } catch {
2842
- }
2843
- schedule();
2844
- }
2845
- function schedule(delay) {
2846
- if (listeners.size === 0 || timer) return;
2847
- const busy = snapshot?.status === "syncing" || !!snapshot?.pending;
2848
- timer = setTimeout(() => void poll(), delay ?? (busy ? BUSY_MS : IDLE_MS));
2849
- }
2850
- function subscribeSyncStatus(listener) {
2851
- listeners.add(listener);
2852
- if (isElectron() && listeners.size === 1 && !timer) void poll();
2853
- return () => {
2854
- listeners.delete(listener);
2855
- if (listeners.size === 0 && timer) {
2856
- clearTimeout(timer);
2857
- timer = null;
2858
- }
2859
- };
2860
- }
2861
- function getSyncStatusSnapshot() {
2862
- return snapshot;
2863
- }
2864
- function setSyncStatus(next) {
2865
- snapshot = next;
2866
- if (lastSyncAt !== void 0 && (next.lastSyncAt ?? null) !== lastSyncAt) {
2867
- announce();
2868
- }
2869
- lastSyncAt = next.lastSyncAt ?? null;
2870
- emit();
2871
- if (timer) clearTimeout(timer);
2872
- timer = null;
2873
- schedule(BUSY_MS);
2874
- }
2875
- function refreshSyncStatus() {
2876
- if (timer) clearTimeout(timer);
2877
- timer = null;
2878
- void poll();
2879
- }
2880
-
2881
- // src/ui/hooks/use-sync-status.ts
2882
- init_define_import_meta_env();
2883
- import { useSyncExternalStore } from "react";
2884
- function useSyncStatus() {
2885
- return useSyncExternalStore(
2886
- subscribeSyncStatus,
2887
- getSyncStatusSnapshot,
2888
- () => null
2889
- );
2890
- }
2891
- function useSyncAttentionCount() {
2892
- const status = useSyncStatus();
2893
- if (!status?.linked) return null;
2894
- const count = (status.conflicts ?? 0) + (status.errors ?? 0) + (status.status === "signed_out" ? 1 : 0);
2895
- return count || null;
2896
- }
2897
-
2898
2700
  // src/ui/shell/panel-registry.ts
2899
2701
  init_define_import_meta_env();
2900
2702
  var registry = createRegistry();
@@ -2906,7 +2708,7 @@ var resetPanels = registry.reset;
2906
2708
 
2907
2709
  // src/ui/sidebar/rail-items.ts
2908
2710
  init_define_import_meta_env();
2909
- import { useMemo, useSyncExternalStore as useSyncExternalStore2 } from "react";
2711
+ import { useMemo, useSyncExternalStore } from "react";
2910
2712
  var RAIL_GROUP_ORDER = ["objects", "tools", "system"];
2911
2713
  var RAIL_ITEMS = [
2912
2714
  {
@@ -2939,15 +2741,6 @@ var RAIL_ITEMS = [
2939
2741
  mobilePrimary: true,
2940
2742
  group: "tools"
2941
2743
  },
2942
- {
2943
- id: "sync",
2944
- icon: Cloud,
2945
- labelKey: "nav.sync",
2946
- electronOnly: true,
2947
- placement: "footer",
2948
- group: "system",
2949
- useBadge: useSyncAttentionCount
2950
- },
2951
2744
  {
2952
2745
  id: "plugins",
2953
2746
  icon: Puzzle,
@@ -3006,7 +2799,7 @@ function permittedRailItems(items, permissions) {
3006
2799
  );
3007
2800
  }
3008
2801
  function useRailItems() {
3009
- const items = useSyncExternalStore2(
2802
+ const items = useSyncExternalStore(
3010
2803
  registeredRailItems.subscribe,
3011
2804
  railItemsSnapshot,
3012
2805
  railItemsSnapshot
@@ -3371,12 +3164,12 @@ var resetDashboardCards = registry3.reset;
3371
3164
  init_define_import_meta_env();
3372
3165
  var points = /* @__PURE__ */ new Map();
3373
3166
  function point(id) {
3374
- let registry5 = points.get(id);
3375
- if (!registry5) {
3376
- registry5 = createRegistry();
3377
- points.set(id, registry5);
3167
+ let registry6 = points.get(id);
3168
+ if (!registry6) {
3169
+ registry6 = createRegistry();
3170
+ points.set(id, registry6);
3378
3171
  }
3379
- return registry5;
3172
+ return registry6;
3380
3173
  }
3381
3174
  function registerExtension(pointId, extension) {
3382
3175
  return point(pointId).register(extension);
@@ -3417,21 +3210,21 @@ init_define_import_meta_env();
3417
3210
  var actions3 = /* @__PURE__ */ new Map();
3418
3211
  var slots = /* @__PURE__ */ new Map();
3419
3212
  var contributions = /* @__PURE__ */ new Map();
3420
- var listeners2 = /* @__PURE__ */ new Set();
3213
+ var listeners = /* @__PURE__ */ new Set();
3421
3214
  var snapshots = /* @__PURE__ */ new Map();
3422
3215
  var EMPTY = [];
3423
- function emit2() {
3216
+ function emit() {
3424
3217
  snapshots.clear();
3425
- for (const listener of listeners2) listener();
3218
+ for (const listener of listeners) listener();
3426
3219
  }
3427
3220
  function registerAction(id, handler, options = {}) {
3428
3221
  const action = { id, handler, ...options };
3429
3222
  actions3.set(id, action);
3430
- emit2();
3223
+ emit();
3431
3224
  return () => {
3432
3225
  if (actions3.get(id) === action) {
3433
3226
  actions3.delete(id);
3434
- emit2();
3227
+ emit();
3435
3228
  }
3436
3229
  };
3437
3230
  }
@@ -3451,11 +3244,11 @@ async function invokeAction(id, ...args) {
3451
3244
  }
3452
3245
  function declareActionSlot(slot) {
3453
3246
  slots.set(slot.id, slot);
3454
- emit2();
3247
+ emit();
3455
3248
  return () => {
3456
3249
  if (slots.get(slot.id) === slot) {
3457
3250
  slots.delete(slot.id);
3458
- emit2();
3251
+ emit();
3459
3252
  }
3460
3253
  };
3461
3254
  }
@@ -3476,7 +3269,7 @@ function registerSlotContribution(slotId, contribution) {
3476
3269
  }
3477
3270
  const stored = { ...contribution, kind };
3478
3271
  forSlot.set(contribution.actionId, stored);
3479
- emit2();
3272
+ emit();
3480
3273
  return () => {
3481
3274
  if (contributions.get(slotId)?.get(contribution.actionId) === stored) {
3482
3275
  unregisterSlotContribution(slotId, contribution.actionId);
@@ -3487,7 +3280,7 @@ function unregisterSlotContribution(slotId, actionId) {
3487
3280
  const forSlot = contributions.get(slotId);
3488
3281
  if (!forSlot?.delete(actionId)) return;
3489
3282
  if (forSlot.size === 0) contributions.delete(slotId);
3490
- emit2();
3283
+ emit();
3491
3284
  }
3492
3285
  function getSlotContributions(slotId) {
3493
3286
  const cached = snapshots.get(slotId);
@@ -3504,9 +3297,9 @@ function getSlotContributions(slotId) {
3504
3297
  return sorted;
3505
3298
  }
3506
3299
  function subscribeToActionRegistry(listener) {
3507
- listeners2.add(listener);
3300
+ listeners.add(listener);
3508
3301
  return () => {
3509
- listeners2.delete(listener);
3302
+ listeners.delete(listener);
3510
3303
  };
3511
3304
  }
3512
3305
  function resetActionRegistry() {
@@ -3514,7 +3307,7 @@ function resetActionRegistry() {
3514
3307
  slots.clear();
3515
3308
  contributions.clear();
3516
3309
  snapshots.clear();
3517
- emit2();
3310
+ emit();
3518
3311
  }
3519
3312
 
3520
3313
  // src/ui/lib/plugin-transport.ts
@@ -3612,14 +3405,14 @@ async function pluginWsUrl(pluginId, path2, options = {}) {
3612
3405
  init_define_import_meta_env();
3613
3406
  import {
3614
3407
  createElement,
3615
- useSyncExternalStore as useSyncExternalStore3
3408
+ useSyncExternalStore as useSyncExternalStore2
3616
3409
  } from "react";
3617
3410
  var components = /* @__PURE__ */ new Map();
3618
- var listeners3 = /* @__PURE__ */ new Set();
3411
+ var listeners2 = /* @__PURE__ */ new Set();
3619
3412
  var version = 0;
3620
3413
  function notify() {
3621
3414
  version += 1;
3622
- for (const listener of listeners3) listener();
3415
+ for (const listener of listeners2) listener();
3623
3416
  }
3624
3417
  function registerPluginComponent(id, component) {
3625
3418
  components.set(id, component);
@@ -3632,11 +3425,11 @@ function registerPluginComponent(id, component) {
3632
3425
  };
3633
3426
  }
3634
3427
  function subscribe(listener) {
3635
- listeners3.add(listener);
3636
- return () => listeners3.delete(listener);
3428
+ listeners2.add(listener);
3429
+ return () => listeners2.delete(listener);
3637
3430
  }
3638
3431
  function usePluginComponent(id) {
3639
- useSyncExternalStore3(subscribe, () => version);
3432
+ useSyncExternalStore2(subscribe, () => version);
3640
3433
  return components.get(id);
3641
3434
  }
3642
3435
  function PluginComponent({
@@ -3730,7 +3523,7 @@ import { createContext, useContext, useEffect as useEffect3, useState as useStat
3730
3523
 
3731
3524
  // src/ui/look/theme-registry.ts
3732
3525
  init_define_import_meta_env();
3733
- import { useSyncExternalStore as useSyncExternalStore4 } from "react";
3526
+ import { useSyncExternalStore as useSyncExternalStore3 } from "react";
3734
3527
  var SYSTEM_THEME = "system";
3735
3528
  var BUILTIN_THEMES = [
3736
3529
  {
@@ -3749,23 +3542,23 @@ var BUILTIN_THEMES = [
3749
3542
  var builtins = new Map(BUILTIN_THEMES.map((theme) => [theme.id, theme]));
3750
3543
  var pluginThemes = /* @__PURE__ */ new Map();
3751
3544
  var pluginThemesKnown = false;
3752
- var listeners4 = /* @__PURE__ */ new Set();
3753
- var snapshot2 = null;
3754
- function emit3() {
3755
- snapshot2 = null;
3756
- for (const listener of listeners4) listener();
3545
+ var listeners3 = /* @__PURE__ */ new Set();
3546
+ var snapshot = null;
3547
+ function emit2() {
3548
+ snapshot = null;
3549
+ for (const listener of listeners3) listener();
3757
3550
  }
3758
3551
  function listThemes() {
3759
- if (!snapshot2) {
3552
+ if (!snapshot) {
3760
3553
  const replaced = BUILTIN_THEMES.map(
3761
3554
  (theme) => pluginThemes.get(theme.id) ?? theme
3762
3555
  );
3763
3556
  const extra = [...pluginThemes.values()].filter(
3764
3557
  (theme) => !builtins.has(theme.id)
3765
3558
  );
3766
- snapshot2 = [...replaced, ...extra];
3559
+ snapshot = [...replaced, ...extra];
3767
3560
  }
3768
- return snapshot2;
3561
+ return snapshot;
3769
3562
  }
3770
3563
  function getTheme(id) {
3771
3564
  return pluginThemes.get(id) ?? builtins.get(id);
@@ -3780,16 +3573,16 @@ function setPluginThemes(themes) {
3780
3573
  });
3781
3574
  if (same && !first) return;
3782
3575
  pluginThemes = next;
3783
- emit3();
3576
+ emit2();
3784
3577
  }
3785
3578
  function subscribeThemes(listener) {
3786
- listeners4.add(listener);
3579
+ listeners3.add(listener);
3787
3580
  return () => {
3788
- listeners4.delete(listener);
3581
+ listeners3.delete(listener);
3789
3582
  };
3790
3583
  }
3791
3584
  function useThemes() {
3792
- return useSyncExternalStore4(subscribeThemes, listThemes, listThemes);
3585
+ return useSyncExternalStore3(subscribeThemes, listThemes, listThemes);
3793
3586
  }
3794
3587
  function themeMode(id) {
3795
3588
  if (id === SYSTEM_THEME) {
@@ -3973,9 +3766,9 @@ function useSshAuthProviders() {
3973
3766
 
3974
3767
  // src/ui/hooks/use-action-slot.ts
3975
3768
  init_define_import_meta_env();
3976
- import { useMemo as useMemo3, useSyncExternalStore as useSyncExternalStore5 } from "react";
3769
+ import { useMemo as useMemo3, useSyncExternalStore as useSyncExternalStore4 } from "react";
3977
3770
  function useActionSlot(slotId, context) {
3978
- const contributions2 = useSyncExternalStore5(
3771
+ const contributions2 = useSyncExternalStore4(
3979
3772
  subscribeToActionRegistry,
3980
3773
  () => getSlotContributions(slotId),
3981
3774
  () => getSlotContributions(slotId)
@@ -4964,28 +4757,28 @@ function restoreSplitTabs(persisted, tabs, openedAt = Date.now()) {
4964
4757
 
4965
4758
  // src/ui/shell/split/split-drag.ts
4966
4759
  init_define_import_meta_env();
4967
- import { useSyncExternalStore as useSyncExternalStore6 } from "react";
4760
+ import { useSyncExternalStore as useSyncExternalStore5 } from "react";
4968
4761
  var state = null;
4969
4762
  var hitTest = null;
4970
4763
  var dropHandler = null;
4971
- var listeners5 = /* @__PURE__ */ new Set();
4972
- function emit4() {
4973
- for (const listener of listeners5) listener();
4764
+ var listeners4 = /* @__PURE__ */ new Set();
4765
+ function emit3() {
4766
+ for (const listener of listeners4) listener();
4974
4767
  }
4975
4768
  function beginSplitDrag(source, x, y) {
4976
4769
  state = { source, x, y, hover: hitTest?.(x, y, source) ?? null };
4977
- emit4();
4770
+ emit3();
4978
4771
  }
4979
4772
  function moveSplitDrag(x, y) {
4980
4773
  if (!state) return;
4981
4774
  state = { ...state, x, y, hover: hitTest?.(x, y, state.source) ?? null };
4982
- emit4();
4775
+ emit3();
4983
4776
  }
4984
4777
  function endSplitDrag(commit) {
4985
4778
  const ended = state;
4986
4779
  if (!ended) return;
4987
4780
  state = null;
4988
- emit4();
4781
+ emit3();
4989
4782
  if (commit && ended.hover) dropHandler?.(ended.source, ended.hover);
4990
4783
  }
4991
4784
  function isSplitDragging() {
@@ -5004,11 +4797,11 @@ function setSplitDropHandler(next) {
5004
4797
  };
5005
4798
  }
5006
4799
  function subscribe2(listener) {
5007
- listeners5.add(listener);
5008
- return () => listeners5.delete(listener);
4800
+ listeners4.add(listener);
4801
+ return () => listeners4.delete(listener);
5009
4802
  }
5010
4803
  function useSplitDrag() {
5011
- return useSyncExternalStore6(
4804
+ return useSyncExternalStore5(
5012
4805
  subscribe2,
5013
4806
  () => state,
5014
4807
  () => null
@@ -5047,18 +4840,18 @@ function dropTargetRect(rect, target) {
5047
4840
 
5048
4841
  // src/ui/shell/split/split-targets.ts
5049
4842
  init_define_import_meta_env();
5050
- import { useSyncExternalStore as useSyncExternalStore7 } from "react";
4843
+ import { useSyncExternalStore as useSyncExternalStore6 } from "react";
5051
4844
  var EMPTY2 = { splits: [], canStartSplit: false };
5052
- var snapshot3 = EMPTY2;
4845
+ var snapshot2 = EMPTY2;
5053
4846
  var snapshotKey = "";
5054
4847
  var opener = null;
5055
- var listeners6 = /* @__PURE__ */ new Set();
4848
+ var listeners5 = /* @__PURE__ */ new Set();
5056
4849
  function publishSplitTargets(next) {
5057
4850
  const key = JSON.stringify(next);
5058
4851
  if (key === snapshotKey) return;
5059
4852
  snapshotKey = key;
5060
- snapshot3 = next;
5061
- for (const listener of listeners6) listener();
4853
+ snapshot2 = next;
4854
+ for (const listener of listeners5) listener();
5062
4855
  }
5063
4856
  function setSplitOpener(next) {
5064
4857
  opener = next;
@@ -5074,13 +4867,13 @@ function openInSplit(target, open) {
5074
4867
  else open();
5075
4868
  }
5076
4869
  function subscribe3(listener) {
5077
- listeners6.add(listener);
5078
- return () => listeners6.delete(listener);
4870
+ listeners5.add(listener);
4871
+ return () => listeners5.delete(listener);
5079
4872
  }
5080
4873
  function useSplitTargets() {
5081
- return useSyncExternalStore7(
4874
+ return useSyncExternalStore6(
5082
4875
  subscribe3,
5083
- () => snapshot3,
4876
+ () => snapshot2,
5084
4877
  () => EMPTY2
5085
4878
  );
5086
4879
  }
@@ -5122,25 +4915,25 @@ function buildLayoutTabSnapshots(tabs, genSlotId = createId) {
5122
4915
  }));
5123
4916
  return { snapshots: snapshots2, slotIdByTabId };
5124
4917
  }
5125
- function snapshotData(snapshot4) {
5126
- if (snapshot4.data) return snapshot4.data;
4918
+ function snapshotData(snapshot3) {
4919
+ if (snapshot3.data) return snapshot3.data;
5127
4920
  const legacy = {};
5128
- if (snapshot4.fleetId !== void 0) legacy.fleetId = snapshot4.fleetId;
5129
- if (snapshot4.initialFilePath)
5130
- legacy.initialFilePath = snapshot4.initialFilePath;
5131
- if (snapshot4.initialPath) legacy.initialPath = snapshot4.initialPath;
4921
+ if (snapshot3.fleetId !== void 0) legacy.fleetId = snapshot3.fleetId;
4922
+ if (snapshot3.initialFilePath)
4923
+ legacy.initialFilePath = snapshot3.initialFilePath;
4924
+ if (snapshot3.initialPath) legacy.initialPath = snapshot3.initialPath;
5132
4925
  return Object.keys(legacy).length > 0 ? legacy : void 0;
5133
4926
  }
5134
- function resolveLayoutTabTarget(snapshot4, allHosts) {
4927
+ function resolveLayoutTabTarget(snapshot3, allHosts) {
5135
4928
  let host;
5136
- if (snapshot4.hostSyncId) {
5137
- host = allHosts.find((h) => h.syncId === snapshot4.hostSyncId);
4929
+ if (snapshot3.hostSyncId) {
4930
+ host = allHosts.find((h) => h.syncId === snapshot3.hostSyncId);
5138
4931
  if (!host) return { kind: "skip" };
5139
4932
  }
5140
- if (opensAsSingleton(snapshot4.type, !!host)) {
4933
+ if (opensAsSingleton(snapshot3.type, !!host)) {
5141
4934
  return { kind: "singleton", host };
5142
4935
  }
5143
- if (!host && isUnregisteredPluginTabType(snapshot4.type)) {
4936
+ if (!host && isUnregisteredPluginTabType(snapshot3.type)) {
5144
4937
  return { kind: "singleton" };
5145
4938
  }
5146
4939
  return host ? { kind: "host", host } : { kind: "skip" };
@@ -5420,11 +5213,11 @@ function createTabStageStore() {
5420
5213
  const paneEls = /* @__PURE__ */ new Map();
5421
5214
  const listeners9 = /* @__PURE__ */ new Set();
5422
5215
  let normalView = null;
5423
- let version3 = 0;
5216
+ let version4 = 0;
5424
5217
  let order2 = [];
5425
5218
  let lastAnimatedTabId = null;
5426
5219
  const bump = () => {
5427
- version3 += 1;
5220
+ version4 += 1;
5428
5221
  for (const listener of listeners9) listener();
5429
5222
  };
5430
5223
  function getNode(tabId, ownBackground) {
@@ -5532,7 +5325,7 @@ function createTabStageStore() {
5532
5325
  listeners9.delete(listener);
5533
5326
  };
5534
5327
  },
5535
- version: () => version3
5328
+ version: () => version4
5536
5329
  };
5537
5330
  }
5538
5331
 
@@ -6908,7 +6701,7 @@ function useSplit({
6908
6701
  useEffect9(() => {
6909
6702
  if (!focusedPaneId || !focusedPaneTabId) return;
6910
6703
  let attempts = 0;
6911
- let timer2;
6704
+ let timer;
6912
6705
  const attempt = () => {
6913
6706
  const focused = document.activeElement;
6914
6707
  const paneEl = paneEls.get(focusedPaneId);
@@ -6924,12 +6717,12 @@ function useSplit({
6924
6717
  handle.focus();
6925
6718
  return;
6926
6719
  }
6927
- if (attempts++ < 10) timer2 = setTimeout(attempt, 100);
6720
+ if (attempts++ < 10) timer = setTimeout(attempt, 100);
6928
6721
  };
6929
6722
  const frame = requestAnimationFrame(attempt);
6930
6723
  return () => {
6931
6724
  cancelAnimationFrame(frame);
6932
- if (timer2) clearTimeout(timer2);
6725
+ if (timer) clearTimeout(timer);
6933
6726
  };
6934
6727
  }, [focusedPaneId, focusedPaneTabId]);
6935
6728
  useEffect9(() => {
@@ -7047,34 +6840,34 @@ function useWorkspaceLayout(tabsState, regions) {
7047
6840
  try {
7048
6841
  const slotIdToNewTabId = /* @__PURE__ */ new Map();
7049
6842
  const skippedTabs = [];
7050
- for (const snapshot4 of payload.tabs) {
7051
- const target = resolveLayoutTabTarget(snapshot4, hostsRef.current);
6843
+ for (const snapshot3 of payload.tabs) {
6844
+ const target = resolveLayoutTabTarget(snapshot3, hostsRef.current);
7052
6845
  if (target.kind === "skip") {
7053
- skippedTabs.push(snapshot4.hostNameSnapshot || snapshot4.label);
6846
+ skippedTabs.push(snapshot3.hostNameSnapshot || snapshot3.label);
7054
6847
  continue;
7055
6848
  }
7056
6849
  if (target.kind === "singleton") {
7057
6850
  const newTabId = tabsState.openSingletonTab(
7058
- snapshot4.type,
6851
+ snapshot3.type,
7059
6852
  void 0,
7060
6853
  target.host,
7061
- snapshotData(snapshot4)
6854
+ snapshotData(snapshot3)
7062
6855
  );
7063
- slotIdToNewTabId.set(snapshot4.slotId, newTabId ?? snapshot4.type);
6856
+ slotIdToNewTabId.set(snapshot3.slotId, newTabId ?? snapshot3.type);
7064
6857
  continue;
7065
6858
  }
7066
6859
  if (target.kind === "host") {
7067
6860
  const newTabId = tabsState.openTab(
7068
6861
  target.host,
7069
- snapshot4.type,
6862
+ snapshot3.type,
7070
6863
  {
7071
6864
  instanceId: createId(),
7072
6865
  restoredSessionId: null,
7073
- savedLabel: snapshot4.customLabel ?? snapshot4.label
6866
+ savedLabel: snapshot3.customLabel ?? snapshot3.label
7074
6867
  },
7075
- { data: snapshotData(snapshot4) }
6868
+ { data: snapshotData(snapshot3) }
7076
6869
  );
7077
- slotIdToNewTabId.set(snapshot4.slotId, newTabId);
6870
+ slotIdToNewTabId.set(snapshot3.slotId, newTabId);
7078
6871
  }
7079
6872
  }
7080
6873
  const splitIdBySlotId = /* @__PURE__ */ new Map();
@@ -7161,7 +6954,7 @@ function useShellKeybindings({
7161
6954
  toggleFullscreenRef.current = toggleFullscreen;
7162
6955
  useEffect11(() => {
7163
6956
  let cancelled = false;
7164
- const load = () => {
6957
+ const load2 = () => {
7165
6958
  getUserPreferences().then((prefs) => {
7166
6959
  if (cancelled) return;
7167
6960
  globalKeybindingsRef.current = parseCustomKeybindings(
@@ -7170,11 +6963,11 @@ function useShellKeybindings({
7170
6963
  }).catch(() => {
7171
6964
  });
7172
6965
  };
7173
- load();
7174
- window.addEventListener("customKeybindingsChanged", load);
6966
+ load2();
6967
+ window.addEventListener("customKeybindingsChanged", load2);
7175
6968
  return () => {
7176
6969
  cancelled = true;
7177
- window.removeEventListener("customKeybindingsChanged", load);
6970
+ window.removeEventListener("customKeybindingsChanged", load2);
7178
6971
  };
7179
6972
  }, []);
7180
6973
  useEffect11(() => {
@@ -7539,19 +7332,19 @@ function useShellLifecycle({
7539
7332
 
7540
7333
  // src/ui/shell/controller/shell-store.ts
7541
7334
  init_define_import_meta_env();
7542
- import { useSyncExternalStore as useSyncExternalStore8 } from "react";
7335
+ import { useSyncExternalStore as useSyncExternalStore7 } from "react";
7543
7336
  var current = null;
7544
- var listeners7 = /* @__PURE__ */ new Set();
7337
+ var listeners6 = /* @__PURE__ */ new Set();
7545
7338
  function publishShellController(next) {
7546
7339
  if (next === current) return;
7547
7340
  current = next;
7548
- for (const listener of listeners7) listener();
7341
+ for (const listener of listeners6) listener();
7549
7342
  }
7550
7343
  function useShellSnapshot() {
7551
- return useSyncExternalStore8(
7344
+ return useSyncExternalStore7(
7552
7345
  (listener) => {
7553
- listeners7.add(listener);
7554
- return () => listeners7.delete(listener);
7346
+ listeners6.add(listener);
7347
+ return () => listeners6.delete(listener);
7555
7348
  },
7556
7349
  () => current,
7557
7350
  () => current
@@ -7622,7 +7415,6 @@ function ShellControllerProvider({
7622
7415
  });
7623
7416
  openSingletonRef.current = tabsState.openSingletonTab;
7624
7417
  const { tabs, activeTabId } = tabsState;
7625
- useSyncStatus();
7626
7418
  const palette = {
7627
7419
  isOpen: paletteOpen,
7628
7420
  open: () => setPaletteOpen(true),
@@ -7821,25 +7613,25 @@ function computeAdaptivePollDelay(policy, state2, random = Math.random) {
7821
7613
  const jitter = base * jitterRatio * (random() * 2 - 1);
7822
7614
  return Math.max(min, Math.min(max, Math.round(base + jitter)));
7823
7615
  }
7824
- function runAdaptivePolling(poll2, policy, options = {}) {
7616
+ function runAdaptivePolling(poll, policy, options = {}) {
7825
7617
  const enabled = options.enabled ?? (() => true);
7826
7618
  const visible = options.visible ?? (() => typeof document === "undefined" || document.visibilityState !== "hidden");
7827
7619
  const state2 = {
7828
7620
  stablePolls: 0,
7829
7621
  consecutiveFailures: 0
7830
7622
  };
7831
- let timer2 = null;
7623
+ let timer = null;
7832
7624
  let stopped = false;
7833
7625
  let inFlight = false;
7834
7626
  const clearTimer = () => {
7835
- if (!timer2) return;
7836
- clearTimeout(timer2);
7837
- timer2 = null;
7627
+ if (!timer) return;
7628
+ clearTimeout(timer);
7629
+ timer = null;
7838
7630
  };
7839
- const schedule2 = () => {
7631
+ const schedule = () => {
7840
7632
  clearTimer();
7841
7633
  if (stopped || !enabled() || !visible()) return;
7842
- timer2 = setTimeout(
7634
+ timer = setTimeout(
7843
7635
  () => void tick(),
7844
7636
  Math.min(
7845
7637
  policy.maxIntervalMs,
@@ -7854,7 +7646,7 @@ function runAdaptivePolling(poll2, policy, options = {}) {
7854
7646
  inFlight = true;
7855
7647
  const startedAt = Date.now();
7856
7648
  try {
7857
- const changed = await poll2();
7649
+ const changed = await poll();
7858
7650
  state2.consecutiveFailures = 0;
7859
7651
  state2.stablePolls = changed === false ? state2.stablePolls + 1 : 0;
7860
7652
  } catch (error) {
@@ -7864,7 +7656,7 @@ function runAdaptivePolling(poll2, policy, options = {}) {
7864
7656
  } finally {
7865
7657
  state2.lastPollDurationMs = Date.now() - startedAt;
7866
7658
  inFlight = false;
7867
- schedule2();
7659
+ schedule();
7868
7660
  }
7869
7661
  };
7870
7662
  const onVisibility = () => {
@@ -7880,7 +7672,7 @@ function runAdaptivePolling(poll2, policy, options = {}) {
7880
7672
  document.addEventListener("visibilitychange", onVisibility);
7881
7673
  }
7882
7674
  if (options.runImmediately ?? true) void tick();
7883
- else schedule2();
7675
+ else schedule();
7884
7676
  return () => {
7885
7677
  stopped = true;
7886
7678
  clearTimer();
@@ -7906,7 +7698,7 @@ import {
7906
7698
  useCallback as useCallback7,
7907
7699
  useRef as useRef7,
7908
7700
  useMemo as useMemo6,
7909
- useSyncExternalStore as useSyncExternalStore9,
7701
+ useSyncExternalStore as useSyncExternalStore8,
7910
7702
  useState as useState13
7911
7703
  } from "react";
7912
7704
 
@@ -8018,7 +7810,7 @@ function ServerStatusProvider({
8018
7810
  storeRef.current = new ServerStatusStore();
8019
7811
  }
8020
7812
  const store = storeRef.current;
8021
- const [version3, setVersion] = useState13(0);
7813
+ const [version4, setVersion] = useState13(0);
8022
7814
  const mountedRef = useRef7(true);
8023
7815
  const refreshInFlightRef = useRef7(null);
8024
7816
  useEffect14(() => store.subscribeAll(() => setVersion((v) => v + 1)), [store]);
@@ -8101,7 +7893,7 @@ function ServerStatusProvider({
8101
7893
  }),
8102
7894
  // version refreshes the statuses/initialLoadComplete snapshots
8103
7895
  // eslint-disable-next-line react-hooks/exhaustive-deps
8104
- [version3, refreshStatuses, getStatus]
7896
+ [version4, refreshStatuses, getStatus]
8105
7897
  );
8106
7898
  return /* @__PURE__ */ jsx8(StatusStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx8(ServerStatusContext.Provider, { value: contextValue, children }) });
8107
7899
  }
@@ -8123,7 +7915,7 @@ function useServerStatus() {
8123
7915
  }
8124
7916
  function useHostStatus(hostId, statusCheckEnabled = true) {
8125
7917
  const store = useStatusStore();
8126
- const status = useSyncExternalStore9(
7918
+ const status = useSyncExternalStore8(
8127
7919
  (onChange) => store.subscribeHost(hostId, onChange),
8128
7920
  () => store.getStatus(hostId),
8129
7921
  () => store.getStatus(hostId)
@@ -8132,7 +7924,7 @@ function useHostStatus(hostId, statusCheckEnabled = true) {
8132
7924
  }
8133
7925
  function useServerStatusMeta() {
8134
7926
  const store = useStatusStore();
8135
- useSyncExternalStore9(
7927
+ useSyncExternalStore8(
8136
7928
  (onChange) => store.subscribeMeta(onChange),
8137
7929
  () => store.getMetaSnapshot(),
8138
7930
  () => store.getMetaSnapshot()
@@ -8142,7 +7934,7 @@ function useServerStatusMeta() {
8142
7934
  var noStatus = () => "unknown";
8143
7935
  function useOptionalHostStatusEntry(hostId) {
8144
7936
  const store = useContext4(StatusStoreContext);
8145
- const status = useSyncExternalStore9(
7937
+ const status = useSyncExternalStore8(
8146
7938
  (onChange) => store && hostId !== void 0 ? store.subscribeHost(hostId, onChange) : () => {
8147
7939
  },
8148
7940
  () => store && hostId !== void 0 ? store.getStatus(hostId) : "unknown",
@@ -8394,7 +8186,7 @@ var pluginHostBridge = {
8394
8186
  window.removeEventListener(PLUGIN_SETTINGS_CHANGED_EVENT, onChanged);
8395
8187
  };
8396
8188
  }, [pluginId, scope, numericHostId]);
8397
- const save = useCallback8(
8189
+ const save2 = useCallback8(
8398
8190
  async (values) => {
8399
8191
  const saved = await SETTINGS_WRITERS[scope](
8400
8192
  pluginId,
@@ -8405,7 +8197,7 @@ var pluginHostBridge = {
8405
8197
  },
8406
8198
  [pluginId, scope, numericHostId]
8407
8199
  );
8408
- return useMemo8(() => ({ ...state2, save }), [state2, save]);
8200
+ return useMemo8(() => ({ ...state2, save: save2 }), [state2, save2]);
8409
8201
  },
8410
8202
  useHost(hostId) {
8411
8203
  const pluginId = usePluginScope();
@@ -8578,7 +8370,7 @@ function onRemoteServerChange(listener) {
8578
8370
 
8579
8371
  // src/ui/look/layout-registry.ts
8580
8372
  init_define_import_meta_env();
8581
- import { useSyncExternalStore as useSyncExternalStore10 } from "react";
8373
+ import { useSyncExternalStore as useSyncExternalStore9 } from "react";
8582
8374
  var descriptorSnapshot = [];
8583
8375
  var descriptorListeners = /* @__PURE__ */ new Set();
8584
8376
  var registered = createRegistry();
@@ -8604,7 +8396,7 @@ function subscribeDescriptors(listener) {
8604
8396
  };
8605
8397
  }
8606
8398
  function useLayouts() {
8607
- return useSyncExternalStore10(subscribeDescriptors, listLayouts, listLayouts);
8399
+ return useSyncExternalStore9(subscribeDescriptors, listLayouts, listLayouts);
8608
8400
  }
8609
8401
  function layoutLabel(layout, t) {
8610
8402
  if (!layout.pluginId) return layout.label;
@@ -8615,6 +8407,86 @@ function layoutLabel(layout, t) {
8615
8407
 
8616
8408
  // src/ui/plugin-host/app.ts
8617
8409
  init_define_import_meta_env();
8410
+ import { useEffect as useEffect16 } from "react";
8411
+
8412
+ // src/ui/shell/companions.ts
8413
+ init_define_import_meta_env();
8414
+ import { useSyncExternalStore as useSyncExternalStore10 } from "react";
8415
+ var registry5 = createRegistry(byOrderThenId);
8416
+ var registerCompanionDef = registry5.register;
8417
+ var getCompanionDef = registry5.get;
8418
+ var listCompanionDefs = registry5.list;
8419
+ var useCompanionDefs = registry5.useList;
8420
+ var resetCompanions = registry5.reset;
8421
+ function companionApplies(def, tab) {
8422
+ if (!tab.host) return false;
8423
+ if (def.tabTypes?.length && !def.tabTypes.includes(tab.type)) return false;
8424
+ return def.when ? def.when(tab, tab.host) : true;
8425
+ }
8426
+ var STORAGE_KEY = "termix:companions";
8427
+ var EMPTY3 = { openId: null, seen: false };
8428
+ var states = /* @__PURE__ */ new Map();
8429
+ var listeners7 = /* @__PURE__ */ new Set();
8430
+ var version2 = 0;
8431
+ function load() {
8432
+ try {
8433
+ const raw = localStorage.getItem(STORAGE_KEY);
8434
+ if (!raw) return;
8435
+ const parsed = JSON.parse(raw);
8436
+ states = new Map(Object.entries(parsed));
8437
+ } catch {
8438
+ }
8439
+ }
8440
+ function save() {
8441
+ try {
8442
+ const entries = [...states.entries()].slice(-200);
8443
+ localStorage.setItem(
8444
+ STORAGE_KEY,
8445
+ JSON.stringify(Object.fromEntries(entries))
8446
+ );
8447
+ } catch {
8448
+ }
8449
+ }
8450
+ load();
8451
+ function companionKey(tab) {
8452
+ return tab.instanceId ?? tab.id;
8453
+ }
8454
+ function getCompanionState(key) {
8455
+ return states.get(key) ?? EMPTY3;
8456
+ }
8457
+ function patchCompanionState(key, patch) {
8458
+ const next = { ...getCompanionState(key), ...patch };
8459
+ states = new Map(states);
8460
+ states.delete(key);
8461
+ states.set(key, next);
8462
+ version2 += 1;
8463
+ save();
8464
+ for (const listener of listeners7) listener();
8465
+ }
8466
+ function subscribe4(listener) {
8467
+ listeners7.add(listener);
8468
+ return () => listeners7.delete(listener);
8469
+ }
8470
+ function useCompanionState(key) {
8471
+ useSyncExternalStore10(
8472
+ subscribe4,
8473
+ () => version2,
8474
+ () => version2
8475
+ );
8476
+ return getCompanionState(key);
8477
+ }
8478
+ function toggleCompanion(tab, id) {
8479
+ const key = companionKey(tab);
8480
+ const current2 = getCompanionState(key);
8481
+ const target = id ?? current2.openId ?? listCompanionDefs().find((def) => companionApplies(def, tab))?.id ?? null;
8482
+ if (!target) return;
8483
+ patchCompanionState(key, {
8484
+ seen: true,
8485
+ openId: current2.openId === target ? null : target
8486
+ });
8487
+ }
8488
+
8489
+ // src/ui/plugin-host/app.ts
8618
8490
  var ONBOARDING_ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
8619
8491
  function createDocsApi(manifest) {
8620
8492
  const url = pluginDocsUrl(manifest?.docs);
@@ -8709,6 +8581,40 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8709
8581
  })
8710
8582
  );
8711
8583
  },
8584
+ registerCompanion(id, component, options2 = {}) {
8585
+ const useDefaults = options2.useDefaults;
8586
+ const Defaults = useDefaults ? scoped(function CompanionDefaultsReader({
8587
+ onValue
8588
+ }) {
8589
+ const value = useDefaults();
8590
+ const { open, side, size, edgeButton } = value;
8591
+ useEffect16(() => {
8592
+ onValue({ open, side, size, edgeButton });
8593
+ }, [onValue, open, side, size, edgeButton]);
8594
+ return null;
8595
+ }) : void 0;
8596
+ return track(
8597
+ registerCompanionDef({
8598
+ id: `${pluginId}.${id}`,
8599
+ pluginId,
8600
+ component: scoped(
8601
+ component
8602
+ ),
8603
+ icon: withIconBoundary(pluginId, options2.icon),
8604
+ titleKey: options2.titleKey ? key(options2.titleKey) : void 0,
8605
+ order: options2.order,
8606
+ tabTypes: options2.tabTypes,
8607
+ when: options2.when,
8608
+ Defaults
8609
+ })
8610
+ );
8611
+ },
8612
+ toggleCompanion(tab, id) {
8613
+ toggleCompanion(
8614
+ tab,
8615
+ id ? `${pluginId}.${id}` : void 0
8616
+ );
8617
+ },
8712
8618
  registerTab(type, component, options2 = {}) {
8713
8619
  requireDeclared("tab", type, "tab type");
8714
8620
  return track(
@@ -8960,8 +8866,8 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8960
8866
  },
8961
8867
  registerOnboardingStep(step) {
8962
8868
  requireStepId(step.id, "onboarding step");
8963
- const version3 = step.version ?? 1;
8964
- if (!Number.isInteger(version3) || version3 < 1) {
8869
+ const version4 = step.version ?? 1;
8870
+ if (!Number.isInteger(version4) || version4 < 1) {
8965
8871
  throw new Error(
8966
8872
  `${pluginId}: onboarding step "${step.id}" needs a whole version of 1 or more`
8967
8873
  );
@@ -8971,7 +8877,7 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
8971
8877
  onboardingSteps.register({
8972
8878
  id: pluginStepKey(pluginId, step.id),
8973
8879
  pluginId,
8974
- version: version3,
8880
+ version: version4,
8975
8881
  titleKey: key(step.titleKey),
8976
8882
  descriptionKey: step.descriptionKey ? key(step.descriptionKey) : void 0,
8977
8883
  icon: step.icon ? withIconBoundary(pluginId, step.icon) : void 0,
@@ -9111,24 +9017,24 @@ function createPluginApp(pluginId, manifest, contributes, options = {}) {
9111
9017
 
9112
9018
  // src/ui/hooks/use-installed-plugins.ts
9113
9019
  init_define_import_meta_env();
9114
- import { useEffect as useEffect16, useState as useState15 } from "react";
9020
+ import { useEffect as useEffect17, useState as useState15 } from "react";
9115
9021
  function useInstalledPlugins() {
9116
9022
  const [plugins, setPlugins] = useState15([]);
9117
- useEffect16(() => {
9023
+ useEffect17(() => {
9118
9024
  let cancelled = false;
9119
9025
  let request = 0;
9120
- const load = () => {
9026
+ const load2 = () => {
9121
9027
  const current2 = ++request;
9122
9028
  void getPlugins().then((loaded) => {
9123
9029
  if (!cancelled && current2 === request) setPlugins(loaded);
9124
9030
  }).catch(() => {
9125
9031
  });
9126
9032
  };
9127
- load();
9128
- window.addEventListener(PLUGINS_CHANGED_EVENT, load);
9033
+ load2();
9034
+ window.addEventListener(PLUGINS_CHANGED_EVENT, load2);
9129
9035
  return () => {
9130
9036
  cancelled = true;
9131
- window.removeEventListener(PLUGINS_CHANGED_EVENT, load);
9037
+ window.removeEventListener(PLUGINS_CHANGED_EVENT, load2);
9132
9038
  };
9133
9039
  }, []);
9134
9040
  return plugins;
@@ -9549,10 +9455,10 @@ function ComponentSlot({
9549
9455
  // src/ui/shell/tab-handles.ts
9550
9456
  init_define_import_meta_env();
9551
9457
  import { useSyncExternalStore as useSyncExternalStore11 } from "react";
9552
- var version2 = 0;
9458
+ var version3 = 0;
9553
9459
  var listeners8 = /* @__PURE__ */ new Set();
9554
9460
  var callbacks = /* @__PURE__ */ new WeakMap();
9555
- function subscribe4(listener) {
9461
+ function subscribe5(listener) {
9556
9462
  listeners8.add(listener);
9557
9463
  return () => listeners8.delete(listener);
9558
9464
  }
@@ -9563,7 +9469,7 @@ function trackTabHandle(ref) {
9563
9469
  callback = (value) => {
9564
9470
  if (ref.current === value) return;
9565
9471
  ref.current = value;
9566
- version2 += 1;
9472
+ version3 += 1;
9567
9473
  for (const listener of listeners8) listener();
9568
9474
  };
9569
9475
  callbacks.set(ref, callback);
@@ -9572,9 +9478,9 @@ function trackTabHandle(ref) {
9572
9478
  }
9573
9479
  function useTabHandlesVersion() {
9574
9480
  return useSyncExternalStore11(
9575
- subscribe4,
9576
- () => version2,
9577
- () => version2
9481
+ subscribe5,
9482
+ () => version3,
9483
+ () => version3
9578
9484
  );
9579
9485
  }
9580
9486
 
@@ -10018,8 +9924,8 @@ function runAdaptiveBackgroundTask(kind, action, task, options = {}) {
10018
9924
 
10019
9925
  // src/ui/shell/tabUtils.tsx
10020
9926
  init_define_import_meta_env();
10021
- import { lazy, memo, Suspense } from "react";
10022
- import { useTranslation as useTranslation8 } from "react-i18next";
9927
+ import { lazy, memo as memo2, Suspense } from "react";
9928
+ import { useTranslation as useTranslation9 } from "react-i18next";
10023
9929
 
10024
9930
  // src/ui/lib/host-to-ssh-host.ts
10025
9931
  init_define_import_meta_env();
@@ -10057,10 +9963,258 @@ function hostToSSHHost(h) {
10057
9963
  };
10058
9964
  }
10059
9965
 
9966
+ // src/ui/shell/CompanionFrame.tsx
9967
+ init_define_import_meta_env();
9968
+ import {
9969
+ memo,
9970
+ useCallback as useCallback9,
9971
+ useEffect as useEffect18,
9972
+ useMemo as useMemo10,
9973
+ useRef as useRef8,
9974
+ useState as useState16
9975
+ } from "react";
9976
+ import { useTranslation as useTranslation8 } from "react-i18next";
9977
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
9978
+ var DEFAULT_SIDE_SIZE = 360;
9979
+ var DEFAULT_BOTTOM_SIZE = 280;
9980
+ var MIN_SIZE = 200;
9981
+ function DefaultsReader({
9982
+ def,
9983
+ onValue
9984
+ }) {
9985
+ const Reader = def.Defaults;
9986
+ const report = useCallback9(
9987
+ (value) => onValue(def.id, value),
9988
+ [def.id, onValue]
9989
+ );
9990
+ return Reader ? /* @__PURE__ */ jsx13(Reader, { onValue: report }) : null;
9991
+ }
9992
+ function sideIcon(side) {
9993
+ if (side === "left") return ArrowLeftToLine;
9994
+ if (side === "bottom") return ArrowDownToLine;
9995
+ return ArrowRightToLine;
9996
+ }
9997
+ var CompanionFrame = memo(function CompanionFrame2({
9998
+ tab,
9999
+ isVisible,
10000
+ children
10001
+ }) {
10002
+ const defs = useCompanionDefs();
10003
+ const applicable = useMemo10(
10004
+ () => defs.filter((def) => companionApplies(def, tab)),
10005
+ // tab.host and tab.type decide it
10006
+ // eslint-disable-next-line react-hooks/exhaustive-deps
10007
+ [defs, tab.type, tab.host]
10008
+ );
10009
+ if (applicable.length === 0) return /* @__PURE__ */ jsx13(Fragment3, { children });
10010
+ return /* @__PURE__ */ jsx13(CompanionLayout, { tab, defs: applicable, isVisible, children });
10011
+ });
10012
+ function CompanionLayout({
10013
+ tab,
10014
+ defs,
10015
+ isVisible,
10016
+ children
10017
+ }) {
10018
+ const { t } = useTranslation8();
10019
+ const isMobile = useIsMobile();
10020
+ const key = companionKey(tab);
10021
+ const state2 = useCompanionState(key);
10022
+ const [defaults3, setDefaults] = useState16(
10023
+ {}
10024
+ );
10025
+ const [dragging, setDragging] = useState16(false);
10026
+ const [mounted, setMounted] = useState16(/* @__PURE__ */ new Set());
10027
+ const rootRef = useRef8(null);
10028
+ const onDefaults = useCallback9((id, value) => {
10029
+ setDefaults((prev) => {
10030
+ const old = prev[id];
10031
+ if (old && old.open === value.open && old.side === value.side && old.size === value.size && old.edgeButton === value.edgeButton) {
10032
+ return prev;
10033
+ }
10034
+ return { ...prev, [id]: value };
10035
+ });
10036
+ }, []);
10037
+ useEffect18(() => {
10038
+ if (state2.seen || !isVisible) return;
10039
+ const ready = defs.every(
10040
+ (def) => !def.Defaults || defaults3[def.id] !== void 0
10041
+ );
10042
+ if (!ready) return;
10043
+ const auto = defs.find((def) => defaults3[def.id]?.open);
10044
+ patchCompanionState(key, { seen: true, openId: auto?.id ?? null });
10045
+ }, [state2.seen, isVisible, defs, defaults3, key]);
10046
+ const active = defs.find((def) => def.id === state2.openId) ?? null;
10047
+ useEffect18(() => {
10048
+ if (!active) return;
10049
+ setMounted(
10050
+ (prev) => prev.has(active.id) ? prev : new Set(prev).add(active.id)
10051
+ );
10052
+ }, [active]);
10053
+ const activeDefaults = active ? defaults3[active.id] : void 0;
10054
+ const side = isMobile ? "bottom" : state2.side ?? activeDefaults?.side ?? "right";
10055
+ const baseSize = state2.size ?? activeDefaults?.size ?? (side === "bottom" ? DEFAULT_BOTTOM_SIZE : DEFAULT_SIDE_SIZE);
10056
+ const setSide = useCallback9(
10057
+ (next) => patchCompanionState(key, { side: next }),
10058
+ [key]
10059
+ );
10060
+ const close = useCallback9(
10061
+ () => patchCompanionState(key, { openId: null }),
10062
+ [key]
10063
+ );
10064
+ const startResize = (event) => {
10065
+ event.preventDefault();
10066
+ const root = rootRef.current;
10067
+ if (!root) return;
10068
+ const rect = root.getBoundingClientRect();
10069
+ const total = side === "bottom" ? rect.height : rect.width;
10070
+ const max = Math.max(MIN_SIZE, total - MIN_SIZE);
10071
+ setDragging(true);
10072
+ const move = (e) => {
10073
+ const raw = side === "right" ? rect.right - e.clientX : side === "left" ? e.clientX - rect.left : rect.bottom - e.clientY;
10074
+ patchCompanionState(key, {
10075
+ size: Math.round(Math.min(max, Math.max(MIN_SIZE, raw)))
10076
+ });
10077
+ };
10078
+ const up = () => {
10079
+ setDragging(false);
10080
+ window.removeEventListener("mousemove", move);
10081
+ window.removeEventListener("mouseup", up);
10082
+ };
10083
+ window.addEventListener("mousemove", move);
10084
+ window.addEventListener("mouseup", up);
10085
+ };
10086
+ const getHandle = useCallback9(
10087
+ () => tab.terminalRef?.current ?? null,
10088
+ [tab.terminalRef]
10089
+ );
10090
+ const sshHost = useMemo10(
10091
+ () => tab.host ? hostToSSHHost(tab.host) : void 0,
10092
+ [tab.host]
10093
+ );
10094
+ const showEdge = defs.filter((def) => defaults3[def.id]?.edgeButton !== false);
10095
+ const vertical = side === "bottom";
10096
+ const clamped = vertical ? Math.min(baseSize, 2e3) : baseSize;
10097
+ const paneStyle = isMobile ? { height: "55%" } : vertical ? { height: clamped } : { width: clamped };
10098
+ const SideIcon = sideIcon(side);
10099
+ return /* @__PURE__ */ jsxs6("div", { className: "flex h-full w-full min-h-0 min-w-0", children: [
10100
+ defs.map(
10101
+ (def) => def.Defaults ? /* @__PURE__ */ jsx13(DefaultsReader, { def, onValue: onDefaults }, def.id) : null
10102
+ ),
10103
+ /* @__PURE__ */ jsxs6(
10104
+ "div",
10105
+ {
10106
+ ref: rootRef,
10107
+ className: cn(
10108
+ "relative flex flex-1 min-h-0 min-w-0",
10109
+ vertical ? "flex-col" : side === "left" ? "flex-row-reverse" : "flex-row"
10110
+ ),
10111
+ children: [
10112
+ /* @__PURE__ */ jsx13("div", { className: "relative flex-1 min-h-0 min-w-0", children }),
10113
+ defs.map((def) => {
10114
+ if (!mounted.has(def.id)) return null;
10115
+ const open = active?.id === def.id;
10116
+ const Component = def.component;
10117
+ const Icon = def.icon;
10118
+ return /* @__PURE__ */ jsxs6(
10119
+ "div",
10120
+ {
10121
+ className: cn(
10122
+ "relative flex-shrink-0 flex-col min-h-0 min-w-0 bg-background border-border",
10123
+ open ? "flex" : "hidden",
10124
+ vertical ? "border-t" : side === "left" ? "border-r" : "border-l",
10125
+ isMobile && "absolute inset-x-0 bottom-0 z-20 shadow-lg"
10126
+ ),
10127
+ style: paneStyle,
10128
+ children: [
10129
+ !isMobile && /* @__PURE__ */ jsx13(
10130
+ "div",
10131
+ {
10132
+ role: "separator",
10133
+ "aria-label": t("companion.resize"),
10134
+ onMouseDown: startResize,
10135
+ className: cn(
10136
+ "absolute z-10",
10137
+ 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",
10138
+ dragging && "bg-primary/30"
10139
+ )
10140
+ }
10141
+ ),
10142
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 h-8 px-2 border-b border-border flex-shrink-0", children: [
10143
+ Icon && /* @__PURE__ */ jsx13(Icon, { className: "size-3.5 text-muted-foreground" }),
10144
+ /* @__PURE__ */ jsx13("span", { className: "text-xs font-medium truncate flex-1", children: def.titleKey ? t(def.titleKey) : def.id }),
10145
+ !isMobile && /* @__PURE__ */ jsx13(
10146
+ "button",
10147
+ {
10148
+ type: "button",
10149
+ className: "p-1 text-muted-foreground hover:text-foreground",
10150
+ title: t("companion.moveSide"),
10151
+ onClick: () => setSide(
10152
+ side === "right" ? "bottom" : side === "bottom" ? "left" : "right"
10153
+ ),
10154
+ children: /* @__PURE__ */ jsx13(SideIcon, { className: "size-3.5" })
10155
+ }
10156
+ ),
10157
+ /* @__PURE__ */ jsx13(
10158
+ "button",
10159
+ {
10160
+ type: "button",
10161
+ className: "p-1 text-muted-foreground hover:text-foreground",
10162
+ title: t("companion.close"),
10163
+ onClick: close,
10164
+ children: /* @__PURE__ */ jsx13(X, { className: "size-3.5" })
10165
+ }
10166
+ )
10167
+ ] }),
10168
+ /* @__PURE__ */ jsx13("div", { className: "flex-1 min-h-0 min-w-0 overflow-hidden", children: /* @__PURE__ */ jsx13(
10169
+ Component,
10170
+ {
10171
+ tab,
10172
+ host: tab.host,
10173
+ sshHost,
10174
+ getHandle,
10175
+ side,
10176
+ isVisible: isVisible && open,
10177
+ close,
10178
+ setSide
10179
+ }
10180
+ ) })
10181
+ ]
10182
+ },
10183
+ def.id
10184
+ );
10185
+ })
10186
+ ]
10187
+ }
10188
+ ),
10189
+ 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) => {
10190
+ const Icon = def.icon ?? PanelRight;
10191
+ const open = active?.id === def.id;
10192
+ return /* @__PURE__ */ jsx13(
10193
+ "button",
10194
+ {
10195
+ type: "button",
10196
+ title: def.titleKey ? t(def.titleKey) : def.id,
10197
+ "aria-pressed": open,
10198
+ onClick: () => patchCompanionState(key, {
10199
+ seen: true,
10200
+ openId: open ? null : def.id
10201
+ }),
10202
+ className: cn(
10203
+ "p-1 text-muted-foreground hover:text-foreground",
10204
+ open && "text-foreground bg-accent"
10205
+ ),
10206
+ children: /* @__PURE__ */ jsx13(Icon, { className: "size-3.5" })
10207
+ },
10208
+ def.id
10209
+ );
10210
+ }) })
10211
+ ] });
10212
+ }
10213
+
10060
10214
  // src/ui/shell/tabUtils.tsx
10061
- import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
10215
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
10062
10216
  var PluginsTab = lazy(
10063
- () => import("./PluginsTab-S33WIIGC.js").then((m) => ({ default: m.PluginsTab }))
10217
+ () => import("./PluginsTab-YAGYRBIE.js").then((m) => ({ default: m.PluginsTab }))
10064
10218
  );
10065
10219
  function preloadTabSurface(type) {
10066
10220
  const loader = getTabType(type)?.preload;
@@ -10073,37 +10227,37 @@ function EmptyState({
10073
10227
  icon: Icon,
10074
10228
  messageKey
10075
10229
  }) {
10076
- const { t } = useTranslation8();
10077
- return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center flex-1 gap-3 p-6 text-center", children: [
10078
- /* @__PURE__ */ jsx13("div", { className: "size-10 rounded-full bg-muted/40 flex items-center justify-center", children: /* @__PURE__ */ jsx13(Icon, { className: "size-5 text-muted-foreground/30" }) }),
10079
- /* @__PURE__ */ jsx13("span", { className: "text-sm font-semibold text-muted-foreground/60", children: t(messageKey) })
10230
+ const { t } = useTranslation9();
10231
+ return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-center justify-center flex-1 gap-3 p-6 text-center", children: [
10232
+ /* @__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" }) }),
10233
+ /* @__PURE__ */ jsx14("span", { className: "text-sm font-semibold text-muted-foreground/60", children: t(messageKey) })
10080
10234
  ] });
10081
10235
  }
10082
10236
  function TabChunkFallback() {
10083
- return /* @__PURE__ */ jsx13("div", { className: "flex h-full w-full items-center justify-center bg-background", children: /* @__PURE__ */ jsx13("div", { className: "size-5 rounded-full border-2 border-muted-foreground/30 border-t-muted-foreground/70 animate-spin" }) });
10237
+ 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" }) });
10084
10238
  }
10085
10239
  function withTabSuspense(node) {
10086
- return /* @__PURE__ */ jsx13(Suspense, { fallback: /* @__PURE__ */ jsx13(TabChunkFallback, {}), children: node });
10240
+ return /* @__PURE__ */ jsx14(Suspense, { fallback: /* @__PURE__ */ jsx14(TabChunkFallback, {}), children: node });
10087
10241
  }
10088
10242
  function PanelTabFrame({ children }) {
10089
- return /* @__PURE__ */ jsx13("div", { className: "flex h-full w-full justify-center overflow-y-auto bg-background", children: /* @__PURE__ */ jsx13("div", { className: "flex flex-col flex-1 min-h-0 w-full max-w-5xl", children }) });
10243
+ 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 }) });
10090
10244
  }
10091
10245
  function tabIcon(type) {
10092
10246
  switch (type) {
10093
10247
  case "user-profile":
10094
10248
  case "admin-settings":
10095
- return /* @__PURE__ */ jsx13(Settings, { className: "size-3.5" });
10249
+ return /* @__PURE__ */ jsx14(Settings, { className: "size-3.5" });
10096
10250
  case "split-screen":
10097
- return /* @__PURE__ */ jsx13(LayoutPanelLeft, { className: "size-3.5" });
10251
+ return /* @__PURE__ */ jsx14(LayoutPanelLeft, { className: "size-3.5" });
10098
10252
  case "plugins":
10099
- return /* @__PURE__ */ jsx13(Puzzle, { className: "size-3.5" });
10253
+ return /* @__PURE__ */ jsx14(Puzzle, { className: "size-3.5" });
10100
10254
  default: {
10101
10255
  const Icon = getTabType(type)?.icon;
10102
- return Icon ? /* @__PURE__ */ jsx13(Icon, { className: "size-3.5" }) : null;
10256
+ return Icon ? /* @__PURE__ */ jsx14(Icon, { className: "size-3.5" }) : null;
10103
10257
  }
10104
10258
  }
10105
10259
  }
10106
- var RegisteredTab = memo(function RegisteredTab2({
10260
+ var RegisteredTab = memo2(function RegisteredTab2({
10107
10261
  def,
10108
10262
  tab,
10109
10263
  isVisible,
@@ -10113,7 +10267,7 @@ var RegisteredTab = memo(function RegisteredTab2({
10113
10267
  }) {
10114
10268
  const { host, label } = tab;
10115
10269
  if (def.requiresHost && !host) {
10116
- return /* @__PURE__ */ jsx13(
10270
+ return /* @__PURE__ */ jsx14(
10117
10271
  EmptyState,
10118
10272
  {
10119
10273
  icon: Server,
@@ -10122,7 +10276,7 @@ var RegisteredTab = memo(function RegisteredTab2({
10122
10276
  );
10123
10277
  }
10124
10278
  const Component = def.component;
10125
- const content = /* @__PURE__ */ jsx13(
10279
+ const content = /* @__PURE__ */ jsx14(
10126
10280
  Component,
10127
10281
  {
10128
10282
  tab,
@@ -10139,10 +10293,10 @@ var RegisteredTab = memo(function RegisteredTab2({
10139
10293
  }
10140
10294
  );
10141
10295
  return withTabSuspense(
10142
- def.panelFrame ? /* @__PURE__ */ jsx13(PanelTabFrame, { children: content }) : content
10296
+ /* @__PURE__ */ jsx14(CompanionFrame, { tab, isVisible, children: def.panelFrame ? /* @__PURE__ */ jsx14(PanelTabFrame, { children: content }) : content })
10143
10297
  );
10144
10298
  });
10145
- var PanelAsTab = memo(function PanelAsTab2({
10299
+ var PanelAsTab = memo2(function PanelAsTab2({
10146
10300
  panel,
10147
10301
  isVisible,
10148
10302
  targetTab,
@@ -10150,7 +10304,7 @@ var PanelAsTab = memo(function PanelAsTab2({
10150
10304
  }) {
10151
10305
  const Panel = panel.component;
10152
10306
  return withTabSuspense(
10153
- /* @__PURE__ */ jsx13(PanelTabFrame, { children: /* @__PURE__ */ jsx13(
10307
+ /* @__PURE__ */ jsx14(PanelTabFrame, { children: /* @__PURE__ */ jsx14(
10154
10308
  Panel,
10155
10309
  {
10156
10310
  targetTab,
@@ -10175,7 +10329,7 @@ function renderTabContent(tab, context) {
10175
10329
  case "split-screen":
10176
10330
  return null;
10177
10331
  case "plugins":
10178
- return withTabSuspense(/* @__PURE__ */ jsx13(PluginsTab, {}));
10332
+ return withTabSuspense(/* @__PURE__ */ jsx14(PluginsTab, {}));
10179
10333
  case "user-profile":
10180
10334
  case "admin-settings":
10181
10335
  return null;
@@ -10185,7 +10339,7 @@ function renderTabContent(tab, context) {
10185
10339
  if (!def) {
10186
10340
  const panel = getPanel(tab.type);
10187
10341
  if (panel) {
10188
- return /* @__PURE__ */ jsx13(
10342
+ return /* @__PURE__ */ jsx14(
10189
10343
  PanelAsTab,
10190
10344
  {
10191
10345
  panel,
@@ -10195,9 +10349,9 @@ function renderTabContent(tab, context) {
10195
10349
  }
10196
10350
  );
10197
10351
  }
10198
- return /* @__PURE__ */ jsx13(PluginViewPlaceholder, { kind: "tab", viewId: tab.type });
10352
+ return /* @__PURE__ */ jsx14(PluginViewPlaceholder, { kind: "tab", viewId: tab.type });
10199
10353
  }
10200
- return /* @__PURE__ */ jsx13(
10354
+ return /* @__PURE__ */ jsx14(
10201
10355
  RegisteredTab,
10202
10356
  {
10203
10357
  def,
@@ -10265,20 +10419,6 @@ export {
10265
10419
  changeAppLanguage,
10266
10420
  rememberLoginLanguage,
10267
10421
  i18n_default,
10268
- probeServer,
10269
- getLinkPreview,
10270
- completeLink,
10271
- reloginLink,
10272
- unlinkServer,
10273
- syncNow,
10274
- updateSyncSettings,
10275
- getSyncConflicts,
10276
- settleSyncConflict,
10277
- getSyncErrors,
10278
- retrySyncErrors,
10279
- setSyncStatus,
10280
- refreshSyncStatus,
10281
- useSyncStatus,
10282
10422
  getPanel,
10283
10423
  listPanels,
10284
10424
  usePanels,