@termix-ssh/plugin-sdk 1.0.13 → 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 (46) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/PLUGIN-GUIDE.md +104 -69
  3. package/dist/backend.d.ts +500 -1
  4. package/dist/backend.js +7 -0
  5. package/dist/capabilities.js +4 -0
  6. package/dist/frontend.d.ts +45 -0
  7. package/dist/host/{PluginsTab-OXQQ7M4I.js → PluginsTab-YAGYRBIE.js} +3 -3
  8. package/dist/host/{chunk-T2PZWXFJ.js → chunk-2MU6WNSX.js} +1 -1
  9. package/dist/host/{chunk-54KCK6IZ.js → chunk-446UEDSK.js} +6 -2
  10. package/dist/host/{chunk-CN5UBTBP.js → chunk-7LEJ3HYF.js} +49 -3
  11. package/dist/host/{chunk-CALNJCHN.js → chunk-J2AILBXM.js} +561 -400
  12. package/dist/host/{chunk-ERGN4YW6.js → chunk-LQKK2JJA.js} +5 -1
  13. package/dist/host/core-classes.json +1 -1
  14. package/dist/host/{linked-server-5VFO4XQB.js → linked-server-OUF3POT7.js} +5 -3
  15. package/dist/host/{rbac-api-5YMBJAO5.js → rbac-api-LZVENREG.js} +1 -1
  16. package/dist/host/testing-host.js +4 -4
  17. package/dist/host/types/src/types/index.d.ts +10 -0
  18. package/dist/host/types/src/types/onboarding.d.ts +0 -1
  19. package/dist/host/types/src/types/ui-types.d.ts +3 -1
  20. package/dist/host/types/src/ui/api/auth-methods-api.d.ts +2 -0
  21. package/dist/host/types/src/ui/api/settings-api.d.ts +6 -0
  22. package/dist/host/types/src/ui/api/ssh-host-management-api.d.ts +6 -0
  23. package/dist/host/types/src/ui/auth/Auth.d.ts +1 -0
  24. package/dist/host/types/src/ui/lib/linked-server.d.ts +2 -0
  25. package/dist/host/types/src/ui/main-axios.d.ts +2 -2
  26. package/dist/host/types/src/ui/plugin-host/NativeView.d.ts +19 -0
  27. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +4 -8
  28. package/dist/host/types/src/ui/shell/CompanionFrame.d.ts +11 -0
  29. package/dist/host/types/src/ui/shell/companions.d.ts +63 -0
  30. package/dist/host/types/src/ui/sidebar/AuthEnrollmentSections.d.ts +4 -1
  31. package/dist/host/ui.js +171 -820
  32. package/dist/host/{use-permissions-ISEDRY3J.js → use-permissions-BYCDS5PD.js} +1 -1
  33. package/dist/manifest.d.ts +1 -1
  34. package/dist/manifest.js +1 -1
  35. package/dist/testing.d.ts +26 -2
  36. package/dist/testing.js +89 -1
  37. package/dist/vitest-preset.js +23 -0
  38. package/package.json +1 -1
  39. package/schema/manifest.schema.json +4 -0
  40. package/dist/host/types/src/ui/api/sync-api.d.ts +0 -93
  41. package/dist/host/types/src/ui/auth/ElectronLoginForm.d.ts +0 -9
  42. package/dist/host/types/src/ui/hooks/use-sync-status.d.ts +0 -4
  43. package/dist/host/types/src/ui/lib/sync-status.d.ts +0 -6
  44. package/dist/host/types/src/ui/settings/sync/LinkServerDialog.d.ts +0 -12
  45. package/dist/host/types/src/ui/settings/sync/ProxySettingsFields.d.ts +0 -15
  46. package/dist/host/types/src/ui/settings/sync/saved-servers.d.ts +0 -3
package/dist/host/ui.js CHANGED
@@ -32,7 +32,6 @@ import {
32
32
  challengeSecondFactor,
33
33
  changeAppLanguage,
34
34
  clearLocalAdaptivePreferences,
35
- completeLink,
36
35
  createPluginApp,
37
36
  createQuickConnectHost,
38
37
  defaultConnectAction,
@@ -45,16 +44,14 @@ import {
45
44
  getAdaptiveResourceBudget,
46
45
  getDefaultConnectionTab,
47
46
  getHostEditorSection,
48
- getLinkPreview,
49
47
  getLiveHostStatus,
50
48
  getLoginMethods,
51
49
  getPanel,
52
50
  getPollingEnvironmentMultiplier,
53
51
  getPreferredHostAction,
54
52
  getRegisteredDashboardCard,
53
+ getRememberDeviceAllowed,
55
54
  getStatusClasses,
56
- getSyncConflicts,
57
- getSyncErrors,
58
55
  getUserSecondFactors,
59
56
  globalShortcutHandler,
60
57
  groupRailItems,
@@ -92,18 +89,15 @@ import {
92
89
  paneNavHotkeyLabel,
93
90
  pluginWsUrl,
94
91
  preloadTabSurface,
95
- probeServer,
96
92
  promotableIds,
97
93
  protocolEnabled,
98
94
  protocolPort,
99
95
  quickConnectTargets,
100
96
  railItemLabel,
101
97
  recordHostActionPreference,
102
- refreshSyncStatus,
103
98
  registerHostEditorSection,
104
99
  registerLayout,
105
100
  registerTabCloseGuard,
106
- reloginLink,
107
101
  rem,
108
102
  remScale,
109
103
  rememberLoginLanguage,
@@ -112,7 +106,6 @@ import {
112
106
  requestManage,
113
107
  resetUserSecondFactors,
114
108
  resolveHostTabType,
115
- retrySyncErrors,
116
109
  rightDockableIds,
117
110
  runAdaptiveBackgroundTask,
118
111
  runAdaptivePolling,
@@ -121,20 +114,15 @@ import {
121
114
  setPluginLocaleResolver,
122
115
  setPluginThemes,
123
116
  setSplitDropHitTest,
124
- setSyncStatus,
125
- settleSyncConflict,
126
117
  shownPanes,
127
118
  sshHostToHost,
128
119
  startLoginRedirect,
129
120
  submitLoginMethod,
130
- syncNow,
131
121
  tabIcon,
132
122
  tabJumpHotkeyKeys,
133
123
  takePendingManageRequest,
134
124
  themeLabel,
135
- unlinkServer,
136
125
  unregisterHostEditorSection,
137
- updateSyncSettings,
138
126
  useActionSlot,
139
127
  useBranding,
140
128
  useHostActions,
@@ -166,17 +154,16 @@ import {
166
154
  useSshAuthEditors,
167
155
  useSshAuthProviders,
168
156
  useStatusColorScheme,
169
- useSyncStatus,
170
157
  useTabHandlesVersion,
171
158
  useTabStageStore,
172
159
  useTheme,
173
160
  useThemes,
174
161
  verifySecondFactor,
175
162
  withProtocolSettings
176
- } from "./chunk-CALNJCHN.js";
163
+ } from "./chunk-J2AILBXM.js";
177
164
  import {
178
165
  usePermissions
179
- } from "./chunk-T2PZWXFJ.js";
166
+ } from "./chunk-2MU6WNSX.js";
180
167
  import {
181
168
  AddButton,
182
169
  Card,
@@ -197,7 +184,7 @@ import {
197
184
  isPrerelease,
198
185
  reportBetaFeedbackUrl,
199
186
  reportCoreIssueUrl
200
- } from "./chunk-54KCK6IZ.js";
187
+ } from "./chunk-446UEDSK.js";
201
188
  import {
202
189
  Action,
203
190
  Activity,
@@ -387,7 +374,7 @@ import {
387
374
  useSurfaceKind,
388
375
  useUiPreference,
389
376
  useUiPreferencesContext
390
- } from "./chunk-ERGN4YW6.js";
377
+ } from "./chunk-LQKK2JJA.js";
391
378
  import {
392
379
  CREDENTIAL_ROW_FIELD_KEYS,
393
380
  PRESETS,
@@ -450,7 +437,9 @@ import {
450
437
  getHostAccess,
451
438
  getHostAuthOverride,
452
439
  getHostPassword,
440
+ getHostPolicy,
453
441
  getHostSidebarPreferences,
442
+ getHttpsSettings,
454
443
  getLogLevel,
455
444
  getPasswordLoginAllowed,
456
445
  getPasswordResetAllowed,
@@ -531,6 +520,7 @@ import {
531
520
  updateExternalAutoProvision,
532
521
  updateFolderMetadata,
533
522
  updateHostAccess,
523
+ updateHttpsSettings,
534
524
  updateLogLevel,
535
525
  updatePasswordLoginAllowed,
536
526
  updatePasswordResetAllowed,
@@ -540,11 +530,12 @@ import {
540
530
  updateSecondFactorAfterExternalLogin,
541
531
  updateSessionTimeout,
542
532
  updateStatusCheckSettings,
533
+ useDesktopLinked,
543
534
  usePluginStore,
544
535
  useTabs,
545
536
  useTabsSafe,
546
537
  verifyPasswordResetCode
547
- } from "./chunk-CN5UBTBP.js";
538
+ } from "./chunk-7LEJ3HYF.js";
548
539
  import {
549
540
  define_import_meta_env_default,
550
541
  init_define_import_meta_env
@@ -3324,10 +3315,11 @@ init_define_import_meta_env();
3324
3315
  function isInMobileWebView() {
3325
3316
  return /Termix-Mobile\/(Android|iOS)/.test(navigator.userAgent) || !!window.ReactNativeWebView;
3326
3317
  }
3318
+ var DESKTOP_USER_AGENT = /Termix-Desktop\/|\btermix\/[\w.-]+ .*\bElectron\//i;
3327
3319
  function isInElectronWebView() {
3328
3320
  if (isInMobileWebView()) return false;
3329
3321
  if (window.IS_ELECTRON_WEBVIEW) return true;
3330
- if (!/Termix-Desktop\//.test(navigator.userAgent)) return false;
3322
+ if (!DESKTOP_USER_AGENT.test(navigator.userAgent)) return false;
3331
3323
  try {
3332
3324
  if (window.self !== window.top) return true;
3333
3325
  } catch {
@@ -4343,6 +4335,7 @@ function Auth({ onLogin }) {
4343
4335
  const [username, setUsername] = useState7("");
4344
4336
  const [password, setPassword] = useState7("");
4345
4337
  const [confirmPassword, setConfirmPassword] = useState7("");
4338
+ const [rememberAllowed, setRememberAllowed] = useState7(true);
4346
4339
  const [rememberMe, setRememberMe] = useState7(() => {
4347
4340
  try {
4348
4341
  return localStorage.getItem("rememberMe") === "true";
@@ -4463,6 +4456,11 @@ function Auth({ onLogin }) {
4463
4456
  getPasswordResetAllowed().then((allowed) => setPasswordResetAllowed(allowed)).catch(() => setPasswordResetAllowed(false));
4464
4457
  void startPreLoginPlugins().catch(() => {
4465
4458
  }).finally(() => setAuthPluginsLoading(false));
4459
+ getRememberDeviceAllowed().then((allowed) => {
4460
+ setRememberAllowed(allowed);
4461
+ if (!allowed) setRememberMe(false);
4462
+ }).catch(() => {
4463
+ });
4466
4464
  getLoginMethods().then((methods) => setAuthMethods(methods)).catch(() => setAuthMethods([])).finally(() => setAuthMethodsLoaded(true));
4467
4465
  }, [localDesktopAuth]);
4468
4466
  useEffect6(() => {
@@ -5358,7 +5356,7 @@ function Auth({ onLogin }) {
5358
5356
  /* @__PURE__ */ jsx28("p", { className: "text-xs text-muted-foreground", children: view === "login" ? t3("auth.loginSubtitle", "") : view === "register" ? t3("auth.registerSubtitle", "") : t3("auth.loginWithExternalDesc") })
5359
5357
  ] }),
5360
5358
  view === "external" && /* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-4", children: [
5361
- /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
5359
+ rememberAllowed && /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
5362
5360
  /* @__PURE__ */ jsx28(
5363
5361
  Checkbox,
5364
5362
  {
@@ -5409,7 +5407,7 @@ function Auth({ onLogin }) {
5409
5407
  }
5410
5408
  ) }),
5411
5409
  /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between", children: [
5412
- /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
5410
+ rememberAllowed ? /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
5413
5411
  /* @__PURE__ */ jsx28(
5414
5412
  Checkbox,
5415
5413
  {
@@ -5427,7 +5425,7 @@ function Auth({ onLogin }) {
5427
5425
  children: t3("auth.rememberMe")
5428
5426
  }
5429
5427
  )
5430
- ] }),
5428
+ ] }) : /* @__PURE__ */ jsx28("span", {}),
5431
5429
  passwordResetAllowed && /* @__PURE__ */ jsx28(
5432
5430
  "button",
5433
5431
  {
@@ -31706,7 +31704,7 @@ function Gate({
31706
31704
  const [dropped, setDropped] = useState18(false);
31707
31705
  const maxAttempts = retry?.maxAttempts ?? 8;
31708
31706
  const gateRef = useRef10(null);
31709
- const write2 = useCallback8(
31707
+ const write = useCallback8(
31710
31708
  (type) => (text, stage = "connection") => addLog({ type, stage, message: text }),
31711
31709
  [addLog]
31712
31710
  );
@@ -31735,12 +31733,12 @@ function Gate({
31735
31733
  } = retryState;
31736
31734
  const log = useMemo7(
31737
31735
  () => ({
31738
- info: write2("info"),
31739
- success: write2("success"),
31740
- warning: write2("warning"),
31741
- error: write2("error")
31736
+ info: write("info"),
31737
+ success: write("success"),
31738
+ warning: write("warning"),
31739
+ error: write("error")
31742
31740
  }),
31743
- [write2]
31741
+ [write]
31744
31742
  );
31745
31743
  const markFailed = useCallback8(
31746
31744
  (err) => {
@@ -33030,776 +33028,132 @@ var LIGHT_SWATCH = BUILTIN_THEMES.find(
33030
33028
  ).swatch;
33031
33029
  var DARK_SWATCH = BUILTIN_THEMES.find((theme2) => theme2.id === "dark").swatch;
33032
33030
 
33033
- // src/ui/settings/sync/LinkServerDialog.tsx
33034
- init_define_import_meta_env();
33035
- import { useEffect as useEffect17, useState as useState24 } from "react";
33036
- import { useTranslation as useTranslation26 } from "react-i18next";
33037
- import { toast as toast5 } from "sonner";
33038
-
33039
- // src/ui/auth/ElectronLoginForm.tsx
33031
+ // src/ui/plugin-host/NativeView.tsx
33040
33032
  init_define_import_meta_env();
33041
- import { useState as useState23, useEffect as useEffect16, useRef as useRef14, useCallback as useCallback10 } from "react";
33042
- import { useTranslation as useTranslation24 } from "react-i18next";
33043
- import { jsx as jsx53, jsxs as jsxs40 } from "react/jsx-runtime";
33044
- var AUTH_MESSAGE_SOURCES = /* @__PURE__ */ new Set([
33045
- "auth_component",
33046
- "second_factor_auth_component",
33047
- "method_auth_component",
33048
- "oidc_callback"
33049
- ]);
33050
- function ElectronLoginForm({
33051
- serverUrl,
33052
- onAuthSuccess,
33053
- onChangeServer,
33054
- targetPurpose = "local"
33033
+ import { useEffect as useEffect16, useRef as useRef14 } from "react";
33034
+ import { Fragment as Fragment8, jsx as jsx53 } from "react/jsx-runtime";
33035
+ function embeddedViewApi() {
33036
+ if (typeof window === "undefined") return null;
33037
+ const api = window.electronAPI?.embeddedView;
33038
+ return api ?? null;
33039
+ }
33040
+ var OVERLAY = [
33041
+ '[role="dialog"]',
33042
+ '[role="alertdialog"]',
33043
+ '[role="menu"]',
33044
+ '[role="listbox"]',
33045
+ "[data-radix-popper-content-wrapper]"
33046
+ ].join(",");
33047
+ function overlayOpen(host) {
33048
+ return Array.from(document.querySelectorAll(OVERLAY)).some(
33049
+ (node) => !host.contains(node)
33050
+ );
33051
+ }
33052
+ function NativeView({
33053
+ url,
33054
+ ignoreCert,
33055
+ hidden,
33056
+ fallback,
33057
+ className,
33058
+ style
33055
33059
  }) {
33056
- const { t: t3 } = useTranslation24();
33057
- const [loading, setLoading] = useState23(true);
33058
- const [error, setError] = useState23(null);
33059
- const [isAuthenticating, setIsAuthenticating] = useState23(false);
33060
- const isAuthenticatingRef = useRef14(false);
33061
- const iframeRef = useRef14(null);
33062
- const hasAuthenticatedRef = useRef14(false);
33063
- const [currentUrl, setCurrentUrl] = useState23(serverUrl);
33064
- const hasLoadedOnce = useRef14(false);
33065
- const onAuthSuccessRef = useRef14(onAuthSuccess);
33060
+ const host = useRef14(null);
33061
+ const api = embeddedViewApi();
33062
+ const hiddenRef = useRef14(!!hidden);
33063
+ hiddenRef.current = !!hidden;
33064
+ const sync = useRef14(() => {
33065
+ });
33066
33066
  useEffect16(() => {
33067
- onAuthSuccessRef.current = onAuthSuccess;
33068
- }, [onAuthSuccess]);
33069
- const handleAuthSuccess = useCallback10(
33070
- async (token) => {
33071
- if (hasAuthenticatedRef.current || isAuthenticatingRef.current) return;
33072
- hasAuthenticatedRef.current = true;
33073
- isAuthenticatingRef.current = true;
33074
- setIsAuthenticating(true);
33075
- try {
33076
- if (targetPurpose === "link") {
33077
- if (!token) {
33078
- throw new Error(t3("errors.authTokenMissing"));
33079
- }
33080
- } else if (token) {
33081
- localStorage.setItem("jwt", token);
33082
- }
33083
- await onAuthSuccessRef.current(token);
33084
- } catch (err) {
33085
- setError(
33086
- err instanceof Error && err.message ? err.message : t3("errors.authTokenSaveFailed")
33087
- );
33088
- isAuthenticatingRef.current = false;
33089
- setIsAuthenticating(false);
33090
- hasAuthenticatedRef.current = false;
33067
+ const element = host.current;
33068
+ const view = embeddedViewApi();
33069
+ if (!element || !view) return;
33070
+ const id = crypto.randomUUID();
33071
+ let opened = false;
33072
+ let lastBounds = "";
33073
+ let lastVisible = null;
33074
+ let frame = 0;
33075
+ const update = () => {
33076
+ frame = 0;
33077
+ const rect = element.getBoundingClientRect();
33078
+ const bounds = {
33079
+ x: rect.left,
33080
+ y: rect.top,
33081
+ width: rect.width,
33082
+ height: rect.height
33083
+ };
33084
+ const visible = !hiddenRef.current && rect.width > 0 && rect.height > 0 && element.offsetParent !== null && !overlayOpen(element);
33085
+ if (!opened) {
33086
+ opened = true;
33087
+ lastBounds = JSON.stringify(bounds);
33088
+ lastVisible = visible;
33089
+ void view.open({ id, url, ignoreCert, bounds, visible });
33090
+ return;
33091
33091
  }
33092
- },
33093
- [t3, targetPurpose]
33094
- );
33095
- useEffect16(() => {
33096
- let serverOrigin;
33097
- try {
33098
- serverOrigin = new URL(serverUrl).origin;
33099
- } catch {
33100
- return;
33101
- }
33102
- const handleMessage = async (event) => {
33103
- try {
33104
- if (event.source !== iframeRef.current?.contentWindow) return;
33105
- if (event.origin !== serverOrigin) return;
33106
- if (!event.data || typeof event.data !== "object") return;
33107
- const {
33108
- type,
33109
- platform,
33110
- source,
33111
- token,
33112
- authUrl,
33113
- callbackPort,
33114
- providerId
33115
- } = event.data;
33116
- if (type === "AUTH_SUCCESS" && platform === "desktop" && AUTH_MESSAGE_SOURCES.has(source)) {
33117
- await handleAuthSuccess(token ?? null);
33118
- return;
33119
- }
33120
- if (type === "OIDC_SYSTEM_BROWSER_AUTH" && authUrl && callbackPort) {
33121
- const sendResultToIframe = (result2) => {
33122
- iframeRef.current?.contentWindow?.postMessage(
33123
- {
33124
- type: "OIDC_SYSTEM_BROWSER_AUTH_RESULT",
33125
- source: "oidc_system_browser_auth",
33126
- providerId,
33127
- ...result2
33128
- },
33129
- serverOrigin
33130
- );
33131
- };
33132
- const electronAPI = window.electronAPI;
33133
- if (!electronAPI?.externalBrowserLogin) {
33134
- sendResultToIframe({
33135
- success: false,
33136
- error: t3("errors.failedExternalLogin")
33137
- });
33138
- return;
33139
- }
33140
- const result = await electronAPI.externalBrowserLogin(
33141
- authUrl,
33142
- callbackPort
33143
- );
33144
- if (result.success && result.token) {
33145
- await handleAuthSuccess(result.token);
33146
- return;
33147
- }
33148
- if (result.secondFactor) {
33149
- sendResultToIframe({
33150
- success: false,
33151
- secondFactor: true,
33152
- tempToken: result.tempToken,
33153
- factors: result.factors
33154
- });
33155
- return;
33156
- }
33157
- sendResultToIframe({
33158
- success: false,
33159
- error: result.error || t3("errors.failedExternalLogin")
33160
- });
33161
- }
33162
- } catch (err) {
33163
- const providerId = event.data && typeof event.data === "object" && typeof event.data.providerId === "number" ? event.data.providerId : void 0;
33164
- const error2 = getErrorMessage(err, t3("errors.failedExternalLogin"));
33165
- iframeRef.current?.contentWindow?.postMessage(
33166
- {
33167
- type: "OIDC_SYSTEM_BROWSER_AUTH_RESULT",
33168
- source: "oidc_system_browser_auth",
33169
- providerId,
33170
- success: false,
33171
- error: error2
33172
- },
33173
- serverOrigin
33174
- );
33092
+ const key = JSON.stringify(bounds);
33093
+ if (key !== lastBounds) {
33094
+ lastBounds = key;
33095
+ void view.setBounds(id, bounds);
33175
33096
  }
33176
- };
33177
- window.addEventListener("message", handleMessage);
33178
- return () => window.removeEventListener("message", handleMessage);
33179
- }, [handleAuthSuccess, serverUrl, t3]);
33180
- useEffect16(() => {
33181
- const iframe = iframeRef.current;
33182
- if (!iframe) return;
33183
- const handleLoad = () => {
33184
- setLoading(false);
33185
- hasLoadedOnce.current = true;
33186
- setError(null);
33187
- try {
33188
- if (iframe.contentWindow) {
33189
- setCurrentUrl(iframe.contentWindow.location.href);
33190
- }
33191
- } catch {
33192
- setCurrentUrl(serverUrl);
33097
+ if (visible !== lastVisible) {
33098
+ lastVisible = visible;
33099
+ void view.setVisible(id, visible);
33193
33100
  }
33194
33101
  };
33195
- const handleError = () => {
33196
- setLoading(false);
33197
- if (hasLoadedOnce.current) {
33198
- setError(t3("errors.failedToLoadServer"));
33199
- }
33102
+ const schedule = () => {
33103
+ if (!frame) frame = requestAnimationFrame(update);
33200
33104
  };
33201
- iframe.addEventListener("load", handleLoad);
33202
- iframe.addEventListener("error", handleError);
33105
+ sync.current = schedule;
33106
+ const resize = new ResizeObserver(schedule);
33107
+ resize.observe(element);
33108
+ const mutations = new MutationObserver(schedule);
33109
+ mutations.observe(document.body, {
33110
+ childList: true,
33111
+ subtree: true,
33112
+ attributes: true,
33113
+ attributeFilter: ["class", "style", "hidden", "data-state"]
33114
+ });
33115
+ window.addEventListener("resize", schedule);
33116
+ window.addEventListener("scroll", schedule, true);
33117
+ const timer = window.setInterval(schedule, 500);
33118
+ schedule();
33203
33119
  return () => {
33204
- iframe.removeEventListener("load", handleLoad);
33205
- iframe.removeEventListener("error", handleError);
33120
+ sync.current = () => {
33121
+ };
33122
+ resize.disconnect();
33123
+ mutations.disconnect();
33124
+ window.removeEventListener("resize", schedule);
33125
+ window.removeEventListener("scroll", schedule, true);
33126
+ window.clearInterval(timer);
33127
+ if (frame) cancelAnimationFrame(frame);
33128
+ void view.close(id);
33206
33129
  };
33207
- }, [serverUrl, t3]);
33208
- const handleRefresh = () => {
33209
- if (iframeRef.current) {
33210
- iframeRef.current.src = serverUrl;
33211
- setLoading(true);
33212
- setError(null);
33213
- }
33214
- };
33215
- const displayUrl = currentUrl.replace(/^https?:\/\//, "");
33216
- return /* @__PURE__ */ jsxs40("div", { className: "relative w-full h-full bg-background flex flex-col", children: [
33217
- isAuthenticating && /* @__PURE__ */ jsx53("div", { className: "absolute inset-0 flex items-center justify-center bg-background z-50", children: /* @__PURE__ */ jsx53(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" }) }),
33218
- !isAuthenticating && /* @__PURE__ */ jsxs40("div", { className: "flex items-center justify-between p-4 pr-12 bg-background border-b border-border", children: [
33219
- /* @__PURE__ */ jsxs40(
33220
- "button",
33221
- {
33222
- onClick: onChangeServer,
33223
- className: "flex items-center gap-2 text-foreground hover:text-primary transition-colors",
33224
- children: [
33225
- /* @__PURE__ */ jsx53(ArrowLeft, { className: "h-5 w-5" }),
33226
- /* @__PURE__ */ jsx53("span", { className: "text-base font-medium", children: t3("sync.wizard.back") })
33227
- ]
33228
- }
33229
- ),
33230
- /* @__PURE__ */ jsx53("div", { className: "flex-1 mx-4 text-center", children: /* @__PURE__ */ jsx53("span", { className: "text-muted-foreground text-sm truncate block", children: displayUrl }) }),
33231
- /* @__PURE__ */ jsx53(
33232
- "button",
33233
- {
33234
- onClick: handleRefresh,
33235
- className: "p-2 text-foreground hover:text-primary transition-colors",
33236
- disabled: loading,
33237
- children: /* @__PURE__ */ jsx53(RefreshCw, { className: `h-5 w-5 ${loading ? "animate-spin" : ""}` })
33238
- }
33239
- )
33240
- ] }),
33241
- error && !isAuthenticating && /* @__PURE__ */ jsx53("div", { className: "absolute top-20 left-1/2 transform -translate-x-1/2 z-50 w-full max-w-md px-4", children: /* @__PURE__ */ jsxs40(Alert, { variant: "destructive", children: [
33242
- /* @__PURE__ */ jsx53(CircleAlert, { className: "h-4 w-4" }),
33243
- /* @__PURE__ */ jsx53(AlertTitle, { children: t3("common.error") }),
33244
- /* @__PURE__ */ jsx53(AlertDescription, { children: error })
33245
- ] }) }),
33246
- loading && !isAuthenticating && /* @__PURE__ */ jsx53(
33247
- "div",
33248
- {
33249
- className: "absolute inset-0 flex items-center justify-center bg-background z-40",
33250
- style: { marginTop: "60px" },
33251
- children: /* @__PURE__ */ jsxs40("div", { className: "flex items-center", children: [
33252
- /* @__PURE__ */ jsx53(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" }),
33253
- /* @__PURE__ */ jsx53("span", { className: "ml-3 text-muted-foreground", children: t3("auth.loadingServer") })
33254
- ] })
33255
- }
33256
- ),
33257
- /* @__PURE__ */ jsx53(
33258
- "div",
33259
- {
33260
- className: "flex-1 overflow-hidden",
33261
- style: { visibility: isAuthenticating ? "hidden" : "visible" },
33262
- children: /* @__PURE__ */ jsx53(
33263
- "iframe",
33264
- {
33265
- ref: iframeRef,
33266
- src: serverUrl,
33267
- className: "w-full h-full border-0",
33268
- title: t3("auth.serverAuthentication"),
33269
- sandbox: "allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-storage-access-by-user-activation allow-top-navigation allow-top-navigation-by-user-activation allow-modals allow-downloads",
33270
- allow: "clipboard-read; clipboard-write; publickey-credentials-get; publickey-credentials-create"
33271
- }
33272
- )
33273
- }
33274
- )
33275
- ] });
33276
- }
33277
-
33278
- // src/ui/settings/sync/ProxySettingsFields.tsx
33279
- init_define_import_meta_env();
33280
- import { useTranslation as useTranslation25 } from "react-i18next";
33281
- import { jsx as jsx54, jsxs as jsxs41 } from "react/jsx-runtime";
33282
- function ProxySettingsFields({
33283
- customHeaders,
33284
- basicAuth,
33285
- onChange
33286
- }) {
33287
- const { t: t3 } = useTranslation25();
33288
- const setHeader = (index, patch) => {
33289
- onChange({
33290
- customHeaders: customHeaders.map(
33291
- (header, i2) => i2 === index ? { ...header, ...patch } : header
33292
- )
33293
- });
33294
- };
33295
- return /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-4 border border-border p-3", children: [
33296
- /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-2", children: [
33297
- /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-0.5", children: [
33298
- /* @__PURE__ */ jsx54("span", { className: "text-xs font-semibold", children: t3("sync.proxy.headersTitle") }),
33299
- /* @__PURE__ */ jsx54("span", { className: "text-[10px] text-muted-foreground", children: t3("sync.proxy.headersHint") })
33300
- ] }),
33301
- customHeaders.map((header, index) => /* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-2", children: [
33302
- /* @__PURE__ */ jsx54(
33303
- Input,
33304
- {
33305
- className: "rounded-none h-8 text-xs",
33306
- placeholder: t3("sync.proxy.headerName"),
33307
- value: header.name,
33308
- onChange: (event) => setHeader(index, { name: event.target.value })
33309
- }
33310
- ),
33311
- /* @__PURE__ */ jsx54(
33312
- PasswordInput,
33313
- {
33314
- className: "rounded-none h-8 text-xs",
33315
- placeholder: header.set ? t3("sync.proxy.savedValue") : t3("sync.proxy.headerValue"),
33316
- value: header.value,
33317
- onChange: (event) => setHeader(index, { value: event.target.value })
33318
- }
33319
- ),
33320
- /* @__PURE__ */ jsx54(
33321
- "button",
33322
- {
33323
- type: "button",
33324
- "aria-label": t3("sync.proxy.removeHeader"),
33325
- className: "p-1.5 text-muted-foreground hover:text-destructive",
33326
- onClick: () => onChange({
33327
- customHeaders: customHeaders.filter((_, i2) => i2 !== index)
33328
- }),
33329
- children: /* @__PURE__ */ jsx54(Trash, { className: "size-3.5" })
33330
- }
33331
- )
33332
- ] }, index)),
33333
- /* @__PURE__ */ jsxs41(
33334
- "button",
33335
- {
33336
- type: "button",
33337
- className: "self-start flex items-center gap-1 text-[10px] font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground",
33338
- onClick: () => onChange({
33339
- customHeaders: [...customHeaders, { name: "", value: "" }]
33340
- }),
33341
- children: [
33342
- /* @__PURE__ */ jsx54(Plus, { className: "size-3" }),
33343
- t3("sync.proxy.addHeader")
33344
- ]
33345
- }
33346
- )
33347
- ] }),
33348
- /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-2 border-t border-border pt-3", children: [
33349
- /* @__PURE__ */ jsxs41("div", { className: "flex items-center justify-between gap-3", children: [
33350
- /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-0.5", children: [
33351
- /* @__PURE__ */ jsx54("span", { className: "text-xs font-semibold", children: t3("sync.proxy.basicAuthTitle") }),
33352
- /* @__PURE__ */ jsx54("span", { className: "text-[10px] text-muted-foreground", children: t3("sync.proxy.basicAuthHint") })
33353
- ] }),
33354
- /* @__PURE__ */ jsx54(
33355
- Switch2,
33356
- {
33357
- checked: !!basicAuth,
33358
- onCheckedChange: (checked) => onChange({
33359
- basicAuth: checked ? { username: "", password: "" } : null
33360
- })
33361
- }
33362
- )
33363
- ] }),
33364
- basicAuth && /* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-2", children: [
33365
- /* @__PURE__ */ jsx54(
33366
- Input,
33367
- {
33368
- className: "rounded-none h-8 text-xs",
33369
- placeholder: t3("sync.proxy.username"),
33370
- value: basicAuth.username,
33371
- onChange: (event) => onChange({
33372
- basicAuth: { ...basicAuth, username: event.target.value }
33373
- })
33374
- }
33375
- ),
33376
- /* @__PURE__ */ jsx54(
33377
- PasswordInput,
33378
- {
33379
- className: "rounded-none h-8 text-xs",
33380
- placeholder: basicAuth.set ? t3("sync.proxy.savedValue") : t3("sync.proxy.password"),
33381
- value: basicAuth.password,
33382
- onChange: (event) => onChange({
33383
- basicAuth: { ...basicAuth, password: event.target.value }
33384
- })
33385
- }
33386
- )
33387
- ] })
33388
- ] })
33389
- ] });
33390
- }
33391
-
33392
- // src/ui/settings/sync/saved-servers.ts
33393
- init_define_import_meta_env();
33394
- var SAVED_URLS_KEY = "termix_saved_server_urls";
33395
- var MAX_SAVED_URLS = 5;
33396
- function getSavedServerUrls() {
33397
- try {
33398
- const parsed = JSON.parse(localStorage.getItem(SAVED_URLS_KEY) ?? "[]");
33399
- return Array.isArray(parsed) ? parsed.filter((url) => typeof url === "string") : [];
33400
- } catch {
33401
- return [];
33402
- }
33403
- }
33404
- function write(urls) {
33405
- try {
33406
- localStorage.setItem(SAVED_URLS_KEY, JSON.stringify(urls));
33407
- } catch {
33408
- }
33409
- }
33410
- function rememberServerUrl(url) {
33411
- const trimmed = url.trim().replace(/\/+$/, "");
33412
- if (!trimmed) return getSavedServerUrls();
33413
- const urls = [
33414
- trimmed,
33415
- ...getSavedServerUrls().filter((saved) => saved !== trimmed)
33416
- ].slice(0, MAX_SAVED_URLS);
33417
- write(urls);
33418
- return urls;
33419
- }
33420
- function forgetServerUrl(url) {
33421
- const urls = getSavedServerUrls().filter((saved) => saved !== url);
33422
- write(urls);
33423
- return urls;
33424
- }
33425
-
33426
- // src/ui/settings/sync/LinkServerDialog.tsx
33427
- import { Fragment as Fragment8, jsx as jsx55, jsxs as jsxs42 } from "react/jsx-runtime";
33428
- function problemText(t3, problem, message) {
33429
- switch (problem) {
33430
- case "invalid_url":
33431
- return t3("sync.problems.invalidUrl");
33432
- case "tls_untrusted":
33433
- return t3("sync.problems.tlsUntrusted");
33434
- case "basic_auth_required":
33435
- return t3("sync.problems.basicAuthRequired");
33436
- case "proxy_login":
33437
- return t3("sync.problems.proxyLogin");
33438
- case "not_termix":
33439
- return t3("sync.problems.notTermix");
33440
- case "version_mismatch":
33441
- return t3("sync.problems.versionMismatch");
33442
- case "different_account":
33443
- return t3("sync.problems.differentAccount");
33444
- default:
33445
- return message ? t3("sync.problems.unreachableWithReason", { reason: message }) : t3("sync.problems.unreachable");
33446
- }
33447
- }
33448
- function LinkServerDialog({
33449
- open,
33450
- onOpenChange,
33451
- onLinked,
33452
- relogin
33453
- }) {
33454
- const { t: t3 } = useTranslation26();
33455
- const [step, setStep] = useState24(relogin ? "signin" : "server");
33456
- const [savedUrls, setSavedUrls] = useState24(getSavedServerUrls);
33457
- const [target, setTarget] = useState24({
33458
- serverUrl: relogin?.serverUrl ?? "",
33459
- customHeaders: [],
33460
- basicAuth: null,
33461
- allowInvalidCertificate: false
33462
- });
33463
- const [showAdvanced, setShowAdvanced] = useState24(false);
33464
- const [checking, setChecking] = useState24(false);
33465
- const [problem, setProblem] = useState24(null);
33466
- const [problemKind, setProblemKind] = useState24(null);
33467
- const [serverName, setServerName] = useState24();
33468
- const [token, setToken] = useState24(null);
33469
- const [counts, setCounts] = useState24({});
33470
- const [mode, setMode] = useState24("merge");
33471
- const [finishError, setFinishError] = useState24(null);
33472
- useEffect17(() => {
33473
- if (!open) return;
33474
- setStep(relogin ? "signin" : "server");
33475
- setProblem(null);
33476
- setProblemKind(null);
33477
- setToken(null);
33478
- setFinishError(null);
33479
- setMode("merge");
33480
- if (relogin) {
33481
- setTarget((current) => ({ ...current, serverUrl: relogin.serverUrl }));
33482
- return;
33483
- }
33484
- window.electronAPI?.invoke?.("get-previous-server-url").then((result) => {
33485
- const url = result?.serverUrl;
33486
- if (url) {
33487
- setTarget(
33488
- (current) => current.serverUrl ? current : { ...current, serverUrl: url }
33489
- );
33490
- }
33491
- }).catch(() => {
33492
- });
33493
- }, [open, relogin]);
33494
- const handleCheck = async () => {
33495
- setChecking(true);
33496
- setProblem(null);
33497
- setProblemKind(null);
33498
- try {
33499
- const result = await probeServer(target);
33500
- if (!result.ok) {
33501
- setProblemKind(result.problem ?? null);
33502
- setProblem(problemText(t3, result.problem, result.message));
33503
- if (result.problem === "basic_auth_required" || result.problem === "proxy_login") {
33504
- setShowAdvanced(true);
33505
- }
33506
- if (result.problem === "basic_auth_required" && !target.basicAuth) {
33507
- setTarget((current) => ({
33508
- ...current,
33509
- basicAuth: { username: "", password: "" }
33510
- }));
33511
- }
33512
- return;
33513
- }
33514
- setTarget((current) => ({
33515
- ...current,
33516
- serverUrl: result.serverUrl ?? current.serverUrl
33517
- }));
33518
- setServerName(result.name);
33519
- setStep("signin");
33520
- } catch {
33521
- setProblem(problemText(t3, "unreachable", void 0));
33522
- } finally {
33523
- setChecking(false);
33524
- }
33525
- };
33526
- const finish = async (sessionToken, chosen) => {
33527
- setStep("finishing");
33528
- setFinishError(null);
33529
- try {
33530
- const status = await completeLink({
33531
- ...target,
33532
- token: sessionToken,
33533
- mode: chosen,
33534
- serverName
33535
- });
33536
- rememberServerUrl(target.serverUrl);
33537
- toast5.success(t3("sync.wizard.linked"));
33538
- onLinked(status);
33539
- onOpenChange(false);
33540
- } catch (error) {
33541
- const data = error?.response?.data;
33542
- setFinishError(problemText(t3, data?.problem, data?.message));
33543
- setStep("choose");
33544
- }
33545
- };
33546
- const handleSignedIn = async (sessionToken) => {
33547
- if (!sessionToken) return;
33548
- if (relogin) {
33549
- try {
33550
- const status = await reloginLink(sessionToken);
33551
- toast5.success(t3("sync.wizard.signedInAgain"));
33552
- onLinked(status);
33553
- onOpenChange(false);
33554
- } catch (error) {
33555
- const data = error?.response?.data;
33556
- throw new Error(
33557
- data?.error === "different_account" ? t3("sync.problems.differentAccount") : problemText(t3, data?.problem, data?.message)
33558
- );
33559
- }
33560
- return;
33561
- }
33562
- setToken(sessionToken);
33563
- const preview = await getLinkPreview().catch(() => ({}));
33564
- setCounts(preview);
33565
- const total = Object.values(preview).reduce((sum, count) => sum + count, 0);
33566
- if (total === 0) {
33567
- await finish(sessionToken, "merge");
33568
- return;
33569
- }
33570
- setStep("choose");
33571
- };
33572
- const localTotal = Object.values(counts).reduce(
33573
- (sum, count) => sum + count,
33574
- 0
33575
- );
33576
- const wide = step === "signin";
33577
- const title = relogin ? t3("sync.wizard.reloginTitle") : t3("sync.wizard.title");
33578
- const description = step === "server" ? t3("sync.wizard.serverDescription") : step === "signin" ? t3("sync.wizard.signinDescription", { url: target.serverUrl }) : step === "choose" ? t3("sync.wizard.chooseDescription") : t3("sync.wizard.finishingDescription");
33579
- const content2 = /* @__PURE__ */ jsxs42(Fragment8, { children: [
33580
- step === "server" && /* @__PURE__ */ jsxs42(
33581
- "form",
33582
- {
33583
- className: "flex flex-col gap-4",
33584
- onSubmit: (event) => {
33585
- event.preventDefault();
33586
- void handleCheck();
33587
- },
33588
- children: [
33589
- /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-1.5", children: [
33590
- /* @__PURE__ */ jsx55("label", { className: "text-xs font-semibold", htmlFor: "sync-server-url", children: t3("sync.wizard.serverUrl") }),
33591
- /* @__PURE__ */ jsx55(
33592
- Input,
33593
- {
33594
- id: "sync-server-url",
33595
- autoFocus: true,
33596
- className: "rounded-none",
33597
- placeholder: "https://termix.example.com",
33598
- value: target.serverUrl,
33599
- onChange: (event) => setTarget({ ...target, serverUrl: event.target.value })
33600
- }
33601
- ),
33602
- /* @__PURE__ */ jsx55("p", { className: "text-[10px] text-muted-foreground", children: t3("sync.wizard.serverUrlHint") }),
33603
- !relogin && savedUrls.length > 0 && /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-1 pt-1", children: [
33604
- /* @__PURE__ */ jsx55("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: t3("sync.wizard.savedServers") }),
33605
- savedUrls.map((url) => /* @__PURE__ */ jsxs42(
33606
- "div",
33607
- {
33608
- className: "flex items-center border border-border hover:bg-muted",
33609
- children: [
33610
- /* @__PURE__ */ jsx55(
33611
- "button",
33612
- {
33613
- type: "button",
33614
- className: "flex-1 min-w-0 truncate px-2 py-1.5 text-left font-mono text-xs",
33615
- onClick: () => setTarget({ ...target, serverUrl: url }),
33616
- children: url
33617
- }
33618
- ),
33619
- /* @__PURE__ */ jsx55(
33620
- "button",
33621
- {
33622
- type: "button",
33623
- title: t3("sync.wizard.removeServer"),
33624
- className: "px-2 py-1.5 text-muted-foreground hover:text-destructive",
33625
- onClick: () => setSavedUrls(forgetServerUrl(url)),
33626
- children: /* @__PURE__ */ jsx55(X, { className: "size-3" })
33627
- }
33628
- )
33629
- ]
33630
- },
33631
- url
33632
- ))
33633
- ] })
33634
- ] }),
33635
- problem && /* @__PURE__ */ jsxs42("div", { className: "flex gap-2 border border-destructive/40 bg-destructive/10 p-2.5 text-xs", children: [
33636
- /* @__PURE__ */ jsx55(CircleAlert, { className: "size-4 shrink-0 text-destructive" }),
33637
- /* @__PURE__ */ jsx55("span", { children: problem })
33638
- ] }),
33639
- (problemKind === "tls_untrusted" || target.allowInvalidCertificate) && /* @__PURE__ */ jsxs42("div", { className: "flex items-center justify-between gap-3 border border-border p-2.5", children: [
33640
- /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-0.5", children: [
33641
- /* @__PURE__ */ jsx55("span", { className: "text-xs font-medium", children: t3("sync.proxy.allowInvalidCertificate") }),
33642
- /* @__PURE__ */ jsx55("span", { className: "text-[10px] text-muted-foreground", children: t3("sync.proxy.allowInvalidCertificateHint") })
33643
- ] }),
33644
- /* @__PURE__ */ jsx55(
33645
- Switch2,
33646
- {
33647
- checked: !!target.allowInvalidCertificate,
33648
- onCheckedChange: (checked) => setTarget({ ...target, allowInvalidCertificate: checked })
33649
- }
33650
- )
33651
- ] }),
33652
- /* @__PURE__ */ jsx55(
33653
- "button",
33654
- {
33655
- type: "button",
33656
- className: "self-start text-[10px] font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground",
33657
- onClick: () => setShowAdvanced((value) => !value),
33658
- children: showAdvanced ? t3("sync.wizard.hideProxySettings") : t3("sync.wizard.showProxySettings")
33659
- }
33660
- ),
33661
- showAdvanced && /* @__PURE__ */ jsx55(
33662
- ProxySettingsFields,
33663
- {
33664
- customHeaders: target.customHeaders ?? [],
33665
- basicAuth: target.basicAuth ?? null,
33666
- onChange: (next) => setTarget({ ...target, ...next })
33667
- }
33668
- ),
33669
- /* @__PURE__ */ jsxs42("div", { className: "flex justify-end gap-2", children: [
33670
- /* @__PURE__ */ jsx55(
33671
- Button,
33672
- {
33673
- type: "button",
33674
- variant: "outline",
33675
- className: "rounded-none",
33676
- onClick: () => onOpenChange(false),
33677
- children: t3("common.cancel")
33678
- }
33679
- ),
33680
- /* @__PURE__ */ jsxs42(
33681
- Button,
33682
- {
33683
- variant: "outline",
33684
- type: "submit",
33685
- className: "rounded-none border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand dark:border-accent-brand/40 dark:bg-transparent dark:hover:bg-accent-brand/10",
33686
- disabled: checking || !target.serverUrl.trim(),
33687
- children: [
33688
- checking && /* @__PURE__ */ jsx55(LoaderCircle, { className: "size-4 animate-spin" }),
33689
- t3("sync.wizard.continue")
33690
- ]
33691
- }
33692
- )
33693
- ] })
33694
- ]
33695
- }
33696
- ),
33697
- step === "signin" && /* @__PURE__ */ jsx55("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx55(
33698
- ElectronLoginForm,
33699
- {
33700
- serverUrl: target.serverUrl,
33701
- targetPurpose: "link",
33702
- onAuthSuccess: handleSignedIn,
33703
- onChangeServer: () => relogin ? onOpenChange(false) : setStep("server")
33704
- }
33705
- ) }),
33706
- step === "choose" && /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-3", children: [
33707
- finishError && /* @__PURE__ */ jsxs42("div", { className: "flex gap-2 border border-destructive/40 bg-destructive/10 p-2.5 text-xs", children: [
33708
- /* @__PURE__ */ jsx55(CircleAlert, { className: "size-4 shrink-0 text-destructive" }),
33709
- /* @__PURE__ */ jsx55("span", { children: finishError })
33710
- ] }),
33711
- [
33712
- {
33713
- value: "merge",
33714
- title: t3("sync.wizard.mergeTitle"),
33715
- body: t3("sync.wizard.mergeBody", { count: localTotal })
33716
- },
33717
- {
33718
- value: "replace",
33719
- title: t3("sync.wizard.replaceTitle"),
33720
- body: t3("sync.wizard.replaceBody", { count: localTotal })
33721
- }
33722
- ].map((option) => /* @__PURE__ */ jsxs42(
33723
- "button",
33724
- {
33725
- type: "button",
33726
- onClick: () => setMode(option.value),
33727
- className: `text-left border p-3 flex flex-col gap-1 transition-colors ${mode === option.value ? "border-accent-brand bg-accent-brand/10" : "border-border hover:bg-muted/40"}`,
33728
- children: [
33729
- /* @__PURE__ */ jsx55("span", { className: "text-sm font-semibold", children: option.title }),
33730
- /* @__PURE__ */ jsx55("span", { className: "text-xs text-muted-foreground", children: option.body })
33731
- ]
33732
- },
33733
- option.value
33734
- )),
33735
- /* @__PURE__ */ jsxs42("div", { className: "flex justify-end gap-2", children: [
33736
- /* @__PURE__ */ jsx55(
33737
- Button,
33738
- {
33739
- type: "button",
33740
- variant: "outline",
33741
- className: "rounded-none",
33742
- onClick: () => onOpenChange(false),
33743
- children: t3("common.cancel")
33744
- }
33745
- ),
33746
- /* @__PURE__ */ jsx55(
33747
- Button,
33748
- {
33749
- variant: "outline",
33750
- type: "button",
33751
- className: "rounded-none border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand dark:border-accent-brand/40 dark:bg-transparent dark:hover:bg-accent-brand/10",
33752
- disabled: !token,
33753
- onClick: () => token && void finish(token, mode),
33754
- children: t3("sync.wizard.link")
33755
- }
33756
- )
33757
- ] })
33758
- ] }),
33759
- step === "finishing" && /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-3 py-6 text-sm text-muted-foreground", children: [
33760
- /* @__PURE__ */ jsx55(LoaderCircle, { className: "size-5 animate-spin text-accent-brand" }),
33761
- t3("sync.wizard.linking")
33762
- ] })
33763
- ] });
33764
- return /* @__PURE__ */ jsxs42(
33765
- InlineView,
33766
- {
33767
- open,
33768
- onOpenChange,
33769
- title,
33770
- width: "wide",
33771
- scroll: !wide,
33772
- children: [
33773
- !wide && /* @__PURE__ */ jsxs42("p", { className: "text-xs text-muted-foreground", children: [
33774
- description,
33775
- " ",
33776
- step === "server" && /* @__PURE__ */ jsx55(DocsLink, { core: "desktopSync" })
33777
- ] }),
33778
- content2
33779
- ]
33780
- }
33781
- );
33130
+ }, [url, ignoreCert]);
33131
+ useEffect16(() => {
33132
+ sync.current();
33133
+ }, [hidden]);
33134
+ if (!api) return /* @__PURE__ */ jsx53(Fragment8, { children: fallback ?? null });
33135
+ return /* @__PURE__ */ jsx53("div", { ref: host, className, style });
33782
33136
  }
33783
33137
 
33784
33138
  // src/ui/shell/split/SplitLayoutMenu.tsx
33785
33139
  init_define_import_meta_env();
33786
33140
  import { useMemo as useMemo9 } from "react";
33787
- import { useTranslation as useTranslation27 } from "react-i18next";
33788
- import { jsx as jsx56, jsxs as jsxs43 } from "react/jsx-runtime";
33141
+ import { useTranslation as useTranslation24 } from "react-i18next";
33142
+ import { jsx as jsx54, jsxs as jsxs40 } from "react/jsx-runtime";
33789
33143
  function NodeThumb({ node }) {
33790
33144
  if (node.kind === "pane") {
33791
- return /* @__PURE__ */ jsx56("div", { className: "size-full border border-current" });
33145
+ return /* @__PURE__ */ jsx54("div", { className: "size-full border border-current" });
33792
33146
  }
33793
- return /* @__PURE__ */ jsx56(
33147
+ return /* @__PURE__ */ jsx54(
33794
33148
  "div",
33795
33149
  {
33796
33150
  className: `flex size-full gap-0.5 ${node.direction === "row" ? "flex-row" : "flex-col"}`,
33797
- children: node.children.map((child, index) => /* @__PURE__ */ jsx56(
33151
+ children: node.children.map((child, index) => /* @__PURE__ */ jsx54(
33798
33152
  "div",
33799
33153
  {
33800
33154
  className: "min-w-0 min-h-0",
33801
33155
  style: { flex: `${node.sizes[index]} 1 0px` },
33802
- children: /* @__PURE__ */ jsx56(NodeThumb, { node: child })
33156
+ children: /* @__PURE__ */ jsx54(NodeThumb, { node: child })
33803
33157
  },
33804
33158
  child.id
33805
33159
  ))
@@ -33808,7 +33162,7 @@ function NodeThumb({ node }) {
33808
33162
  }
33809
33163
  function PresetThumb({ id }) {
33810
33164
  const node = useMemo9(() => buildPreset(id), [id]);
33811
- return /* @__PURE__ */ jsx56(NodeThumb, { node });
33165
+ return /* @__PURE__ */ jsx54(NodeThumb, { node });
33812
33166
  }
33813
33167
  function SplitLayoutMenu({
33814
33168
  activeIsSplit,
@@ -33816,10 +33170,10 @@ function SplitLayoutMenu({
33816
33170
  onSplit,
33817
33171
  onPreset
33818
33172
  }) {
33819
- const { t: t3 } = useTranslation27();
33173
+ const { t: t3 } = useTranslation24();
33820
33174
  const fullTitle = canAddPane ? void 0 : t3("splitScreen.maxPanes", { count: MAX_PANES });
33821
- return /* @__PURE__ */ jsxs43(DropdownMenu, { children: [
33822
- /* @__PURE__ */ jsx56(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx56(
33175
+ return /* @__PURE__ */ jsxs40(DropdownMenu, { children: [
33176
+ /* @__PURE__ */ jsx54(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx54(
33823
33177
  Button,
33824
33178
  {
33825
33179
  variant: "ghost",
@@ -33827,65 +33181,65 @@ function SplitLayoutMenu({
33827
33181
  className: `h-full w-12.5 rounded-none ${activeIsSplit ? "text-accent-brand" : "text-muted-foreground hover:text-foreground"}`,
33828
33182
  title: t3("splitScreen.title"),
33829
33183
  "aria-label": t3("splitScreen.title"),
33830
- children: /* @__PURE__ */ jsx56(LayoutPanelLeft, { className: "size-4" })
33184
+ children: /* @__PURE__ */ jsx54(LayoutPanelLeft, { className: "size-4" })
33831
33185
  }
33832
33186
  ) }),
33833
- /* @__PURE__ */ jsxs43(
33187
+ /* @__PURE__ */ jsxs40(
33834
33188
  DropdownMenuContent,
33835
33189
  {
33836
33190
  align: "end",
33837
33191
  sideOffset: 1,
33838
33192
  className: "w-64 border-t-0 [clip-path:inset(0px_-4px_-4px_-4px)] p-1",
33839
33193
  children: [
33840
- /* @__PURE__ */ jsxs43(
33194
+ /* @__PURE__ */ jsxs40(
33841
33195
  DropdownMenuItem,
33842
33196
  {
33843
33197
  disabled: !canAddPane,
33844
33198
  title: fullTitle,
33845
33199
  onSelect: () => onSplit("right"),
33846
33200
  children: [
33847
- /* @__PURE__ */ jsx56(Columns2, { className: "size-3.5" }),
33201
+ /* @__PURE__ */ jsx54(Columns2, { className: "size-3.5" }),
33848
33202
  t3("splitScreen.splitRight"),
33849
- /* @__PURE__ */ jsx56(DropdownMenuShortcut, { children: "Ctrl+Shift+\\" })
33203
+ /* @__PURE__ */ jsx54(DropdownMenuShortcut, { children: "Ctrl+Shift+\\" })
33850
33204
  ]
33851
33205
  }
33852
33206
  ),
33853
- /* @__PURE__ */ jsxs43(
33207
+ /* @__PURE__ */ jsxs40(
33854
33208
  DropdownMenuItem,
33855
33209
  {
33856
33210
  disabled: !canAddPane,
33857
33211
  title: fullTitle,
33858
33212
  onSelect: () => onSplit("bottom"),
33859
33213
  children: [
33860
- /* @__PURE__ */ jsx56(Rows2, { className: "size-3.5" }),
33214
+ /* @__PURE__ */ jsx54(Rows2, { className: "size-3.5" }),
33861
33215
  t3("splitScreen.splitDown"),
33862
- /* @__PURE__ */ jsx56(DropdownMenuShortcut, { children: "Ctrl+Shift+-" })
33216
+ /* @__PURE__ */ jsx54(DropdownMenuShortcut, { children: "Ctrl+Shift+-" })
33863
33217
  ]
33864
33218
  }
33865
33219
  ),
33866
- /* @__PURE__ */ jsx56(DropdownMenuSeparator, {}),
33867
- /* @__PURE__ */ jsx56(DropdownMenuLabel, { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: activeIsSplit ? t3("splitScreen.applyLayout") : t3("splitScreen.layouts") }),
33868
- /* @__PURE__ */ jsx56("div", { className: "grid grid-cols-4 gap-1 p-1", children: SPLIT_PRESETS.map((preset) => /* @__PURE__ */ jsx56(
33220
+ /* @__PURE__ */ jsx54(DropdownMenuSeparator, {}),
33221
+ /* @__PURE__ */ jsx54(DropdownMenuLabel, { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: activeIsSplit ? t3("splitScreen.applyLayout") : t3("splitScreen.layouts") }),
33222
+ /* @__PURE__ */ jsx54("div", { className: "grid grid-cols-4 gap-1 p-1", children: SPLIT_PRESETS.map((preset) => /* @__PURE__ */ jsx54(
33869
33223
  DropdownMenuItem,
33870
33224
  {
33871
33225
  className: "flex h-12 items-center justify-center p-1.5 text-muted-foreground focus:text-accent-brand",
33872
33226
  title: t3(preset.titleKey),
33873
33227
  "aria-label": t3(preset.titleKey),
33874
33228
  onSelect: () => onPreset(preset.id),
33875
- children: /* @__PURE__ */ jsx56("div", { className: "h-8 w-11", children: /* @__PURE__ */ jsx56(PresetThumb, { id: preset.id }) })
33229
+ children: /* @__PURE__ */ jsx54("div", { className: "h-8 w-11", children: /* @__PURE__ */ jsx54(PresetThumb, { id: preset.id }) })
33876
33230
  },
33877
33231
  preset.id
33878
33232
  )) }),
33879
- /* @__PURE__ */ jsx56(DropdownMenuSeparator, {}),
33880
- /* @__PURE__ */ jsx56(DropdownMenuLabel, { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: t3("splitScreen.shortcuts") }),
33881
- /* @__PURE__ */ jsxs43("div", { className: "flex flex-col gap-1 px-2 pb-1.5 text-xs text-muted-foreground", children: [
33882
- /* @__PURE__ */ jsxs43("div", { className: "flex items-center justify-between gap-2", children: [
33883
- /* @__PURE__ */ jsx56("span", { children: t3("splitScreen.shortcutNavigate") }),
33884
- /* @__PURE__ */ jsx56("kbd", { className: "font-mono text-[10px]", children: paneNavHotkeyLabel() })
33233
+ /* @__PURE__ */ jsx54(DropdownMenuSeparator, {}),
33234
+ /* @__PURE__ */ jsx54(DropdownMenuLabel, { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: t3("splitScreen.shortcuts") }),
33235
+ /* @__PURE__ */ jsxs40("div", { className: "flex flex-col gap-1 px-2 pb-1.5 text-xs text-muted-foreground", children: [
33236
+ /* @__PURE__ */ jsxs40("div", { className: "flex items-center justify-between gap-2", children: [
33237
+ /* @__PURE__ */ jsx54("span", { children: t3("splitScreen.shortcutNavigate") }),
33238
+ /* @__PURE__ */ jsx54("kbd", { className: "font-mono text-[10px]", children: paneNavHotkeyLabel() })
33885
33239
  ] }),
33886
- /* @__PURE__ */ jsxs43("div", { className: "flex items-center justify-between gap-2", children: [
33887
- /* @__PURE__ */ jsx56("span", { children: t3("splitScreen.zoom") }),
33888
- /* @__PURE__ */ jsx56("kbd", { className: "font-mono text-[10px]", children: "Ctrl+Shift+Enter" })
33240
+ /* @__PURE__ */ jsxs40("div", { className: "flex items-center justify-between gap-2", children: [
33241
+ /* @__PURE__ */ jsx54("span", { children: t3("splitScreen.zoom") }),
33242
+ /* @__PURE__ */ jsx54("kbd", { className: "font-mono text-[10px]", children: "Ctrl+Shift+Enter" })
33889
33243
  ] })
33890
33244
  ] })
33891
33245
  ]
@@ -33896,23 +33250,27 @@ function SplitLayoutMenu({
33896
33250
 
33897
33251
  // src/ui/sidebar/AuthEnrollmentSections.tsx
33898
33252
  init_define_import_meta_env();
33899
- import { useTranslation as useTranslation28 } from "react-i18next";
33900
- import { jsx as jsx57, jsxs as jsxs44 } from "react/jsx-runtime";
33901
- function AuthEnrollmentSections() {
33902
- const { t: t3 } = useTranslation28();
33903
- const sections = [...useLoginMethods(), ...useSecondFactors()].filter(
33253
+ import { useTranslation as useTranslation25 } from "react-i18next";
33254
+ import { jsx as jsx55, jsxs as jsxs41 } from "react/jsx-runtime";
33255
+ function AuthEnrollmentSections({
33256
+ factorsOnly = false
33257
+ } = {}) {
33258
+ const { t: t3 } = useTranslation25();
33259
+ const methods = useLoginMethods();
33260
+ const factors = useSecondFactors();
33261
+ const sections = [...factorsOnly ? [] : methods, ...factors].filter(
33904
33262
  (entry) => entry.enrollment
33905
33263
  );
33906
33264
  if (sections.length === 0) return null;
33907
- return /* @__PURE__ */ jsx57("div", { className: "flex flex-col gap-4", children: sections.map((entry) => {
33265
+ return /* @__PURE__ */ jsx55("div", { className: "flex flex-col gap-4", children: sections.map((entry) => {
33908
33266
  const Enrollment = entry.enrollment;
33909
- return /* @__PURE__ */ jsxs44(
33267
+ return /* @__PURE__ */ jsxs41(
33910
33268
  "div",
33911
33269
  {
33912
33270
  className: "flex flex-col gap-2",
33913
33271
  children: [
33914
- /* @__PURE__ */ jsx57("span", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: t3(entry.titleKey) }),
33915
- /* @__PURE__ */ jsx57(Enrollment, {})
33272
+ /* @__PURE__ */ jsx55("span", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: t3(entry.titleKey) }),
33273
+ /* @__PURE__ */ jsx55(Enrollment, {})
33916
33274
  ]
33917
33275
  },
33918
33276
  `${entry.pluginId}:${entry.id}`
@@ -34027,7 +33385,6 @@ export {
34027
33385
  LANGUAGES,
34028
33386
  Label,
34029
33387
  LineChart,
34030
- LinkServerDialog,
34031
33388
  ListBadge,
34032
33389
  ListRow,
34033
33390
  ListRowAction,
@@ -34040,6 +33397,7 @@ export {
34040
33397
  Meter,
34041
33398
  MetricCard,
34042
33399
  MiniStat,
33400
+ NativeView,
34043
33401
  NumberField,
34044
33402
  OPEN_PLUGINS_EVENT,
34045
33403
  PANEL,
@@ -34061,7 +33419,6 @@ export {
34061
33419
  PopoverAnchor,
34062
33420
  PopoverContent,
34063
33421
  PopoverTrigger,
34064
- ProxySettingsFields,
34065
33422
  RadialGauge,
34066
33423
  Repeater,
34067
33424
  RobustClipboardProvider,
@@ -34205,8 +33562,10 @@ export {
34205
33562
  getHostDefaultsLevel,
34206
33563
  getHostEditorSection,
34207
33564
  getHostPassword,
33565
+ getHostPolicy,
34208
33566
  getHostSidebarPreferences,
34209
33567
  getHostTags,
33568
+ getHttpsSettings,
34210
33569
  getLiveHostStatus,
34211
33570
  getLogLevel,
34212
33571
  getNotificationPrivateEndpoints,
@@ -34232,8 +33591,6 @@ export {
34232
33591
  getSessions,
34233
33592
  getStatusCheckSettings,
34234
33593
  getStatusClasses,
34235
- getSyncConflicts,
34236
- getSyncErrors,
34237
33594
  getTabType,
34238
33595
  getTlsStatus,
34239
33596
  getUpdateChannel,
@@ -34298,7 +33655,6 @@ export {
34298
33655
  railItemLabel,
34299
33656
  readFromClipboard,
34300
33657
  recordHostActionPreference,
34301
- refreshSyncStatus,
34302
33658
  registerLayout,
34303
33659
  registerTabCloseGuard,
34304
33660
  releaseUrlFrom,
@@ -34321,7 +33677,6 @@ export {
34321
33677
  resolveHostDefaults,
34322
33678
  resolveHostTabType,
34323
33679
  resolveRemoteHostId,
34324
- retrySyncErrors,
34325
33680
  revokeAllUserSessions,
34326
33681
  revokeCredentialAccess,
34327
33682
  revokeFolderAccess,
@@ -34345,25 +33700,22 @@ export {
34345
33700
  setHostAuthOverride,
34346
33701
  setNotificationPrivateEndpoints,
34347
33702
  setPluginOptions,
34348
- setSyncStatus,
34349
33703
  setUpdateChannel,
34350
- settleSyncConflict,
34351
33704
  shareCredential,
34352
33705
  shareFolder,
34353
33706
  shareHost,
34354
33707
  shell,
34355
33708
  splitDefaultKey,
34356
33709
  sshHostToHost,
34357
- syncNow,
34358
33710
  tabIcon,
34359
33711
  tabJumpHotkeyKeys,
34360
33712
  takePendingManageRequest,
34361
33713
  unlinkExternalFromPasswordAccount,
34362
- unlinkServer,
34363
33714
  updateCredential,
34364
33715
  updateExternalAutoProvision,
34365
33716
  updateFolderMetadata,
34366
33717
  updateHostAccess,
33718
+ updateHttpsSettings,
34367
33719
  updateLogLevel,
34368
33720
  updatePasswordLoginAllowed,
34369
33721
  updatePasswordResetAllowed,
@@ -34376,7 +33728,6 @@ export {
34376
33728
  updateSecondFactorAfterExternalLogin,
34377
33729
  updateSessionTimeout,
34378
33730
  updateStatusCheckSettings,
34379
- updateSyncSettings,
34380
33731
  uploadTlsCertificate,
34381
33732
  usageBarColor,
34382
33733
  usageColor,
@@ -34389,6 +33740,7 @@ export {
34389
33740
  useConfirm,
34390
33741
  useConnectionGate,
34391
33742
  useConnectionLog,
33743
+ useDesktopLinked,
34392
33744
  useDesktopUpdate,
34393
33745
  useEditorDefaultKeys,
34394
33746
  useHostActions,
@@ -34423,7 +33775,6 @@ export {
34423
33775
  useSshAuthProviders,
34424
33776
  useStatusColorScheme,
34425
33777
  useSurfaceKind,
34426
- useSyncStatus,
34427
33778
  useTabHandlesVersion,
34428
33779
  useTabs,
34429
33780
  useTabsSafe,