ds-harness-remote 0.3.21 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -1771,6 +1771,20 @@ Minimum version required to store current data is: ` + bestVersion + `.
1771
1771
  openWorkspace: "Open workspace",
1772
1772
  openingWorkspace: "Opening\u2026",
1773
1773
  loadingDirectory: "Loading directories\u2026",
1774
+ remoteProgressCheckingHost: "Checking Host",
1775
+ remoteProgressCheckingHostDetail: "Finding the selected device and checking whether it is online.",
1776
+ remoteProgressAuthorizingPeer: "Verifying authorization",
1777
+ remoteProgressAuthorizingPeerDetail: "Confirming account membership and pinned Host identity.",
1778
+ remoteProgressOpeningChannel: "Opening encrypted channel",
1779
+ remoteProgressOpeningChannelDetail: "Trying LAN, P2P, TURN, then Relay if needed.",
1780
+ remoteProgressLoadingWorkspaces: "Loading workspaces",
1781
+ remoteProgressLoadingWorkspacesDetail: "Reading the remote Harness workspace list through the tunnel.",
1782
+ remoteProgressOpeningWorkspace: "Opening workspace",
1783
+ remoteProgressOpeningWorkspaceDetail: "Asking the Host to prepare the selected working directory.",
1784
+ remoteProgressSwitchingWorkspace: "Switching interface",
1785
+ remoteProgressSwitchingWorkspaceDetail: "Handing the remote workspace to the local Harness UI.",
1786
+ remoteProgressReady: "Ready",
1787
+ remoteProgressReadyDetail: "The remote Host is connected and encrypted.",
1774
1788
  backToHosts: "Choose another Host",
1775
1789
  currentDirectory: "Selected directory",
1776
1790
  directoryTruncated: "Only part of this directory could be shown.",
@@ -1840,10 +1854,14 @@ Minimum version required to store current data is: ` + bestVersion + `.
1840
1854
  startSignIn: "Start sign-in",
1841
1855
  allowControlCurrentDevice: "Allow control of this device",
1842
1856
  exitRemoteAccount: "Sign out",
1843
- scanWithZhihu: "Scan to sign in",
1857
+ githubLogin: "GitHub QR",
1858
+ zhihuLogin: "Zhihu QR",
1859
+ scanWithGitHub: "Scan to continue with GitHub",
1860
+ scanWithZhihu: "Scan to continue with Zhihu",
1861
+ openInBrowser: "Continue in browser",
1844
1862
  scanLoginHint: "Authorize on your phone. This window will continue automatically.",
1845
1863
  currentServiceAddress: "Current service address:",
1846
- accountPasswordLogin: "Account and password",
1864
+ accountPasswordLogin: "Password",
1847
1865
  qrLoginExpired: "This QR code expired. Refresh it to continue.",
1848
1866
  refreshQrCode: "Refresh QR code"
1849
1867
  }, zh = {
@@ -1937,6 +1955,20 @@ Minimum version required to store current data is: ` + bestVersion + `.
1937
1955
  openWorkspace: "\u6253\u5F00\u5DE5\u4F5C\u533A",
1938
1956
  openingWorkspace: "\u6B63\u5728\u6253\u5F00\u2026",
1939
1957
  loadingDirectory: "\u6B63\u5728\u52A0\u8F7D\u76EE\u5F55\u2026",
1958
+ remoteProgressCheckingHost: "\u6B63\u5728\u68C0\u67E5 Host",
1959
+ remoteProgressCheckingHostDetail: "\u6B63\u5728\u67E5\u627E\u6240\u9009\u8BBE\u5907\u5E76\u786E\u8BA4\u662F\u5426\u5728\u7EBF\u3002",
1960
+ remoteProgressAuthorizingPeer: "\u6B63\u5728\u9A8C\u8BC1\u6388\u6743",
1961
+ remoteProgressAuthorizingPeerDetail: "\u6B63\u5728\u786E\u8BA4\u8D26\u53F7\u6210\u5458\u5173\u7CFB\u548C\u5DF2\u56FA\u5B9A\u7684 Host \u8EAB\u4EFD\u3002",
1962
+ remoteProgressOpeningChannel: "\u6B63\u5728\u5EFA\u7ACB\u52A0\u5BC6\u901A\u9053",
1963
+ remoteProgressOpeningChannelDetail: "\u4F9D\u6B21\u5C1D\u8BD5\u5C40\u57DF\u7F51\u3001P2P\u3001TURN\uFF0C\u5FC5\u8981\u65F6\u56DE\u843D\u5230 Relay\u3002",
1964
+ remoteProgressLoadingWorkspaces: "\u6B63\u5728\u52A0\u8F7D\u5DE5\u4F5C\u533A",
1965
+ remoteProgressLoadingWorkspacesDetail: "\u901A\u8FC7\u96A7\u9053\u8BFB\u53D6\u8FDC\u7AEF Harness \u5DE5\u4F5C\u533A\u5217\u8868\u3002",
1966
+ remoteProgressOpeningWorkspace: "\u6B63\u5728\u6253\u5F00\u5DE5\u4F5C\u533A",
1967
+ remoteProgressOpeningWorkspaceDetail: "\u6B63\u5728\u8BF7\u6C42 Host \u51C6\u5907\u6240\u9009\u5DE5\u4F5C\u76EE\u5F55\u3002",
1968
+ remoteProgressSwitchingWorkspace: "\u6B63\u5728\u5207\u6362\u754C\u9762",
1969
+ remoteProgressSwitchingWorkspaceDetail: "\u6B63\u5728\u628A\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u4EA4\u7ED9\u672C\u5730 Harness UI\u3002",
1970
+ remoteProgressReady: "\u5DF2\u5C31\u7EEA",
1971
+ remoteProgressReadyDetail: "\u8FDC\u7AEF Host \u5DF2\u8FDE\u63A5\uFF0C\u7AEF\u5230\u7AEF\u52A0\u5BC6\u5DF2\u5EFA\u7ACB\u3002",
1940
1972
  backToHosts: "\u9009\u62E9\u5176\u4ED6\u4E3B\u673A",
1941
1973
  currentDirectory: "\u5DF2\u9009\u76EE\u5F55",
1942
1974
  directoryTruncated: "\u76EE\u5F55\u5185\u5BB9\u8F83\u591A\uFF0C\u76EE\u524D\u53EA\u663E\u793A\u4E86\u4E00\u90E8\u5206\u3002",
@@ -2006,7 +2038,11 @@ Minimum version required to store current data is: ` + bestVersion + `.
2006
2038
  startSignIn: "\u5F00\u59CB\u767B\u5F55",
2007
2039
  allowControlCurrentDevice: "\u5141\u8BB8\u63A7\u5236\u5F53\u524D\u8BBE\u5907",
2008
2040
  exitRemoteAccount: "\u9000\u51FA",
2009
- scanWithZhihu: "\u626B\u7801\u767B\u5F55",
2041
+ githubLogin: "GitHub \u626B\u7801",
2042
+ zhihuLogin: "\u77E5\u4E4E\u626B\u7801",
2043
+ scanWithGitHub: "\u4F7F\u7528 GitHub \u626B\u7801\u767B\u5F55",
2044
+ scanWithZhihu: "\u4F7F\u7528\u77E5\u4E4E\u626B\u7801\u767B\u5F55",
2045
+ openInBrowser: "\u5728\u6D4F\u89C8\u5668\u4E2D\u7EE7\u7EED",
2010
2046
  scanLoginHint: "\u8BF7\u5728\u624B\u673A\u4E0A\u5B8C\u6210\u6388\u6743\uFF0C\u6B64\u7A97\u53E3\u4F1A\u81EA\u52A8\u7EE7\u7EED\u3002",
2011
2047
  currentServiceAddress: "\u5F53\u524D\u670D\u52A1\u5730\u5740\uFF1A",
2012
2048
  accountPasswordLogin: "\u8D26\u53F7\u5BC6\u7801",
@@ -2046,6 +2082,58 @@ Minimum version required to store current data is: ` + bestVersion + `.
2046
2082
  id: clientModuleId,
2047
2083
  factory: (require2) => {
2048
2084
  let module = { exports: {} }, React = require2("react"), inject = ["connection", "slots", "locale", "workspaces", "sessions"];
2085
+ function RemoteProgressView(props) {
2086
+ let progress = props.progress;
2087
+ if (progress === void 0) return null;
2088
+ let percent = Math.max(0, Math.min(100, Math.round(progress.percent)));
2089
+ return React.createElement(
2090
+ "div",
2091
+ {
2092
+ className: "dshRemoteProgress",
2093
+ role: "status",
2094
+ "aria-live": "polite"
2095
+ },
2096
+ React.createElement(
2097
+ "div",
2098
+ { className: "dshRemoteProgressHeader" },
2099
+ React.createElement("strong", null, props.t(progress.label)),
2100
+ React.createElement("span", null, `${percent}%`)
2101
+ ),
2102
+ React.createElement("div", {
2103
+ className: "dshRemoteProgressBar",
2104
+ role: "progressbar",
2105
+ "aria-valuemin": 0,
2106
+ "aria-valuemax": 100,
2107
+ "aria-valuenow": percent,
2108
+ "aria-label": props.t(progress.label)
2109
+ }, React.createElement("span", { style: { width: `${percent}%` } })),
2110
+ React.createElement("p", null, props.t(progress.detail))
2111
+ );
2112
+ }
2113
+ async function runRemoteProgress(steps, setProgress, progressRun, action) {
2114
+ let runId = progressRun.current + 1;
2115
+ progressRun.current = runId;
2116
+ let apply2 = (next) => {
2117
+ progressRun.current === runId && setProgress(next);
2118
+ }, [first, ...rest] = steps;
2119
+ first !== void 0 && apply2(first);
2120
+ let timers = rest.map((step) => window.setTimeout(() => apply2(step), step.delayMs ?? 0));
2121
+ try {
2122
+ let result = await action();
2123
+ return apply2({ label: "remoteProgressReady", detail: "remoteProgressReadyDetail", percent: 100 }), await new Promise((resolve) => window.setTimeout(resolve, 220)), result;
2124
+ } finally {
2125
+ timers.forEach((timer) => window.clearTimeout(timer)), progressRun.current === runId && setProgress(void 0);
2126
+ }
2127
+ }
2128
+ let connectHostProgressSteps = [
2129
+ { label: "remoteProgressCheckingHost", detail: "remoteProgressCheckingHostDetail", percent: 12 },
2130
+ { label: "remoteProgressAuthorizingPeer", detail: "remoteProgressAuthorizingPeerDetail", percent: 32, delayMs: 280 },
2131
+ { label: "remoteProgressOpeningChannel", detail: "remoteProgressOpeningChannelDetail", percent: 58, delayMs: 760 },
2132
+ { label: "remoteProgressLoadingWorkspaces", detail: "remoteProgressLoadingWorkspacesDetail", percent: 82, delayMs: 1350 }
2133
+ ], openWorkspaceProgressSteps = [
2134
+ { label: "remoteProgressOpeningWorkspace", detail: "remoteProgressOpeningWorkspaceDetail", percent: 30 },
2135
+ { label: "remoteProgressSwitchingWorkspace", detail: "remoteProgressSwitchingWorkspaceDetail", percent: 74, delayMs: 520 }
2136
+ ];
2049
2137
  function RemotePluginOptions(props) {
2050
2138
  let { t } = props, [open, setOpen] = React.useState(!1), [serverUrl, setServerUrl] = React.useState(""), role = "host", [registrationCode, setRegistrationCode] = React.useState(""), [associations, setAssociations] = React.useState({}), [loaded, setLoaded] = React.useState(!1), [writable, setWritable] = React.useState(!1), [busy, setBusy] = React.useState(!1), [reconnectBusy, setReconnectBusy] = React.useState(!1), [hostStatus, setHostStatus] = React.useState(void 0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0), [settingsView, setSettingsView] = React.useState(void 0), persistedServerUrl = settingsView?.config.serverUrl ?? "https://dsh.r2049.cn", association = associations.client ?? associations.host, serverDirty = settingsView !== void 0 && serverUrl !== persistedServerUrl, draftDirty = serverDirty, applyView = (view) => {
2051
2139
  setSettingsView(view), setServerUrl(view.config.serverUrl ?? "https://dsh.r2049.cn"), setAssociations(view.associations ?? (view.association === void 0 ? {} : { host: view.association })), setWritable(view.writable), setLoaded(!0);
@@ -2274,7 +2362,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2274
2362
  );
2275
2363
  }
2276
2364
  function RemoteWorkspaceAction(props) {
2277
- let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [loginMethod, setLoginMethod] = React.useState("qr"), [qrSession, setQrSession] = React.useState(void 0), [qrImage, setQrImage] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0);
2365
+ let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [loginMethod, setLoginMethod] = React.useState(props.preferredQrProvider), [loginMethodManuallySelected, setLoginMethodManuallySelected] = React.useState(!1), [qrSession, setQrSession] = React.useState(void 0), [qrImage, setQrImage] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0);
2278
2366
  React.useEffect(() => {
2279
2367
  if (!open) return;
2280
2368
  let closeOnEscape = (event) => {
@@ -2290,10 +2378,10 @@ Minimum version required to store current data is: ` + bestVersion + `.
2290
2378
  remoteActive && document.documentElement.classList.remove("dshRemoteTargetActive");
2291
2379
  };
2292
2380
  }, [status?.mode]);
2293
- let startQrLogin = async () => {
2381
+ let startQrLogin = async (provider) => {
2294
2382
  setBusy(!0), setError(void 0), setQrExpired(!1);
2295
2383
  try {
2296
- let session = await props.control("client.account.qr.start"), image = await import_qrcode.default.toDataURL(session.scanUrl, {
2384
+ let session = await props.control("client.account.qr.start", { provider }), image = await import_qrcode.default.toDataURL(session.scanUrl, {
2297
2385
  width: 184,
2298
2386
  margin: 1,
2299
2387
  errorCorrectionLevel: "L"
@@ -2306,9 +2394,9 @@ Minimum version required to store current data is: ` + bestVersion + `.
2306
2394
  }
2307
2395
  };
2308
2396
  React.useEffect(() => {
2309
- !open || !needsAuthorization || loginMethod !== "qr" || qrSession !== void 0 || qrExpired || startQrLogin();
2397
+ !open || !needsAuthorization || loginMethod === "password" || qrSession !== void 0 || qrExpired || startQrLogin(loginMethod);
2310
2398
  }, [open, needsAuthorization, loginMethod, qrSession, qrExpired]), React.useEffect(() => {
2311
- if (!open || loginMethod !== "qr" || qrSession === void 0) return;
2399
+ if (!open || loginMethod === "password" || qrSession === void 0) return;
2312
2400
  let active = !0, poll = () => {
2313
2401
  props.control("client.account.qr.poll", { qrId: qrSession.qrId }).then(async (result) => {
2314
2402
  active && (result.status === "complete" ? (setDevices(await props.control("devices")), setStatus(await props.control("status")), setNeedsAuthorization(!1), setQrSession(void 0), setQrImage(void 0)) : result.status === "expired" && (setQrExpired(!0), setQrSession(void 0), setQrImage(void 0)));
@@ -2321,11 +2409,30 @@ Minimum version required to store current data is: ` + bestVersion + `.
2321
2409
  return () => {
2322
2410
  active = !1, window.clearInterval(timer);
2323
2411
  };
2324
- }, [open, loginMethod, qrSession]);
2325
- let selectHost = async (host) => {
2412
+ }, [open, loginMethod, qrSession]), React.useEffect(() => {
2413
+ loginMethodManuallySelected || loginMethod === props.preferredQrProvider || (setLoginMethod(props.preferredQrProvider), setQrSession(void 0), setQrImage(void 0), setQrExpired(!1), setError(void 0));
2414
+ }, [props.preferredQrProvider, loginMethodManuallySelected]);
2415
+ let selectLoginMethod = (method) => {
2416
+ setLoginMethodManuallySelected(!0), method !== loginMethod && (setLoginMethod(method), setQrSession(void 0), setQrImage(void 0), setQrExpired(!1), setError(void 0));
2417
+ }, orderedQrProviders = props.preferredQrProvider === "zhihu" ? ["zhihu", "github"] : ["github", "zhihu"], qrLoginTab = (provider) => React.createElement("button", {
2418
+ key: provider,
2419
+ type: "button",
2420
+ role: "tab",
2421
+ id: `dsh-remote-${provider}-tab`,
2422
+ "aria-selected": loginMethod === provider,
2423
+ "aria-controls": `dsh-remote-${provider}-panel`,
2424
+ className: loginMethod === provider ? "isActive" : "",
2425
+ disabled: busy,
2426
+ onClick: () => selectLoginMethod(provider)
2427
+ }, t(provider === "github" ? "githubLogin" : "zhihuLogin")), selectHost = async (host) => {
2326
2428
  setBusy(!0), setError(void 0);
2327
2429
  try {
2328
- setWorkspaces(await props.control("workspaces.list", { targetDeviceId: host.deviceId })), setSelectedHost(host), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
2430
+ setWorkspaces(await runRemoteProgress(
2431
+ connectHostProgressSteps,
2432
+ setProgress,
2433
+ progressRun,
2434
+ () => props.control("workspaces.list", { targetDeviceId: host.deviceId })
2435
+ )), setSelectedHost(host), setPath(""), setAddingWorkspace(!1), setDirectory(void 0);
2329
2436
  } catch (reason) {
2330
2437
  setError(messageOf(reason));
2331
2438
  } finally {
@@ -2401,10 +2508,15 @@ Minimum version required to store current data is: ` + bestVersion + `.
2401
2508
  if (!(selectedHost === void 0 || path.trim() === "")) {
2402
2509
  setBusy(!0), setError(void 0);
2403
2510
  try {
2404
- await props.control("workspace.open", {
2405
- targetDeviceId: selectedHost.deviceId,
2406
- path: path.trim()
2407
- }), window.location.reload();
2511
+ await runRemoteProgress(
2512
+ openWorkspaceProgressSteps,
2513
+ setProgress,
2514
+ progressRun,
2515
+ () => props.control("workspace.open", {
2516
+ targetDeviceId: selectedHost.deviceId,
2517
+ path: path.trim()
2518
+ })
2519
+ ), window.location.reload();
2408
2520
  } catch (reason) {
2409
2521
  setError(messageOf(reason)), setBusy(!1);
2410
2522
  }
@@ -2487,18 +2599,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2487
2599
  React.createElement(
2488
2600
  "div",
2489
2601
  { className: "dshRemoteLoginTabs", role: "tablist" },
2490
- React.createElement("button", {
2491
- type: "button",
2492
- role: "tab",
2493
- id: "dsh-remote-qr-tab",
2494
- "aria-selected": loginMethod === "qr",
2495
- "aria-controls": "dsh-remote-qr-panel",
2496
- className: loginMethod === "qr" ? "isActive" : "",
2497
- disabled: busy,
2498
- onClick: () => {
2499
- setLoginMethod("qr"), setError(void 0);
2500
- }
2501
- }, t("scanWithZhihu")),
2602
+ ...orderedQrProviders.map(qrLoginTab),
2502
2603
  React.createElement("button", {
2503
2604
  type: "button",
2504
2605
  role: "tab",
@@ -2507,25 +2608,39 @@ Minimum version required to store current data is: ` + bestVersion + `.
2507
2608
  "aria-controls": "dsh-remote-password-panel",
2508
2609
  className: loginMethod === "password" ? "isActive" : "",
2509
2610
  disabled: busy,
2510
- onClick: () => {
2511
- setLoginMethod("password"), setError(void 0);
2512
- }
2611
+ onClick: () => selectLoginMethod("password")
2513
2612
  }, t("accountPasswordLogin"))
2514
2613
  ),
2515
- loginMethod === "qr" ? React.createElement(
2614
+ loginMethod !== "password" ? React.createElement(
2516
2615
  "div",
2517
2616
  {
2518
2617
  className: "dshRemoteQrLogin",
2519
2618
  role: "tabpanel",
2520
- id: "dsh-remote-qr-panel",
2521
- "aria-labelledby": "dsh-remote-qr-tab"
2619
+ id: `dsh-remote-${loginMethod}-panel`,
2620
+ "aria-labelledby": `dsh-remote-${loginMethod}-tab`
2522
2621
  },
2523
2622
  qrImage === void 0 ? React.createElement(
2524
2623
  "div",
2525
2624
  { className: "dshRemoteQrPlaceholder", "aria-busy": busy },
2526
2625
  qrExpired ? React.createElement("p", null, t("qrLoginExpired")) : React.createElement("span", null, t("checkingConnection"))
2527
- ) : React.createElement("img", { src: qrImage, width: 184, height: 184, alt: t("scanWithZhihu") }),
2528
- React.createElement("strong", null, t("scanWithZhihu")),
2626
+ ) : qrSession !== void 0 ? React.createElement(
2627
+ "a",
2628
+ {
2629
+ className: "dshRemoteQrOpen",
2630
+ href: qrSession.scanUrl,
2631
+ target: "_blank",
2632
+ rel: "noopener noreferrer",
2633
+ "aria-label": t("openInBrowser")
2634
+ },
2635
+ React.createElement("img", {
2636
+ src: qrImage,
2637
+ width: 184,
2638
+ height: 184,
2639
+ alt: t(loginMethod === "github" ? "scanWithGitHub" : "scanWithZhihu")
2640
+ }),
2641
+ React.createElement("span", null, t("openInBrowser"), " \u2197")
2642
+ ) : null,
2643
+ React.createElement("strong", null, t(loginMethod === "github" ? "scanWithGitHub" : "scanWithZhihu")),
2529
2644
  React.createElement("p", null, t("scanLoginHint")),
2530
2645
  status?.serverUrl === void 0 ? null : React.createElement(
2531
2646
  "p",
@@ -2638,6 +2753,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2638
2753
  React.createElement("small", null, [formatPlatform(selectedHost.platform), t("online")].join(" \xB7 "))
2639
2754
  )
2640
2755
  ),
2756
+ React.createElement(RemoteProgressView, { progress, t }),
2641
2757
  selectedHost === void 0 ? React.createElement("p", { className: "dshRemoteHint" }, t("selectHostHint")) : React.createElement(
2642
2758
  "section",
2643
2759
  { className: "dshRemoteBrowser", "aria-label": t("chooseDirectory") },
@@ -2717,7 +2833,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2717
2833
  );
2718
2834
  }
2719
2835
  function RemoteModeAction(props) {
2720
- let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [hostRegistrationCode, setHostRegistrationCode] = React.useState(""), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [busy, setBusy] = React.useState(!1), [error, setError] = React.useState(void 0), [supported, setSupported] = React.useState(!0), refresh = async () => {
2836
+ let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [hostRegistrationCode, setHostRegistrationCode] = React.useState(""), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [busy, setBusy] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), [error, setError] = React.useState(void 0), [supported, setSupported] = React.useState(!0), refresh = async () => {
2721
2837
  let [nextStatus, nextDevices] = await Promise.all([
2722
2838
  props.control("status"),
2723
2839
  props.control("devices").catch(() => [])
@@ -2741,7 +2857,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
2741
2857
  let switchMode = async (mode, targetDeviceId) => {
2742
2858
  setBusy(!0), setError(void 0);
2743
2859
  try {
2744
- await props.control("mode.set", { mode, ...targetDeviceId === void 0 ? {} : { targetDeviceId } }), window.location.reload();
2860
+ let action = () => props.control("mode.set", { mode, ...targetDeviceId === void 0 ? {} : { targetDeviceId } });
2861
+ mode === "remote" ? await runRemoteProgress(connectHostProgressSteps, setProgress, progressRun, action) : await action(), window.location.reload();
2745
2862
  } catch (reason) {
2746
2863
  setError(messageOf(reason)), setBusy(!1);
2747
2864
  }
@@ -2806,6 +2923,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2806
2923
  disabled: busy || !device.online || status?.target?.deviceId === device.deviceId,
2807
2924
  onClick: () => void switchMode("remote", device.deviceId)
2808
2925
  }, `${device.name} \xB7 ${t(device.online ? "online" : "offline")}`))),
2926
+ React.createElement(RemoteProgressView, { progress, t }),
2809
2927
  status?.hostAuthorizationAvailable && status.host !== void 0 ? React.createElement(
2810
2928
  "div",
2811
2929
  { className: "dshRemoteHostAccount" },
@@ -3051,6 +3169,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
3051
3169
  ".dshRemoteSectionHeading>.dshRemoteAddWorkspace{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:50%;font-size:20px;line-height:1}.dshRemoteSectionHeading>.dshRemoteAddWorkspace:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
3052
3170
  ".dshRemoteHostList{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.dshRemoteHostList>button{min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:16px;text-align:left;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:10px 4px;cursor:pointer}.dshRemoteHostList>button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteHostList>button:disabled{opacity:.5;cursor:default}.dshRemoteHostList>button>span{min-width:0;display:flex;flex-direction:column;gap:3px}.dshRemoteHostList>button strong{font-size:14px;font-weight:500}.dshRemoteHostList small,.dshRemoteSelectedHost small{color:var(--dsw-alias-label-secondary);font-size:12px}",
3053
3171
  ".dshRemoteSelectedHost{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 14px;border-radius:10px;background:var(--dsw-alias-bg-layer-2)}",
3172
+ ".dshRemoteProgress{display:flex;flex-direction:column;gap:8px;margin:12px 0;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2)}.dshRemoteProgressHeader{display:flex;align-items:center;justify-content:space-between;gap:12px}.dshRemoteProgressHeader strong{font-size:13px;font-weight:600}.dshRemoteProgressHeader span{color:var(--dsw-alias-label-secondary);font-size:12px}.dshRemoteProgressBar{height:6px;overflow:hidden;border-radius:999px;background:var(--dsw-alias-bg-layer-3)}.dshRemoteProgressBar>span{display:block;height:100%;border-radius:inherit;background:var(--dsw-alias-brand-primary);transition:width .22s ease-out}.dshRemoteProgress p{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45}@media(prefers-reduced-motion:reduce){.dshRemoteProgressBar>span{transition:none}}",
3054
3173
  '.dshRemoteBrowser{display:flex;flex-direction:column}.dshRemoteCrumbs{display:flex;align-items:center;gap:4px;overflow:auto;padding:2px 0 10px}.dshRemoteCrumbs>button{flex:0 0 auto;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:5px 7px;border-radius:6px;cursor:pointer}.dshRemoteCrumbs>button:not(:last-child)::after{content:" /";color:var(--dsw-alias-label-tertiary)}.dshRemoteCrumbs>button:disabled{color:var(--dsw-alias-label-primary);font-weight:600}',
3055
3174
  ".dshRemoteDirectoryList{min-height:72px;display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.dshRemoteDirectoryList>button{min-height:52px;display:grid;grid-template-columns:auto 1fr;column-gap:10px;text-align:left;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:8px 4px;cursor:pointer}.dshRemoteDirectoryList>button:hover,.dshRemoteDirectoryList>button.isSelected{background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteDirectoryList>button.isSelected{color:var(--dsw-alias-label-primary)}.dshRemoteDirectoryList>button>span:first-child{grid-row:1/3}.dshRemoteDirectoryList>button>small{grid-column:2;color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis}.dshRemoteDirectoryList>p,.dshRemoteHint{margin:12px 0;color:var(--dsw-alias-label-secondary);font-size:13px}",
3056
3175
  ".dshRemoteFolderBrowser{margin-top:14px}.dshRemoteFolderBrowser>p,.dshRemoteFolderList>p{margin:12px 0;color:var(--dsw-alias-label-secondary);font-size:13px}.dshRemoteFolderList{max-height:260px;overflow:auto;border-block:1px solid var(--dsw-alias-border-l2)}.dshRemoteFolderList>button{width:100%;min-height:42px;display:flex;align-items:center;gap:9px;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:7px 6px;text-align:left;cursor:pointer}.dshRemoteFolderList>button:hover{background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteFolderBrowser>small{display:block;margin-top:8px;color:var(--dsw-alias-state-warn-label)}",
@@ -3059,8 +3178,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
3059
3178
  ".dshRemoteEnable{max-width:600px;display:flex;flex-direction:column;align-items:flex-start;gap:10px}.dshRemoteEnable p{margin:0;color:var(--dsw-alias-label-secondary);line-height:1.5}",
3060
3179
  '.dshRemoteLoginTabs{width:min(440px,100%);display:flex;border-bottom:1px solid var(--dsw-alias-border-l2)}.dshRemoteLoginTabs>button{position:relative;min-height:38px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:6px 14px;font:inherit;font-size:13px;cursor:pointer}.dshRemoteLoginTabs>button:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.dshRemoteLoginTabs>button.isActive{color:var(--dsw-alias-label-primary);font-weight:600}.dshRemoteLoginTabs>button.isActive::after{content:"";position:absolute;right:12px;bottom:-1px;left:12px;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-brand-primary)}.dshRemoteLoginTabs>button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px;border-radius:6px}',
3061
3180
  ".dshRemoteClientLogin{width:min(440px,100%);display:flex;flex-direction:column;gap:8px}.dshRemoteClientLogin input{min-height:40px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:inherit;padding:0 12px;font:inherit}.dshRemoteClientLogin button{align-self:flex-start;min-height:40px;border:0;border-radius:8px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-1);padding:8px 16px;cursor:pointer}",
3062
- ".dshRemoteQrLogin{width:min(440px,100%);display:flex;flex-direction:column;align-items:center;gap:8px;padding:6px 0 2px;text-align:center}.dshRemoteQrLogin img,.dshRemoteQrPlaceholder{box-sizing:border-box;width:200px;height:200px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:#fff;padding:8px}.dshRemoteQrPlaceholder{display:flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-secondary)}.dshRemoteQrLogin>strong{font-size:14px}.dshRemoteQrLogin>p{max-width:48ch;margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5}.dshRemoteQrLogin>.dshRemoteServiceAddress{margin-top:2px;color:var(--dsw-alias-label-tertiary)}.dshRemoteServiceAddress>a{color:var(--dsw-alias-label-secondary);text-decoration:none}.dshRemoteServiceAddress>a:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteQrLogin>button,.dshRemoteClientLogin>.dshRemoteLoginSwitch{min-height:32px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:4px 8px;font:inherit;font-size:12px;cursor:pointer}.dshRemoteQrLogin>button:hover,.dshRemoteClientLogin>.dshRemoteLoginSwitch:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteClientLogin>.dshRemoteLoginSwitch{align-self:flex-start;background:transparent;color:var(--dsw-alias-label-secondary);padding-left:0}",
3063
- ".dshRemoteEnable{box-sizing:border-box;width:100%;min-height:388px}.dshRemoteLoginHeading{max-width:100%;display:flex;align-items:baseline;gap:10px;overflow:hidden;white-space:nowrap}.dshRemoteLoginTitle{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dshRemoteLoginHeading>span{flex:0 0 auto;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:400}.dshRemoteLoginTabs{width:100%}.dshRemoteLoginTabs>button{flex:1}.dshRemoteLoginTabs>button.isActive::after{right:0;left:0;border-radius:0}.dshRemoteClientLogin,.dshRemoteQrLogin{box-sizing:border-box;width:min(440px,100%);height:300px;min-height:300px;align-self:center}.dshRemoteClientLogin{align-items:stretch;padding-top:16px}.dshRemoteClientLogin>button{align-self:stretch;width:100%}.dshRemoteQrLogin{padding-top:12px}",
3181
+ ".dshRemoteQrLogin{width:min(440px,100%);display:flex;flex-direction:column;align-items:center;gap:8px;padding:6px 0 2px;text-align:center}.dshRemoteQrLogin img,.dshRemoteQrPlaceholder{box-sizing:border-box;width:200px;height:200px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:#fff;padding:8px}.dshRemoteQrOpen{display:flex;flex-direction:column;align-items:center;gap:5px;color:var(--dsw-alias-label-secondary);font-size:12px;text-decoration:none}.dshRemoteQrOpen:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteQrOpen:hover img{border-color:var(--dsw-alias-label-dimmed)}.dshRemoteQrOpen:focus-visible{border-radius:12px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.dshRemoteQrPlaceholder{display:flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-secondary)}.dshRemoteQrLogin>strong{font-size:14px}.dshRemoteQrLogin>p{max-width:48ch;margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5}.dshRemoteQrLogin>.dshRemoteServiceAddress{margin-top:2px;color:var(--dsw-alias-label-tertiary)}.dshRemoteServiceAddress>a{color:var(--dsw-alias-label-secondary);text-decoration:none}.dshRemoteServiceAddress>a:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteQrLogin>button,.dshRemoteClientLogin>.dshRemoteLoginSwitch{min-height:32px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:4px 8px;font:inherit;font-size:12px;cursor:pointer}.dshRemoteQrLogin>button:hover,.dshRemoteClientLogin>.dshRemoteLoginSwitch:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteClientLogin>.dshRemoteLoginSwitch{align-self:flex-start;background:transparent;color:var(--dsw-alias-label-secondary);padding-left:0}",
3182
+ ".dshRemoteEnable{box-sizing:border-box;width:100%;min-height:388px}.dshRemoteLoginHeading{max-width:100%;display:flex;align-items:baseline;gap:10px;overflow:hidden;white-space:nowrap}.dshRemoteLoginTitle{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dshRemoteLoginHeading>span{flex:0 0 auto;color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:400}.dshRemoteLoginTabs{width:100%}.dshRemoteLoginTabs>button{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dshRemoteLoginTabs>button.isActive::after{right:0;left:0;border-radius:0}.dshRemoteClientLogin,.dshRemoteQrLogin{box-sizing:border-box;width:min(440px,100%);height:300px;min-height:300px;align-self:center}.dshRemoteClientLogin{align-items:stretch;padding-top:16px}.dshRemoteClientLogin>button{align-self:stretch;width:100%}.dshRemoteQrLogin{padding-top:12px}",
3064
3183
  '.dshRemoteHostControlToggle{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:12px;white-space:nowrap;cursor:default}.dshRemoteHostControlToggle>input{appearance:none;position:relative;width:38px;height:22px;flex:0 0 auto;margin:0;border:1px solid var(--dsw-alias-label-secondary);border-radius:999px;background:var(--dsw-alias-bg-layer-3);cursor:pointer;box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2);transition:background .16s ease-out,border-color .16s ease-out,box-shadow .16s ease-out}.dshRemoteHostControlToggle>input::after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-secondary);transition:transform .16s ease-out,background .16s ease-out}.dshRemoteHostControlToggle>input:checked{border-color:var(--dsw-alias-state-success-primary);background:var(--dsw-alias-state-success-primary);box-shadow:none}.dshRemoteHostControlToggle>input:checked::after{transform:translateX(16px);background:var(--dsw-alias-bg-layer-1)}.dshRemoteHostControlToggle>input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.dshRemoteHostControlToggle>input:disabled{opacity:.5;cursor:default}@media(prefers-reduced-motion:reduce){.dshRemoteHostControlToggle>input,.dshRemoteHostControlToggle>input::after{transition:none}}',
3065
3184
  ".dshRemoteAccountExit{flex:0 0 auto}.dshRemoteAccountExit:disabled{opacity:.5;cursor:default;text-decoration:none}",
3066
3185
  ".dshRemoteLocalLink{align-self:flex-start;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:4px 0;cursor:pointer}.dshRemoteLocalLink:hover{color:var(--dsw-alias-label-primary)}",
@@ -3151,7 +3270,10 @@ Minimum version required to store current data is: ` + bestVersion + `.
3151
3270
  id: "dsh-remote-workspace",
3152
3271
  order: -20,
3153
3272
  locale: localeNamespace,
3154
- inject: () => ({ control })
3273
+ inject: () => ({
3274
+ control,
3275
+ preferredQrProvider: ctx.locale.getLocale().active === "zh" ? "zhihu" : "github"
3276
+ })
3155
3277
  }, RemoteWorkspaceAction)), ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
3156
3278
  name: "settings.plugin.item",
3157
3279
  key: "dsh-remote",