dsh-zen-remote 2.0.2 → 2.2.0
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/AGENTS.md +15 -1
- package/README.md +11 -5
- package/README.zh-CN.md +11 -5
- package/docs/adr/0001-plugin-bridge-forwards-plugin-routes.md +15 -0
- package/docs/adr/0002-relay-emulates-websocket.md +8 -0
- package/docs/interface.md +3 -1
- package/docs/plugin-bridge.md +140 -0
- package/docs/remote-access.en.md +57 -0
- package/docs/remote-access.md +63 -0
- package/lib/adapters/dsh-better-sidebar/index.js +144 -0
- package/lib/adapters/dsh-better-sidebar/index.js.map +1 -0
- package/lib/adapters/dsh-better-sidebar/methods.js +198 -0
- package/lib/adapters/dsh-better-sidebar/methods.js.map +1 -0
- package/lib/adapters/index.js +17 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/types.js +17 -0
- package/lib/adapters/types.js.map +1 -0
- package/lib/admin-routes.js +51 -0
- package/lib/admin-routes.js.map +1 -1
- package/lib/client-routes.js +18 -1
- package/lib/client-routes.js.map +1 -1
- package/lib/client.js +449 -45
- package/lib/config.js +27 -0
- package/lib/config.js.map +1 -1
- package/lib/http.js +10 -0
- package/lib/http.js.map +1 -1
- package/lib/index.js +246 -0
- package/lib/index.js.map +1 -1
- package/lib/intercept.js +5 -2
- package/lib/intercept.js.map +1 -1
- package/lib/plugin-adapter-core.js +385 -0
- package/lib/plugin-adapter-core.js.map +1 -0
- package/lib/plugin-bridge.js +318 -0
- package/lib/plugin-bridge.js.map +1 -0
- package/lib/plugin-loopback.js +207 -0
- package/lib/plugin-loopback.js.map +1 -0
- package/lib/plugin-route-intercept.js +1103 -0
- package/lib/plugin-route-intercept.js.map +1 -0
- package/lib/plugin-tunnel.js +416 -0
- package/lib/plugin-tunnel.js.map +1 -0
- package/lib/relay-client.js +340 -4
- package/lib/relay-client.js.map +1 -1
- package/lib/relay-server.js +665 -7
- package/lib/relay-server.js.map +1 -1
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts +27 -0
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -0
- package/lib/types/adapters/dsh-better-sidebar/methods.d.ts +51 -0
- package/lib/types/adapters/dsh-better-sidebar/methods.d.ts.map +1 -0
- package/lib/types/adapters/index.d.ts +17 -0
- package/lib/types/adapters/index.d.ts.map +1 -0
- package/lib/types/adapters/types.d.ts +42 -0
- package/lib/types/adapters/types.d.ts.map +1 -0
- package/lib/types/admin-routes.d.ts +16 -0
- package/lib/types/admin-routes.d.ts.map +1 -1
- package/lib/types/client/effects/remote-session.d.ts +0 -22
- package/lib/types/client/effects/remote-session.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +16 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/settings/SettingsSection.d.ts.map +1 -1
- package/lib/types/client/styles/remote-session.css.d.ts +1 -1
- package/lib/types/client/styles/remote-session.css.d.ts.map +1 -1
- package/lib/types/client-data/plugin-bridge.d.ts +79 -0
- package/lib/types/client-data/plugin-bridge.d.ts.map +1 -0
- package/lib/types/client-data/remote-status.d.ts +20 -0
- package/lib/types/client-data/remote-status.d.ts.map +1 -1
- package/lib/types/client-data/settings-form.d.ts +37 -1
- package/lib/types/client-data/settings-form.d.ts.map +1 -1
- package/lib/types/client-routes.d.ts +13 -0
- package/lib/types/client-routes.d.ts.map +1 -1
- package/lib/types/config.d.ts +8 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/http.d.ts +7 -0
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/intercept.d.ts.map +1 -1
- package/lib/types/plugin-adapter-core.d.ts +263 -0
- package/lib/types/plugin-adapter-core.d.ts.map +1 -0
- package/lib/types/plugin-bridge.d.ts +187 -0
- package/lib/types/plugin-bridge.d.ts.map +1 -0
- package/lib/types/plugin-loopback.d.ts +104 -0
- package/lib/types/plugin-loopback.d.ts.map +1 -0
- package/lib/types/plugin-route-intercept.d.ts +114 -0
- package/lib/types/plugin-route-intercept.d.ts.map +1 -0
- package/lib/types/plugin-tunnel.d.ts +96 -0
- package/lib/types/plugin-tunnel.d.ts.map +1 -0
- package/lib/types/relay-client.d.ts +81 -1
- package/lib/types/relay-client.d.ts.map +1 -1
- package/lib/types/relay-server.d.ts +32 -1
- package/lib/types/relay-server.d.ts.map +1 -1
- package/llms.txt +1 -0
- package/package.json +2 -2
- package/scripts/check-plugin-bridge-ui.mjs +168 -0
- package/scripts/check-settings-form.mjs +3 -1
- package/src/adapters/dsh-better-sidebar/index.ts +144 -0
- package/src/adapters/dsh-better-sidebar/methods.ts +215 -0
- package/src/adapters/index.ts +19 -0
- package/src/adapters/types.ts +44 -0
- package/src/admin-routes.ts +64 -0
- package/src/client/effects/remote-session.ts +82 -10
- package/src/client/locales.ts +36 -0
- package/src/client/settings/SettingsSection.tsx +107 -3
- package/src/client/styles/remote-session.css.ts +70 -31
- package/src/client-data/plugin-bridge.ts +150 -0
- package/src/client-data/remote-status.ts +30 -0
- package/src/client-data/settings-form.ts +78 -0
- package/src/client-routes.ts +29 -1
- package/src/config.ts +32 -0
- package/src/http.ts +12 -0
- package/src/index.ts +252 -0
- package/src/intercept.ts +5 -2
- package/src/plugin-adapter-core.ts +530 -0
- package/src/plugin-bridge.ts +435 -0
- package/src/plugin-loopback.ts +299 -0
- package/src/plugin-route-intercept.ts +1193 -0
- package/src/plugin-tunnel.ts +470 -0
- package/src/relay-client.ts +413 -4
- package/src/relay-server.ts +687 -9
package/lib/client.js
CHANGED
|
@@ -1850,6 +1850,123 @@ exports.RESTART_FIELDS = [
|
|
|
1850
1850
|
'pushTool',
|
|
1851
1851
|
];
|
|
1852
1852
|
};
|
|
1853
|
+
__modules["client-data/plugin-bridge.js"] = function (require, module, exports) {
|
|
1854
|
+
"use strict";
|
|
1855
|
+
/**
|
|
1856
|
+
* The plugin bridge's settings-page pure pieces (PB-5): the status → copy
|
|
1857
|
+
* mapping the 子客户端诊断区 renders, and the consent-toggle arithmetic the
|
|
1858
|
+
* 主服务端插件桥 section stages. Pure by the settings-form discipline — no
|
|
1859
|
+
* fetch, no clock, no context — so scripts/check-plugin-bridge-ui.mjs pins
|
|
1860
|
+
* every copy and every toggle shape.
|
|
1861
|
+
*
|
|
1862
|
+
* Deliberately import-free: this file rides the BROWSER bundle, and the
|
|
1863
|
+
* bridge's own module (src/plugin-bridge.ts) drags node:crypto in. The
|
|
1864
|
+
* status shape is therefore restated structurally here — it mirrors
|
|
1865
|
+
* lib/plugin-bridge.js's `BridgeStatus` verbatim, and a drifted wire is
|
|
1866
|
+
* handled by the tolerant mapping, not by the type system.
|
|
1867
|
+
*/
|
|
1868
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1869
|
+
exports.derivePluginBridgeStatuses = derivePluginBridgeStatuses;
|
|
1870
|
+
exports.bridgeSectionState = bridgeSectionState;
|
|
1871
|
+
exports.togglePluginConsent = togglePluginConsent;
|
|
1872
|
+
exports.readAdminBridgeRow = readAdminBridgeRow;
|
|
1873
|
+
/** The reason → copy-key table, one entry per BridgeReason plus the
|
|
1874
|
+
* everything-else fallback (an unknown reason from a newer server reads as
|
|
1875
|
+
* 未桥接-generic — the offline copy, honest without inventing a claim). */
|
|
1876
|
+
const REASON_KEYS = {
|
|
1877
|
+
'not-on-server': 'settings.bridge.stateNotOnServer',
|
|
1878
|
+
'version-mismatch': 'settings.bridge.stateVersionMismatch',
|
|
1879
|
+
'declaration-mismatch': 'settings.bridge.stateDeclarationMismatch',
|
|
1880
|
+
'consent-off': 'settings.bridge.stateConsentOff',
|
|
1881
|
+
offline: 'settings.bridge.stateOffline',
|
|
1882
|
+
'server-too-old': 'settings.bridge.stateServerTooOld',
|
|
1883
|
+
};
|
|
1884
|
+
/** Tolerant read of one `pluginBridge` wire array into the render rows,
|
|
1885
|
+
* input order kept (the route already sorts by plugin name). A malformed
|
|
1886
|
+
* entry is skipped, never thrown into the settings page. */
|
|
1887
|
+
function derivePluginBridgeStatuses(value) {
|
|
1888
|
+
if (!Array.isArray(value))
|
|
1889
|
+
return [];
|
|
1890
|
+
const rows = [];
|
|
1891
|
+
for (const entry of value) {
|
|
1892
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
|
|
1893
|
+
continue;
|
|
1894
|
+
const record = entry;
|
|
1895
|
+
if (typeof record.plugin !== 'string' || record.plugin === '')
|
|
1896
|
+
continue;
|
|
1897
|
+
const bridged = record.bridged === true;
|
|
1898
|
+
const reason = typeof record.reason === 'string' ? record.reason : undefined;
|
|
1899
|
+
rows.push({
|
|
1900
|
+
plugin: record.plugin,
|
|
1901
|
+
localVersion: typeof record.localVersion === 'string' ? record.localVersion : '',
|
|
1902
|
+
stateKey: bridged
|
|
1903
|
+
? 'settings.bridge.stateBridged'
|
|
1904
|
+
: reason !== undefined && REASON_KEYS[reason] !== undefined
|
|
1905
|
+
? REASON_KEYS[reason]
|
|
1906
|
+
: 'settings.bridge.stateOffline',
|
|
1907
|
+
serverVersion: typeof record.serverVersion === 'string' ? record.serverVersion : undefined,
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
return rows;
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* The three-state resolution for the host settings page's 插件桥 list
|
|
1914
|
+
* (PB-9-fix): a fetch that has not answered is LOADING, a failed or
|
|
1915
|
+
* refused fetch is an ERROR — and only a successful answer with zero rows
|
|
1916
|
+
* means "no plugin has registered a bridge". Collapsing the first two into
|
|
1917
|
+
* the empty copy would tell the user nothing is bridged while the answer is
|
|
1918
|
+
* simply not in yet (or the page was opened through the gateway, where the
|
|
1919
|
+
* listing is refused). Pure so the check pins all four outcomes.
|
|
1920
|
+
*/
|
|
1921
|
+
function bridgeSectionState(load, rows) {
|
|
1922
|
+
if (load === 'loading')
|
|
1923
|
+
return 'loading';
|
|
1924
|
+
if (load === 'error')
|
|
1925
|
+
return 'error';
|
|
1926
|
+
return Array.isArray(rows) && rows.length > 0 ? 'plugins' : 'empty';
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* One consent-toggle click: the NEXT whole-machine-data consent map, staged
|
|
1930
|
+
* over the row's `pluginBridgeConsent` field. Only `plugin`'s key moves —
|
|
1931
|
+
* every other key (including entries for plugins no longer registered; the
|
|
1932
|
+
* settings page deliberately never prunes what it does not render) is
|
|
1933
|
+
* carried over verbatim, in its existing key order. An illegal current map
|
|
1934
|
+
* (a hand-edited row) reads as the empty map: the toggle then writes exactly
|
|
1935
|
+
* one key, and the junk is left for the row validator to keep refusing.
|
|
1936
|
+
*/
|
|
1937
|
+
function togglePluginConsent(current, plugin, allow) {
|
|
1938
|
+
const next = {};
|
|
1939
|
+
if (current !== null && typeof current === 'object' && !Array.isArray(current)) {
|
|
1940
|
+
for (const [key, value] of Object.entries(current)) {
|
|
1941
|
+
if (typeof value === 'boolean')
|
|
1942
|
+
next[key] = value;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
next[plugin] = allow;
|
|
1946
|
+
return next;
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Tolerant read of one admin listing row into the render shape (PB-11).
|
|
1950
|
+
* A malformed row is `undefined`, never a throw — the settings page skips
|
|
1951
|
+
* it the same way {@link derivePluginBridgeStatuses} skips a bad verdict.
|
|
1952
|
+
* `adapted` defaults to false: an older server's answer simply reads as
|
|
1953
|
+
* "nothing is adapted".
|
|
1954
|
+
*/
|
|
1955
|
+
function readAdminBridgeRow(row) {
|
|
1956
|
+
if (row === null || typeof row !== 'object' || Array.isArray(row))
|
|
1957
|
+
return undefined;
|
|
1958
|
+
const record = row;
|
|
1959
|
+
if (typeof record.plugin !== 'string' || record.plugin === '')
|
|
1960
|
+
return undefined;
|
|
1961
|
+
return {
|
|
1962
|
+
plugin: record.plugin,
|
|
1963
|
+
version: typeof record.version === 'string' ? record.version : '',
|
|
1964
|
+
hasServerScope: record.hasServerScope === true,
|
|
1965
|
+
consent: record.consent === true,
|
|
1966
|
+
adapted: record.adapted === true,
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1853
1970
|
__modules["client-data/settings-form.js"] = function (require, module, exports) {
|
|
1854
1971
|
"use strict";
|
|
1855
1972
|
/**
|
|
@@ -1879,7 +1996,7 @@ __modules["client-data/settings-form.js"] = function (require, module, exports)
|
|
|
1879
1996
|
* (devices / pairing) plus the T14 envelope (ok / gateway / config / viaGateway).
|
|
1880
1997
|
*/
|
|
1881
1998
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1882
|
-
exports.ZenRemoteSettingsForm = exports.SETTINGS_FIELDS = exports.IDLE_HOURS_MAX = exports.SERVER_NAME_MAX = exports.RESTART_FIELDS = exports.DEVICE_TOKEN_FIELD = exports.CLIENT_CONFIG_ROUTE = exports.CLIENT_RECONNECT_ROUTE = exports.CLIENT_STATUS_ROUTE = exports.CLIENT_CLAIM_ROUTE = exports.ADMIN_PUSH_TEST_ROUTE = exports.ADMIN_ACTION_ROUTE = exports.ADMIN_PAIR_ROUTE = exports.ADMIN_STATUS_ROUTE = void 0;
|
|
1999
|
+
exports.ZenRemoteSettingsForm = exports.SETTINGS_FIELDS = exports.IDLE_HOURS_MAX = exports.SERVER_NAME_MAX = exports.RESTART_FIELDS = exports.DEVICE_TOKEN_FIELD = exports.CLIENT_CONFIG_ROUTE = exports.CLIENT_RECONNECT_ROUTE = exports.CLIENT_STATUS_ROUTE = exports.CLIENT_CLAIM_ROUTE = exports.ADMIN_PLUGIN_BRIDGE_ROUTE = exports.ADMIN_PUSH_TEST_ROUTE = exports.ADMIN_ACTION_ROUTE = exports.ADMIN_PAIR_ROUTE = exports.ADMIN_STATUS_ROUTE = void 0;
|
|
1883
2000
|
exports.deriveClientStatusView = deriveClientStatusView;
|
|
1884
2001
|
exports.clientStatusLineOf = clientStatusLineOf;
|
|
1885
2002
|
exports.normalizePairingCode = normalizePairingCode;
|
|
@@ -1895,14 +2012,18 @@ exports.nameField = nameField;
|
|
|
1895
2012
|
exports.claimDeviceNameOf = claimDeviceNameOf;
|
|
1896
2013
|
exports.oneOfField = oneOfField;
|
|
1897
2014
|
exports.boolField = boolField;
|
|
2015
|
+
exports.consentField = consentField;
|
|
1898
2016
|
exports.localOpsAllowed = localOpsAllowed;
|
|
1899
2017
|
exports.deriveSettingsView = deriveSettingsView;
|
|
1900
2018
|
const restart_fields_ts_1 = require("./restart-fields.js");
|
|
2019
|
+
const plugin_bridge_ts_1 = require("./client-data/plugin-bridge.js");
|
|
1901
2020
|
/** Same-origin admin routes the settings block talks to (host half: T14). */
|
|
1902
2021
|
exports.ADMIN_STATUS_ROUTE = '/_dsh/zen-remote/admin/status';
|
|
1903
2022
|
exports.ADMIN_PAIR_ROUTE = '/_dsh/zen-remote/admin/pair';
|
|
1904
2023
|
exports.ADMIN_ACTION_ROUTE = '/_dsh/zen-remote/admin/action';
|
|
1905
2024
|
exports.ADMIN_PUSH_TEST_ROUTE = '/_dsh/zen-remote/admin/push-test';
|
|
2025
|
+
/** PB-5: the plugin bridge's registered-plugin listing (GET, server-local only). */
|
|
2026
|
+
exports.ADMIN_PLUGIN_BRIDGE_ROUTE = '/_dsh/zen-remote/admin/plugin-bridge';
|
|
1906
2027
|
/** Same-origin client routes the sub-client block talks to (host half: T16). */
|
|
1907
2028
|
exports.CLIENT_CLAIM_ROUTE = '/_dsh/zen-remote/client/claim';
|
|
1908
2029
|
exports.CLIENT_STATUS_ROUTE = '/_dsh/zen-remote/client/status';
|
|
@@ -1972,6 +2093,18 @@ function deriveInterceptView(value) {
|
|
|
1972
2093
|
recentFailures: failureListOf(record.recentFailures),
|
|
1973
2094
|
};
|
|
1974
2095
|
}
|
|
2096
|
+
/** PB-4 route-interception block, presence-gated the same way: only the
|
|
2097
|
+
* install flag and the shape reasons (the 本机改道未启用 line) — the
|
|
2098
|
+
* counters belong to the status surface, not this copy. */
|
|
2099
|
+
function derivePluginRouteInterceptView(value) {
|
|
2100
|
+
if (value === undefined || value === null)
|
|
2101
|
+
return undefined;
|
|
2102
|
+
const record = asRecord(value);
|
|
2103
|
+
return {
|
|
2104
|
+
installed: record.installed === true,
|
|
2105
|
+
reasons: stringListOf(record.reasons, 20),
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
1975
2108
|
/** T42 compat block, only when the body carried the field at all. The wire
|
|
1976
2109
|
* names are the status route's (`different` / `incompatibleCalls`); the view
|
|
1977
2110
|
* keeps the render-side names the block was written against. */
|
|
@@ -2016,6 +2149,8 @@ function deriveClientStatusView(body) {
|
|
|
2016
2149
|
deviceName: typeof safe.deviceName === 'string' ? safe.deviceName : '',
|
|
2017
2150
|
intercept: deriveInterceptView(safe.intercept),
|
|
2018
2151
|
compat: deriveCompatView(safe.compat),
|
|
2152
|
+
pluginBridge: (0, plugin_bridge_ts_1.derivePluginBridgeStatuses)(safe.pluginBridge),
|
|
2153
|
+
pluginRouteIntercept: derivePluginRouteInterceptView(safe.pluginRouteIntercept),
|
|
2019
2154
|
};
|
|
2020
2155
|
}
|
|
2021
2156
|
function clientStatusLineOf(view, now) {
|
|
@@ -2215,6 +2350,51 @@ function boolField(field) {
|
|
|
2215
2350
|
parse: (text) => (text === 'true' || text === 'false' ? { kind: 'set', value: text === 'true' } : undefined),
|
|
2216
2351
|
};
|
|
2217
2352
|
}
|
|
2353
|
+
/**
|
|
2354
|
+
* `pluginBridgeConsent` (PB-5): a name → boolean map staged as its canonical
|
|
2355
|
+
* JSON draft. Nothing renders it as text — the 插件桥 toggles stage the NEXT
|
|
2356
|
+
* map wholesale (`togglePluginConsent` builds it, one key per click) — but
|
|
2357
|
+
* riding the shared spec table is what puts the field on the SAME save path
|
|
2358
|
+
* as `autoShareNewSessions`: one staged revision, one save button, one
|
|
2359
|
+
* atomic mutation. `format` keeps the value's own key order so a staged
|
|
2360
|
+
* draft equal to the displayed value reads as "no change"; an illegal stored
|
|
2361
|
+
* map formats as `{}` (the toggle then writes from scratch, and the junk
|
|
2362
|
+
* stays refused by the row validator, never laundered through).
|
|
2363
|
+
*/
|
|
2364
|
+
function consentField(field) {
|
|
2365
|
+
const format = (value) => {
|
|
2366
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
2367
|
+
return '{}';
|
|
2368
|
+
const out = {};
|
|
2369
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2370
|
+
if (typeof entry === 'boolean')
|
|
2371
|
+
out[key] = entry;
|
|
2372
|
+
}
|
|
2373
|
+
return JSON.stringify(out);
|
|
2374
|
+
};
|
|
2375
|
+
return {
|
|
2376
|
+
field,
|
|
2377
|
+
format,
|
|
2378
|
+
parse: (text) => {
|
|
2379
|
+
let parsed;
|
|
2380
|
+
try {
|
|
2381
|
+
parsed = JSON.parse(text);
|
|
2382
|
+
}
|
|
2383
|
+
catch {
|
|
2384
|
+
return undefined;
|
|
2385
|
+
}
|
|
2386
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
2387
|
+
return undefined;
|
|
2388
|
+
const out = {};
|
|
2389
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
2390
|
+
if (typeof entry !== 'boolean')
|
|
2391
|
+
return undefined;
|
|
2392
|
+
out[key] = entry;
|
|
2393
|
+
}
|
|
2394
|
+
return { kind: 'set', value: out };
|
|
2395
|
+
},
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2218
2398
|
/** Longest legal `serverName`, mirroring src/config.ts's cap. */
|
|
2219
2399
|
exports.SERVER_NAME_MAX = 40;
|
|
2220
2400
|
/** Upper bound of `idleHours`, mirroring src/config.ts. */
|
|
@@ -2235,6 +2415,7 @@ exports.SETTINGS_FIELDS = [
|
|
|
2235
2415
|
nameField('serverName', exports.SERVER_NAME_MAX),
|
|
2236
2416
|
hoursField('idleHours', exports.IDLE_HOURS_MAX),
|
|
2237
2417
|
boolField('autoShareNewSessions'),
|
|
2418
|
+
consentField('pluginBridgeConsent'),
|
|
2238
2419
|
];
|
|
2239
2420
|
// --- the status -> view mapping ---------------------------------------------
|
|
2240
2421
|
/**
|
|
@@ -3038,6 +3219,22 @@ exports.zh = {
|
|
|
3038
3219
|
'settings.client.diagRecentFailures': '最近的远程调用失败',
|
|
3039
3220
|
'settings.client.diagCompatGroups': '指纹有差异的组:{groups}',
|
|
3040
3221
|
'settings.client.diagCompatCalls': '最近的不兼容调用',
|
|
3222
|
+
// --- PB-5 插件桥:主服务端同意小节 + 子客户端诊断小节 ---
|
|
3223
|
+
'settings.bridge.title': '插件桥',
|
|
3224
|
+
'settings.bridge.empty': '暂无插件登记插件桥',
|
|
3225
|
+
'settings.bridge.loading': '正在读取插件桥登记…',
|
|
3226
|
+
'settings.bridge.loadFailed': '插件桥列表读取失败(旧版服务端或经网关打开的页面)',
|
|
3227
|
+
'settings.bridge.sessionOnly': '随会话共享转发',
|
|
3228
|
+
'settings.bridge.consentToggle': '允许转发整机数据',
|
|
3229
|
+
'settings.bridge.consentHint': '打开后,已配对的桌面应用端可在远程会话里读取该插件的整机数据。',
|
|
3230
|
+
'settings.bridge.stateBridged': '已桥接',
|
|
3231
|
+
'settings.bridge.stateNotOnServer': '服务端未安装',
|
|
3232
|
+
'settings.bridge.stateVersionMismatch': '版本不同(服务端 {version})',
|
|
3233
|
+
'settings.bridge.stateDeclarationMismatch': '声明不一致',
|
|
3234
|
+
'settings.bridge.stateConsentOff': '服务端主人未允许整机数据',
|
|
3235
|
+
'settings.bridge.stateOffline': '中继离线',
|
|
3236
|
+
'settings.bridge.stateServerTooOld': '服务端 zen-remote 版本过旧',
|
|
3237
|
+
'settings.bridge.interceptOff': '本机改道未启用:{reason}',
|
|
3041
3238
|
// --- T34 子客户端远程状态:标题行图标、横幅、关闭远程 ---
|
|
3042
3239
|
'remoteStatusOnline': '远程会话 · 已连接',
|
|
3043
3240
|
'remoteStatusOffline': '服务端离线,正在重连',
|
|
@@ -3054,6 +3251,9 @@ exports.zh = {
|
|
|
3054
3251
|
'remoteStatusOfflineClick': '服务端离线,无法操作',
|
|
3055
3252
|
'remoteStatusUnshareFail': '关闭远程失败,请稍后再试',
|
|
3056
3253
|
'remoteStatusUnshareFailCode': '关闭远程失败({code}),请稍后再试',
|
|
3254
|
+
// --- PB-14: the unbridged better-sidebar note (a CSS variable the
|
|
3255
|
+
// remote-session stylesheet spends as the empty tab's ::before copy) ---
|
|
3256
|
+
'remoteBridgeUnbridgedNote': 'better-sidebar 未在远程会话桥接(服务端未装同版本或未允许),文件与改动请以会话内消息为准',
|
|
3057
3257
|
// 远程分组标题的后缀(后台 merge-streams 拿不到界面语言,固定用中文);
|
|
3058
3258
|
// 这里登记英文对照,供界面侧需要复述同样状态时使用。
|
|
3059
3259
|
'remoteGroupOffline': '(离线)',
|
|
@@ -3308,6 +3508,22 @@ exports.en = {
|
|
|
3308
3508
|
'settings.client.diagRecentFailures': 'Recent remote call failures',
|
|
3309
3509
|
'settings.client.diagCompatGroups': 'Groups with fingerprint differences: {groups}',
|
|
3310
3510
|
'settings.client.diagCompatCalls': 'Recent incompatible calls',
|
|
3511
|
+
// --- PB-5 plugin bridge: host consent section + sub-client diagnostics ---
|
|
3512
|
+
'settings.bridge.title': 'Plugin bridge',
|
|
3513
|
+
'settings.bridge.empty': 'No plugin has registered a bridge yet',
|
|
3514
|
+
'settings.bridge.loading': 'Loading the bridge registrations…',
|
|
3515
|
+
'settings.bridge.loadFailed': 'The bridge listing failed to load (an older server, or the page opened through the gateway)',
|
|
3516
|
+
'settings.bridge.sessionOnly': 'Forwards with shared sessions',
|
|
3517
|
+
'settings.bridge.consentToggle': 'Allow forwarding whole-machine data',
|
|
3518
|
+
'settings.bridge.consentHint': 'When on, paired desktop apps can read this plugin\u2019s whole-machine data in remote sessions.',
|
|
3519
|
+
'settings.bridge.stateBridged': 'Bridged',
|
|
3520
|
+
'settings.bridge.stateNotOnServer': 'Not installed on the server',
|
|
3521
|
+
'settings.bridge.stateVersionMismatch': 'Version differs (server {version})',
|
|
3522
|
+
'settings.bridge.stateDeclarationMismatch': 'Declaration mismatch',
|
|
3523
|
+
'settings.bridge.stateConsentOff': 'Server owner has not allowed whole-machine data',
|
|
3524
|
+
'settings.bridge.stateOffline': 'Relay offline',
|
|
3525
|
+
'settings.bridge.stateServerTooOld': 'Server zen-remote is too old',
|
|
3526
|
+
'settings.bridge.interceptOff': 'Local rerouting not enabled: {reason}',
|
|
3311
3527
|
// --- T34 remote-status icon, composer banner, unshare ---
|
|
3312
3528
|
'remoteStatusOnline': 'Remote session · connected',
|
|
3313
3529
|
'remoteStatusOffline': 'Server offline — reconnecting',
|
|
@@ -3324,6 +3540,7 @@ exports.en = {
|
|
|
3324
3540
|
'remoteStatusOfflineClick': 'The server is offline — nothing to do',
|
|
3325
3541
|
'remoteStatusUnshareFail': 'Closing the remote failed — try again later',
|
|
3326
3542
|
'remoteStatusUnshareFailCode': 'Closing the remote failed ({code}) — try again later',
|
|
3543
|
+
'remoteBridgeUnbridgedNote': 'better-sidebar is not bridged for this remote session (the server lacks the same version or has not allowed it) — treat files and changes as session messages only',
|
|
3327
3544
|
// Remote group title suffixes (the background merger cannot know the UI
|
|
3328
3545
|
// language and always uses the Chinese forms); the English equivalents
|
|
3329
3546
|
// live here for UI surfaces that restate the same status.
|
|
@@ -15067,6 +15284,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
15067
15284
|
const react_1 = require("react");
|
|
15068
15285
|
const dsh_client_ui_primitives_1 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
15069
15286
|
const settings_form_ts_1 = require("./client-data/settings-form.js");
|
|
15287
|
+
const plugin_bridge_ts_1 = require("./client-data/plugin-bridge.js");
|
|
15070
15288
|
const shares_ts_1 = require("./client-data/shares.js");
|
|
15071
15289
|
/** Device roles the pairing picker offers, in display order. */
|
|
15072
15290
|
const PAIR_ROLES = ['web', 'desktop-client'];
|
|
@@ -15320,6 +15538,41 @@ function SettingsSectionPage({ config, shares, t }) {
|
|
|
15320
15538
|
? (0, settings_form_ts_1.deriveSettingsView)(load.body, { now, rowUser: config.rowUser() })
|
|
15321
15539
|
: undefined;
|
|
15322
15540
|
const clientRole = config.savedRoleIsClient();
|
|
15541
|
+
// The plugin bridge's registered-plugin listing (PB-5), host page only:
|
|
15542
|
+
// loaded once per page life and refreshed whenever a save lands (the
|
|
15543
|
+
// consent staged by the toggles below rides that very save). Starts in
|
|
15544
|
+
// the loading state — the section renders the loading copy until the
|
|
15545
|
+
// first answer lands (or the load fails, which is its own state, never
|
|
15546
|
+
// the empty one); a composition without the route stays loading.
|
|
15547
|
+
// PB-9-fix: the listing is a THREE-state read — loading, failed (an old
|
|
15548
|
+
// build, or the page opened through the gateway where the listing is
|
|
15549
|
+
// refused), or an answer that carries zero rows. Only the third means
|
|
15550
|
+
// "nothing registered"; the first two must not masquerade as it.
|
|
15551
|
+
const [bridgeLoad, setBridgeLoad] = (0, react_1.useState)({ state: 'loading', rows: [] });
|
|
15552
|
+
const loadBridge = (0, react_1.useCallback)(() => {
|
|
15553
|
+
setBridgeLoad((prev) => ({ state: 'loading', rows: prev.rows }));
|
|
15554
|
+
fetch(settings_form_ts_1.ADMIN_PLUGIN_BRIDGE_ROUTE)
|
|
15555
|
+
.then((res) => {
|
|
15556
|
+
if (!res.ok)
|
|
15557
|
+
return Promise.reject(new Error(String(res.status)));
|
|
15558
|
+
return res.json();
|
|
15559
|
+
})
|
|
15560
|
+
.then((body) => {
|
|
15561
|
+
const plugins = body?.value?.plugins;
|
|
15562
|
+
if (!Array.isArray(plugins))
|
|
15563
|
+
return;
|
|
15564
|
+
setBridgeLoad({
|
|
15565
|
+
state: 'ready',
|
|
15566
|
+
rows: plugins.filter((row) => row !== null && typeof row === 'object' && typeof row.plugin === 'string'),
|
|
15567
|
+
});
|
|
15568
|
+
})
|
|
15569
|
+
.catch(() => { setBridgeLoad((prev) => ({ state: 'error', rows: prev.rows })); });
|
|
15570
|
+
}, []);
|
|
15571
|
+
(0, react_1.useEffect)(() => {
|
|
15572
|
+
if (!clientRole)
|
|
15573
|
+
loadBridge();
|
|
15574
|
+
}, [clientRole, loadBridge]);
|
|
15575
|
+
const bridgeState = (0, plugin_bridge_ts_1.bridgeSectionState)(bridgeLoad.state, bridgeLoad.rows);
|
|
15323
15576
|
const statusPoll = config.statusPoll();
|
|
15324
15577
|
(0, react_1.useEffect)(() => {
|
|
15325
15578
|
if (statusPoll === 'client')
|
|
@@ -15632,6 +15885,10 @@ function SettingsSectionPage({ config, shares, t }) {
|
|
|
15632
15885
|
setClientLoad({ state: 'loading' });
|
|
15633
15886
|
const refresh = config.savedRoleIsClient() ? loadClientStatus : loadStatus;
|
|
15634
15887
|
refresh();
|
|
15888
|
+
// The plugin bridge listing re-reads too (PB-5): the consent toggles
|
|
15889
|
+
// stage through the very save that just landed.
|
|
15890
|
+
if (!config.savedRoleIsClient())
|
|
15891
|
+
loadBridge();
|
|
15635
15892
|
// Immediate pull + 1.5s covers a hot-only save. A save that reloads the
|
|
15636
15893
|
// plugin row (T17) restarts the row and the gateway child with it — the
|
|
15637
15894
|
// reload and the gateway's rebinding of its port both take longer than
|
|
@@ -15653,7 +15910,19 @@ function SettingsSectionPage({ config, shares, t }) {
|
|
|
15653
15910
|
const reloadsRow = saveReloadsRow();
|
|
15654
15911
|
void config.save().then((landed) => { if (landed)
|
|
15655
15912
|
saveLanded(reloadsRow); });
|
|
15656
|
-
}, onDiscard: () => { config.discard(); }, children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.roleTitle') }), select(form.role, 'role', t('settings.role'), t('settings.roleHint'), ['host', 'client'], (option) => (option === 'host' ? t('settings.roleHost') : t('settings.roleClient'))), text(form.serverName, 'serverName', t('settings.fieldServerName'), t(clientRole ? 'settings.fieldServerNameHintClient' : 'settings.fieldServerNameHint'), t('settings.invalidName')), reloadPending && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-status-line", children: t('settings.reloadNote') }), !clientRole && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.gatewayTitle') }), text(form.host, 'host', t('settings.fieldHost'), t('settings.fieldHostHint'), '', false, t('settings.fieldHostNote')), text(form.port, 'port', t('settings.fieldPort'), t('settings.fieldPortHint'), t('settings.invalidPort'), true), text(form.trustedProxies, 'trustedProxies', t('settings.fieldTrustedProxies'), t('settings.fieldTrustedProxiesHint'), ''), text(form.rateLimit, 'rateLimit', t('settings.fieldRateLimit'), t('settings.fieldRateLimitHint'), t('settings.invalidPositiveInt'), true), text(form.vapidSubject, 'vapidSubject', t('settings.fieldVapidSubject'), t('settings.fieldVapidSubjectHint'), ''), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.pushTitle') }), bool(form.pushSummary, 'pushSummary', t('settings.fieldPushSummary'), t('settings.fieldPushSummaryHint')), bool(form.pushTurnEnd, 'pushTurnEnd', t('settings.fieldPushTurnEnd'), t('settings.fieldPushTurnEndHint')), bool(form.pushTool, 'pushTool', t('settings.fieldPushTool'), t('settings.fieldPushToolHint')), text(form.pushDebounceMs, 'pushDebounceMs', t('settings.fieldPushDebounceMs'), t('settings.fieldPushDebounceMsHint'), t('settings.invalidNonNegativeInt'), true), select(form.lang, 'lang', t('settings.fieldLang'), t('settings.fieldLangHint'), ['auto', 'zh', 'en'], (option) => (option === 'auto' ? t('settings.langAuto') : option === 'zh' ? t('settings.langZh') : t('settings.langEn'))), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.shareTitle') }), text(form.idleHours, 'idleHours', t('settings.fieldIdleHours'), t('settings.fieldIdleHoursHint'), t('settings.invalidHours'), true), bool(form.autoShareNewSessions, 'autoShareNewSessions', t('settings.fieldAutoShare'), t('settings.fieldAutoShareHint'))
|
|
15913
|
+
}, onDiscard: () => { config.discard(); }, children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.roleTitle') }), select(form.role, 'role', t('settings.role'), t('settings.roleHint'), ['host', 'client'], (option) => (option === 'host' ? t('settings.roleHost') : t('settings.roleClient'))), text(form.serverName, 'serverName', t('settings.fieldServerName'), t(clientRole ? 'settings.fieldServerNameHintClient' : 'settings.fieldServerNameHint'), t('settings.invalidName')), reloadPending && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-status-line", children: t('settings.reloadNote') }), !clientRole && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.gatewayTitle') }), text(form.host, 'host', t('settings.fieldHost'), t('settings.fieldHostHint'), '', false, t('settings.fieldHostNote')), text(form.port, 'port', t('settings.fieldPort'), t('settings.fieldPortHint'), t('settings.invalidPort'), true), text(form.trustedProxies, 'trustedProxies', t('settings.fieldTrustedProxies'), t('settings.fieldTrustedProxiesHint'), ''), text(form.rateLimit, 'rateLimit', t('settings.fieldRateLimit'), t('settings.fieldRateLimitHint'), t('settings.invalidPositiveInt'), true), text(form.vapidSubject, 'vapidSubject', t('settings.fieldVapidSubject'), t('settings.fieldVapidSubjectHint'), ''), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.pushTitle') }), bool(form.pushSummary, 'pushSummary', t('settings.fieldPushSummary'), t('settings.fieldPushSummaryHint')), bool(form.pushTurnEnd, 'pushTurnEnd', t('settings.fieldPushTurnEnd'), t('settings.fieldPushTurnEndHint')), bool(form.pushTool, 'pushTool', t('settings.fieldPushTool'), t('settings.fieldPushToolHint')), text(form.pushDebounceMs, 'pushDebounceMs', t('settings.fieldPushDebounceMs'), t('settings.fieldPushDebounceMsHint'), t('settings.invalidNonNegativeInt'), true), select(form.lang, 'lang', t('settings.fieldLang'), t('settings.fieldLangHint'), ['auto', 'zh', 'en'], (option) => (option === 'auto' ? t('settings.langAuto') : option === 'zh' ? t('settings.langZh') : t('settings.langEn'))), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.shareTitle') }), text(form.idleHours, 'idleHours', t('settings.fieldIdleHours'), t('settings.fieldIdleHoursHint'), t('settings.invalidHours'), true), bool(form.autoShareNewSessions, 'autoShareNewSessions', t('settings.fieldAutoShare'), t('settings.fieldAutoShareHint')), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.bridge.title') }), bridgeState === 'loading' && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.bridge.loading') }), bridgeState === 'error' && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.bridge.loadFailed') }), bridgeState === 'empty' && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.bridge.empty') }), bridgeLoad.rows.map((row) => {
|
|
15914
|
+
let current = {};
|
|
15915
|
+
try {
|
|
15916
|
+
const parsed = JSON.parse(form.pluginBridgeConsent.text);
|
|
15917
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
15918
|
+
current = parsed;
|
|
15919
|
+
}
|
|
15920
|
+
}
|
|
15921
|
+
catch { /* a malformed draft reads as nothing consented */ }
|
|
15922
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-head", children: [(0, jsx_runtime_1.jsx)("span", { className: "zr-settings-head-title", children: row.plugin }), (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-badge", children: row.version })] }), row.hasServerScope ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-head", children: [(0, jsx_runtime_1.jsx)("label", { children: t('settings.bridge.consentToggle') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Switch, { checked: current[row.plugin] === true, onChange: (checked) => {
|
|
15923
|
+
config.stage('pluginBridgeConsent', JSON.stringify((0, plugin_bridge_ts_1.togglePluginConsent)(current, row.plugin, checked)));
|
|
15924
|
+
}, label: t('settings.bridge.consentToggle') })] }), (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.bridge.consentHint') })] })) : ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.bridge.sessionOnly') }))] }, row.plugin));
|
|
15925
|
+
})] }))] }) }), !clientRole && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-card", children: [(0, jsx_runtime_1.jsx)("p", { className: "zr-settings-status-line", "data-down": data !== undefined && !data.gatewayReachable && !data.gatewayAbnormal ? 'true' : undefined, children: data === undefined
|
|
15657
15926
|
? ''
|
|
15658
15927
|
: data.gatewayAbnormal
|
|
15659
15928
|
? t('settings.gatewayAbnormal', { code: data.gatewayStatus })
|
|
@@ -15667,9 +15936,11 @@ function SettingsSectionPage({ config, shares, t }) {
|
|
|
15667
15936
|
// hyphens stripped) on every keystroke, and only the
|
|
15668
15937
|
// NORMALIZED length gates the pair button — a code pasted with
|
|
15669
15938
|
// separators must survive pasting intact.
|
|
15670
|
-
value: pairCode, onChange: (e) => { setPairCode((0, settings_form_ts_1.normalizePairingCode)(e.currentTarget.value)); } }), (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.client.pairingCodeHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 12 }, children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: !claimAllowed, onClick: () => { void runClaim(); }, children: claimBusy ? t('settings.client.pairing') : t('settings.client.pair') }), form.serverName.invalid && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.fixDeviceName') }), claimFail !== null && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: pairFailText(claimFail, t) }), claimWriteFailed && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.failWrite') })] }), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.client.statusTitle') }), (0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 4 }, children: [(0, jsx_runtime_1.jsx)("p", { className: "zr-settings-status-line", style: { flex: 1 }, "data-down": clientView !== undefined && (clientView.state === 'unreachable' || clientView.state === 'unexpected' || clientView.state === 'revoked' || clientView.state === 'invalid-url' || clientView.state === 'incompatible') ? 'true' : undefined, children: clientView === undefined ? '' : clientStatusText(clientView, now, t) }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: clientView === undefined || clientView.nextRetryAt === undefined || reconnectBusy, onClick: () => { void runReconnect(); }, children: reconnectBusy ? t('settings.client.reconnectBusy') : t('settings.client.reconnect') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: clientLoad.state === 'loading', onClick: loadClientStatus, children: clientLoad.state === 'loading' ? t('settings.client.statusRefreshing') : t('settings.client.statusRefresh') })] }), form.deviceToken.configured && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 12 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "zr-settings-badge", children: t('settings.client.tokenSet') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "ghost", size: "sm", disabled: !form.available || !form.writable || unpairBusy, onClick: () => { void runUnpair(); }, children: unpairBusy ? t('settings.client.unpairBusy') : t('settings.client.unpair') })] })), unpairDone && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", style: { paddingBottom: 12 }, children: t('settings.client.unpairDone') }), clientView !== undefined && (clientView.lastError !== '' || clientView.intercept !== undefined || clientView.compat !== undefined) && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.client.diagTitle') }), clientView.lastError !== '' && ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": clientView.state === 'unreachable' ? 'true' : undefined, children: t('settings.client.diagLastError', { code: clientView.lastError }) })), clientView.intercept !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: clientView.intercept.installed
|
|
15939
|
+
value: pairCode, onChange: (e) => { setPairCode((0, settings_form_ts_1.normalizePairingCode)(e.currentTarget.value)); } }), (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: t('settings.client.pairingCodeHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 12 }, children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: !claimAllowed, onClick: () => { void runClaim(); }, children: claimBusy ? t('settings.client.pairing') : t('settings.client.pair') }), form.serverName.invalid && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.fixDeviceName') }), claimFail !== null && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: pairFailText(claimFail, t) }), claimWriteFailed && (0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.failWrite') })] }), (0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.client.statusTitle') }), (0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 4 }, children: [(0, jsx_runtime_1.jsx)("p", { className: "zr-settings-status-line", style: { flex: 1 }, "data-down": clientView !== undefined && (clientView.state === 'unreachable' || clientView.state === 'unexpected' || clientView.state === 'revoked' || clientView.state === 'invalid-url' || clientView.state === 'incompatible') ? 'true' : undefined, children: clientView === undefined ? '' : clientStatusText(clientView, now, t) }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: clientView === undefined || clientView.nextRetryAt === undefined || reconnectBusy, onClick: () => { void runReconnect(); }, children: reconnectBusy ? t('settings.client.reconnectBusy') : t('settings.client.reconnect') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", size: "sm", disabled: clientLoad.state === 'loading', onClick: loadClientStatus, children: clientLoad.state === 'loading' ? t('settings.client.statusRefreshing') : t('settings.client.statusRefresh') })] }), form.deviceToken.configured && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-row", style: { paddingBottom: 12 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "zr-settings-badge", children: t('settings.client.tokenSet') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "ghost", size: "sm", disabled: !form.available || !form.writable || unpairBusy, onClick: () => { void runUnpair(); }, children: unpairBusy ? t('settings.client.unpairBusy') : t('settings.client.unpair') })] })), unpairDone && (0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", style: { paddingBottom: 12 }, children: t('settings.client.unpairDone') }), clientView !== undefined && (clientView.lastError !== '' || clientView.intercept !== undefined || clientView.compat !== undefined || (clientView.pluginBridge?.length ?? 0) > 0 || clientView.pluginRouteIntercept !== undefined) && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h3", { className: "zr-settings-card-title", children: t('settings.client.diagTitle') }), clientView.lastError !== '' && ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": clientView.state === 'unreachable' ? 'true' : undefined, children: t('settings.client.diagLastError', { code: clientView.lastError }) })), clientView.intercept !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", children: clientView.intercept.installed
|
|
15671
15940
|
? t('settings.client.diagInterceptInstalled')
|
|
15672
|
-
: t('settings.client.diagInterceptMissing') }), clientView.intercept.reasons.length > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", "data-invalid": "true", children: [t('settings.client.diagInterceptDisabled'), clientView.intercept.reasons.map((reason, index) => ((0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", children: reason }, index)))] })), clientView.intercept.recentFailures.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsx)("div", { className: "zr-settings-head", children: (0, jsx_runtime_1.jsx)("label", { children: t('settings.client.diagRecentFailures') }) }), clientView.intercept.recentFailures.map((failure, index) => ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", children: [new Date(failure.time).toLocaleString(), " \u00B7 ", failure.method !== '' ? failure.method : '—', " \u00B7 ", failure.code] }, index)))] }))] })), clientView.compat !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [clientView.compat.mismatchedGroups.length > 0 && ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.diagCompatGroups', { groups: clientView.compat.mismatchedGroups.join(', ') }) })), clientView.compat.recentCalls.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsx)("div", { className: "zr-settings-head", children: (0, jsx_runtime_1.jsx)("label", { children: t('settings.client.diagCompatCalls') }) }), clientView.compat.recentCalls.map((failure, index) => ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", children: [new Date(failure.time).toLocaleString(), " \u00B7 ", failure.method !== '' ? failure.method : '—', " \u00B7 ", failure.code] }, index)))] }))] }))
|
|
15941
|
+
: t('settings.client.diagInterceptMissing') }), clientView.intercept.reasons.length > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", "data-invalid": "true", children: [t('settings.client.diagInterceptDisabled'), clientView.intercept.reasons.map((reason, index) => ((0, jsx_runtime_1.jsx)("span", { className: "zr-settings-hint", children: reason }, index)))] })), clientView.intercept.recentFailures.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsx)("div", { className: "zr-settings-head", children: (0, jsx_runtime_1.jsx)("label", { children: t('settings.client.diagRecentFailures') }) }), clientView.intercept.recentFailures.map((failure, index) => ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", children: [new Date(failure.time).toLocaleString(), " \u00B7 ", failure.method !== '' ? failure.method : '—', " \u00B7 ", failure.code] }, index)))] }))] })), clientView.compat !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [clientView.compat.mismatchedGroups.length > 0 && ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.client.diagCompatGroups', { groups: clientView.compat.mismatchedGroups.join(', ') }) })), clientView.compat.recentCalls.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsx)("div", { className: "zr-settings-head", children: (0, jsx_runtime_1.jsx)("label", { children: t('settings.client.diagCompatCalls') }) }), clientView.compat.recentCalls.map((failure, index) => ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", children: [new Date(failure.time).toLocaleString(), " \u00B7 ", failure.method !== '' ? failure.method : '—', " \u00B7 ", failure.code] }, index)))] }))] })), (clientView.pluginBridge?.length ?? 0) > 0 && clientView.pluginBridge !== undefined && ((0, jsx_runtime_1.jsxs)("div", { className: "zr-settings-field", children: [(0, jsx_runtime_1.jsx)("div", { className: "zr-settings-head", children: (0, jsx_runtime_1.jsx)("label", { children: t('settings.bridge.title') }) }), clientView.pluginBridge.map((row) => ((0, jsx_runtime_1.jsxs)("p", { className: "zr-settings-hint", children: [row.plugin, " \u00B7 ", row.localVersion !== '' ? `v${row.localVersion}` : '—', " \u00B7", ' ', row.stateKey === 'settings.bridge.stateVersionMismatch'
|
|
15942
|
+
? t(row.stateKey, { version: row.serverVersion ?? '?' })
|
|
15943
|
+
: t(row.stateKey)] }, row.plugin)))] })), clientView.pluginRouteIntercept !== undefined && !clientView.pluginRouteIntercept.installed && ((0, jsx_runtime_1.jsx)("p", { className: "zr-settings-hint", "data-invalid": "true", children: t('settings.bridge.interceptOff', { reason: clientView.pluginRouteIntercept.reasons.join('; ') || '—' }) }))] }))] }))] }));
|
|
15673
15944
|
}
|
|
15674
15945
|
/** Display name of a value's source layer for the invalid-saved-value note. */
|
|
15675
15946
|
function sourceName(source, t) {
|
|
@@ -16486,8 +16757,9 @@ __modules["client-data/remote-status.js"] = function (require, module, exports)
|
|
|
16486
16757
|
* - `getRemoteStatusStore()` is the page-wide singleton.
|
|
16487
16758
|
*/
|
|
16488
16759
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16489
|
-
exports.REMOTE_STATUS_POLL_MS = exports.documentVisibility = exports.CLIENT_UNSHARE_ROUTE = exports.REMOTE_STATUS_ROUTE = void 0;
|
|
16760
|
+
exports.REMOTE_STATUS_POLL_MS = exports.documentVisibility = exports.BRIDGE_STAMP_ATTRIBUTE = exports.BRIDGE_STAMP_PLUGIN = exports.CLIENT_UNSHARE_ROUTE = exports.REMOTE_STATUS_ROUTE = void 0;
|
|
16490
16761
|
exports.parseRemoteStatusBody = parseRemoteStatusBody;
|
|
16762
|
+
exports.shouldStampBridgeAttribute = shouldStampBridgeAttribute;
|
|
16491
16763
|
exports.createZhRemoteStatusFormatter = createZhRemoteStatusFormatter;
|
|
16492
16764
|
exports.describeRemoteStatus = describeRemoteStatus;
|
|
16493
16765
|
exports.bannerText = bannerText;
|
|
@@ -16521,9 +16793,30 @@ function parseRemoteStatusBody(body, now) {
|
|
|
16521
16793
|
versionMismatch: record.versionMismatch === true,
|
|
16522
16794
|
serverName: typeof record.serverName === 'string' ? record.serverName : '',
|
|
16523
16795
|
closed,
|
|
16796
|
+
bridgedPlugins: Array.isArray(record.bridgedPlugins)
|
|
16797
|
+
? record.bridgedPlugins.filter((name) => typeof name === 'string' && name !== '')
|
|
16798
|
+
: [],
|
|
16524
16799
|
asOf: now,
|
|
16525
16800
|
};
|
|
16526
16801
|
}
|
|
16802
|
+
// --- the bridge stamp (PB-14) -----------------------------------------------
|
|
16803
|
+
/**
|
|
16804
|
+
* Whether ONE plugin's bridge verdict is bridged RIGHT NOW, judged off the
|
|
16805
|
+
* `bridgedPlugins` list the remote-status body carries. Tolerant by the
|
|
16806
|
+
* same discipline as the parse: a garbage list (an older server that never
|
|
16807
|
+
* learned the field, a broken body) reads as "not bridged" — the hiding
|
|
16808
|
+
* rules stay in force unless the server positively says otherwise, never
|
|
16809
|
+
* the reverse. This is the pure core behind the
|
|
16810
|
+
* `html[data-zr-bridge-<plugin>]` stamp the remote-session stylesheet
|
|
16811
|
+
* spends.
|
|
16812
|
+
*/
|
|
16813
|
+
function shouldStampBridgeAttribute(bridgedPlugins, plugin) {
|
|
16814
|
+
return Array.isArray(bridgedPlugins) && bridgedPlugins.includes(plugin);
|
|
16815
|
+
}
|
|
16816
|
+
/** The plugin the bridge stamp names. */
|
|
16817
|
+
exports.BRIDGE_STAMP_PLUGIN = 'dsh-better-sidebar';
|
|
16818
|
+
/** The html attribute the stamp writes (`data-zr-bridge-dsh-better-sidebar`). */
|
|
16819
|
+
exports.BRIDGE_STAMP_ATTRIBUTE = `data-zr-bridge-${exports.BRIDGE_STAMP_PLUGIN}`;
|
|
16527
16820
|
/** Default formatter: the plugin's Chinese dictionary. Components pass their
|
|
16528
16821
|
* real framework `t`; this serves the pure-function callers (tests, check
|
|
16529
16822
|
* scripts). */
|
|
@@ -17654,17 +17947,23 @@ html[data-zr-remote-session="1"] [data-open-target] {
|
|
|
17654
17947
|
display: none !important;
|
|
17655
17948
|
}
|
|
17656
17949
|
|
|
17657
|
-
/* ---------- remote session: hide dsh-better-sidebar's file surfaces (T53) ----------
|
|
17658
|
-
dsh-better-sidebar 0.
|
|
17659
|
-
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17663
|
-
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17950
|
+
/* ---------- remote session: hide dsh-better-sidebar's file surfaces (T53, two regimes since PB-14) ----------
|
|
17951
|
+
dsh-better-sidebar 0.25.0 is the second big "open the machine's files" UI
|
|
17952
|
+
AND, since PB-12/13, the first ADAPTER-BRIDGED one: at exactly 0.25.0 its
|
|
17953
|
+
/sidebar/api routes, its two WebSockets and its side chat cross the byte
|
|
17954
|
+
tunnel and read/write the SERVER's data, gated on the whole-machine-data
|
|
17955
|
+
consent. The hiding below therefore applies ONLY while the plugin is NOT
|
|
17956
|
+
bridged — the :not([data-zr-bridge-dsh-better-sidebar="1"]) stamp the
|
|
17957
|
+
remote-status poll writes; see the PB-14 note above): bridged, the panels
|
|
17958
|
+
are remote-aware and stay visible; unbridged (a different version, the
|
|
17959
|
+
server without the adapter's consent), every tab it registers (files/
|
|
17960
|
+
editor, git changes, subagent/tasks, side chat, the markdown/html/code
|
|
17961
|
+
file viewers) builds its requests from a {sessionId, cwd} scope and POSTs
|
|
17962
|
+
them to the plugin's own /sidebar/api/* routes on whichever machine
|
|
17963
|
+
answers — in a remote session that is the SUB-CLIENT's machine (client.js
|
|
17964
|
+
call() ~3111, fetchUpload ~3162, encodeHtmlUrl /sidebar/html/ ~3069-3414),
|
|
17965
|
+
so the tree would show same-named LOCAL files as if they were the
|
|
17966
|
+
session's and an edit would WRITE locally.
|
|
17668
17967
|
|
|
17669
17968
|
Anchors, all the plugin's or the host's own rendered contracts (no hashed
|
|
17670
17969
|
class names; the one suffix selector, _chipIcon, is unique among the
|
|
@@ -17673,18 +17972,21 @@ html[data-zr-remote-session="1"] [data-open-target] {
|
|
|
17673
17972
|
@liustack/modsearch / @wxg-prc-cpg/browser-skill-dsh-plugin (modsearch /
|
|
17674
17973
|
browser-skill) / dshmarket and every @deepseek-ai sidebar package for it):
|
|
17675
17974
|
|
|
17676
|
-
HONEST LIMITS (T53-fix
|
|
17975
|
+
HONEST LIMITS (T53-fix, adjusted PB-14): unbridged, the plugin's
|
|
17677
17976
|
components stay mounted and keep silently POSTing READ-ONLY /sidebar/api
|
|
17678
17977
|
requests to the local machine (session.cwd, changes.ops, the tree reads a
|
|
17679
17978
|
mounted tab refreshes with) — the data never leaves that machine, and no
|
|
17680
|
-
write can happen because every write control is inside the hidden
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17979
|
+
write can happen because every write control is inside the hidden
|
|
17980
|
+
surfaces (the tab body hides its CHILDREN and spends the note copy as a
|
|
17981
|
+
::before, so an already-open tab reads WHY it is empty instead of
|
|
17982
|
+
sitting blank). Known boundary: a dsh-resource://file/** address opened
|
|
17983
|
+
through sidebarRight.openResource in a remote session (file links in
|
|
17984
|
+
chat, deliverables, @-references, skills) is CLAIMED by the plugin's
|
|
17985
|
+
editor registration (client.js ~21377: patterns ["dsh-resource://file/**"])
|
|
17986
|
+
before the host preview sees it — the right sidebar expands, the rules
|
|
17987
|
+
below hide the tab's body content and chip, and the note fills the panel.
|
|
17988
|
+
Bridged (0.25.0 + consent), none of this applies: the tab reads the
|
|
17989
|
+
server's data through the adapter and stays visible whole.
|
|
17688
17990
|
|
|
17689
17991
|
- [data-dsh-bottom-toggle] — the header-utilities button that expands the
|
|
17690
17992
|
bottom workbench (client.js BottomDockToggle ~20863; slot registration
|
|
@@ -17757,31 +18059,61 @@ html[data-zr-remote-session="1"] [data-open-target] {
|
|
|
17757
18059
|
attribute and this rule hides it too (cosmetic; the page stays reachable
|
|
17758
18060
|
through its shortcut command path and the plugin's takeover is the
|
|
17759
18061
|
"files" implementation on every deployment this plugin pairs with
|
|
17760
|
-
better-sidebar anyway).
|
|
17761
|
-
|
|
18062
|
+
better-sidebar anyway).
|
|
18063
|
+
|
|
18064
|
+
/* PB-14: every T53 rule below carries
|
|
18065
|
+
:not([data-zr-bridge-dsh-better-sidebar="1"]) — when the adapter
|
|
18066
|
+
(PB-12) bridges the plugin, the panels read the SERVER's data through
|
|
18067
|
+
the byte tunnel and MUST stay visible; the hiding applies only to the
|
|
18068
|
+
unbridged combination, and the empty tab body then carries a note
|
|
18069
|
+
instead of blankness. The stamp is written by
|
|
18070
|
+
effects/remote-session.ts off the remote-status poll's bridgedPlugins
|
|
18071
|
+
list (client-data/remote-status.ts shouldStampBridgeAttribute). */
|
|
18072
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dsh-bottom-toggle] {
|
|
17762
18073
|
display: none !important;
|
|
17763
18074
|
}
|
|
17764
|
-
html[data-zr-remote-session="1"] [data-dsh-panel-host] {
|
|
18075
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dsh-panel-host] {
|
|
17765
18076
|
display: none !important;
|
|
17766
18077
|
}
|
|
17767
|
-
html[data-zr-remote-session="1"] {
|
|
18078
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) {
|
|
17768
18079
|
--dsh-sidebar-height: 0px !important;
|
|
17769
18080
|
}
|
|
17770
|
-
|
|
18081
|
+
/* The unbridged tab body: hide the plugin's own content and spend the
|
|
18082
|
+
note copy as a ::before — an already-open tab reads WHY it is empty
|
|
18083
|
+
(0.25.0 keeps tabs mounted, so a stale body would otherwise sit blank).
|
|
18084
|
+
--zr-bridge-note is set on <html> by the stamp effect, in the
|
|
18085
|
+
browser's language; unset (effect not running yet) the content falls
|
|
18086
|
+
through and nothing renders. */
|
|
18087
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dsh-native-tab-host] > * {
|
|
18088
|
+
display: none !important;
|
|
18089
|
+
}
|
|
18090
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dsh-native-tab-host]::before {
|
|
18091
|
+
content: var(--zr-bridge-note);
|
|
18092
|
+
display: block;
|
|
18093
|
+
padding: 24px 16px;
|
|
18094
|
+
font-size: 13px;
|
|
18095
|
+
line-height: 1.7;
|
|
18096
|
+
color: var(--dsh-fg-muted, #6b7280);
|
|
18097
|
+
word-break: break-word;
|
|
18098
|
+
}
|
|
18099
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dockkit-tab]:has([class$="_chipIcon"]) {
|
|
17771
18100
|
display: none !important;
|
|
17772
18101
|
}
|
|
17773
|
-
html[data-zr-remote-session="1"] [data-
|
|
18102
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-floating-window]:has([data-window-body]) {
|
|
17774
18103
|
display: none !important;
|
|
17775
18104
|
}
|
|
17776
|
-
html[data-zr-remote-session="1"]
|
|
18105
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-sidebar-right-guide-entry="git"],
|
|
18106
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-sidebar-right-guide-entry="subagent"],
|
|
18107
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-sidebar-right-guide-entry="sidechat"] {
|
|
17777
18108
|
display: none !important;
|
|
17778
18109
|
}
|
|
17779
|
-
html[data-zr-remote-session="1"] [data-sidebar-right-guide-entry="
|
|
17780
|
-
html[data-zr-remote-session="1"] [data-sidebar-right-guide-entry="subagent"],
|
|
17781
|
-
html[data-zr-remote-session="1"] [data-sidebar-right-guide-entry="sidechat"] {
|
|
18110
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-sidebar-right-guide-entry="files"]:not([aria-keyshortcuts]) {
|
|
17782
18111
|
display: none !important;
|
|
17783
18112
|
}
|
|
17784
|
-
|
|
18113
|
+
/* 0.25.0's dock fallback — the floating quick-open button the plugin pins
|
|
18114
|
+
for empty workspaces. Same treatment as every other open-the-machine's-
|
|
18115
|
+
files control: hidden while unbridged, untouched when bridged. */
|
|
18116
|
+
html[data-zr-remote-session="1"]:not([data-zr-bridge-dsh-better-sidebar="1"]) [data-dsh-dock-fallback] {
|
|
17785
18117
|
display: none !important;
|
|
17786
18118
|
}
|
|
17787
18119
|
|
|
@@ -17902,6 +18234,8 @@ exports.installRemoteSessionGuard = installRemoteSessionGuard;
|
|
|
17902
18234
|
const types_ts_1 = require("./client/compat/types.js");
|
|
17903
18235
|
const virtual_id_ts_1 = require("./virtual-id.js");
|
|
17904
18236
|
const remote_session_css_ts_1 = require("./client/styles/remote-session.css.js");
|
|
18237
|
+
const remote_status_ts_1 = require("./client-data/remote-status.js");
|
|
18238
|
+
const locales_ts_1 = require("./client/locales.js");
|
|
17905
18239
|
/**
|
|
17906
18240
|
* The remote-session marker (T41b): the CURRENT main-view session id, read
|
|
17907
18241
|
* the way the official uiSession `publishMain` reads it (RT
|
|
@@ -17924,6 +18258,26 @@ const remote_session_css_ts_1 = require("./client/styles/remote-session.css.js")
|
|
|
17924
18258
|
* attribute must track the current session regardless of which panel is
|
|
17925
18259
|
* mounted, and an attribute + stylesheet survives every re-render untouched.
|
|
17926
18260
|
*/
|
|
18261
|
+
/** The CURRENT main-view session id, the publishMain way (see the module
|
|
18262
|
+
* comment). Shared by the session marker and the bridge stamp — both key on
|
|
18263
|
+
* the same "which session is on screen" answer. */
|
|
18264
|
+
function currentMainSessionIdOf(ctx) {
|
|
18265
|
+
const { byId } = ctx.sessions.list.getSnapshot();
|
|
18266
|
+
const uiSession = ctx.get('uiSession');
|
|
18267
|
+
const current = uiSession?.current?.value?.key;
|
|
18268
|
+
const rows = byId;
|
|
18269
|
+
return typeof current === 'string' && (rows[current]?.retainedBy?.mainView ?? 0) > 0
|
|
18270
|
+
? current
|
|
18271
|
+
: (0, types_ts_1.mainSessionIdOf)(byId);
|
|
18272
|
+
}
|
|
18273
|
+
/** The unbridged note copy in the BROWSER's language (the plugin's own
|
|
18274
|
+
* namespace carries both dictionaries; the framework `t` seat is not
|
|
18275
|
+
* reachable from a non-React effect, so the navigator's language picks —
|
|
18276
|
+
* `zh*` reads Chinese, everything else English). */
|
|
18277
|
+
function bridgeNoteOf(language) {
|
|
18278
|
+
const key = 'remoteBridgeUnbridgedNote';
|
|
18279
|
+
return typeof language === 'string' && language.startsWith('zh') ? locales_ts_1.zh[key] : locales_ts_1.en[key];
|
|
18280
|
+
}
|
|
17927
18281
|
function installRemoteSessionGuard(ctx) {
|
|
17928
18282
|
ctx.effect(() => {
|
|
17929
18283
|
const tag = document.createElement('style');
|
|
@@ -17937,15 +18291,7 @@ function installRemoteSessionGuard(ctx) {
|
|
|
17937
18291
|
}, 'dsh-zen-remote: remote-session styles');
|
|
17938
18292
|
ctx.effect(() => {
|
|
17939
18293
|
const apply = () => {
|
|
17940
|
-
const
|
|
17941
|
-
// publishMain's tie-break (see the module comment): prefer the still-
|
|
17942
|
-
// retained current main binding, else the first mainView row.
|
|
17943
|
-
const uiSession = ctx.get('uiSession');
|
|
17944
|
-
const current = uiSession?.current?.value?.key;
|
|
17945
|
-
const rows = byId;
|
|
17946
|
-
const sessionId = typeof current === 'string' && (rows[current]?.retainedBy?.mainView ?? 0) > 0
|
|
17947
|
-
? current
|
|
17948
|
-
: (0, types_ts_1.mainSessionIdOf)(byId);
|
|
18294
|
+
const sessionId = currentMainSessionIdOf(ctx);
|
|
17949
18295
|
const root = document.documentElement;
|
|
17950
18296
|
if (sessionId !== undefined && (0, virtual_id_ts_1.isVirtual)(sessionId))
|
|
17951
18297
|
root.setAttribute('data-zr-remote-session', '1');
|
|
@@ -17966,6 +18312,64 @@ function installRemoteSessionGuard(ctx) {
|
|
|
17966
18312
|
document.documentElement.removeAttribute('data-zr-remote-session');
|
|
17967
18313
|
};
|
|
17968
18314
|
}, 'dsh-zen-remote: remote-session attribute');
|
|
18315
|
+
// The bridge stamp (PB-14): while a REMOTE session is on screen, the
|
|
18316
|
+
// remote-status poll's `bridgedPlugins` decides whether better-sidebar is
|
|
18317
|
+
// bridged RIGHT NOW — bridged, the T53 hiding rules lift (the panels read
|
|
18318
|
+
// the server's data through the adapter) and the html carries
|
|
18319
|
+
// `data-zr-bridge-dsh-better-sidebar="1"`; not bridged, the rules stay
|
|
18320
|
+
// and the html carries the unbridged note copy as a CSS variable the
|
|
18321
|
+
// stylesheet spends on the empty tab body. The subscription rides
|
|
18322
|
+
// subscribeIfVirtual — a LOCAL session (or a host page) never polls, and
|
|
18323
|
+
// the attribute comes off; better-sidebar not installed or the server not
|
|
18324
|
+
// paired simply never appear in `bridgedPlugins`, so the stamp never
|
|
18325
|
+
// fires from this side alone.
|
|
18326
|
+
ctx.effect(() => {
|
|
18327
|
+
const root = document.documentElement;
|
|
18328
|
+
const store = (0, remote_status_ts_1.getRemoteStatusStore)();
|
|
18329
|
+
let offStore;
|
|
18330
|
+
const clear = () => {
|
|
18331
|
+
root.removeAttribute(remote_status_ts_1.BRIDGE_STAMP_ATTRIBUTE);
|
|
18332
|
+
root.style.removeProperty('--zr-bridge-note');
|
|
18333
|
+
};
|
|
18334
|
+
const applyBridge = () => {
|
|
18335
|
+
const view = store.getSnapshot().view;
|
|
18336
|
+
if ((0, remote_status_ts_1.shouldStampBridgeAttribute)(view?.bridgedPlugins, remote_status_ts_1.BRIDGE_STAMP_PLUGIN)) {
|
|
18337
|
+
root.setAttribute(remote_status_ts_1.BRIDGE_STAMP_ATTRIBUTE, '1');
|
|
18338
|
+
root.style.removeProperty('--zr-bridge-note');
|
|
18339
|
+
}
|
|
18340
|
+
else {
|
|
18341
|
+
root.removeAttribute(remote_status_ts_1.BRIDGE_STAMP_ATTRIBUTE);
|
|
18342
|
+
// JSON.stringify's quotes are a legal CSS string — the stylesheet's
|
|
18343
|
+
// ::before spends it as `content: var(--zr-bridge-note)`.
|
|
18344
|
+
root.style.setProperty('--zr-bridge-note', JSON.stringify(bridgeNoteOf(navigator.language)));
|
|
18345
|
+
}
|
|
18346
|
+
};
|
|
18347
|
+
const retune = () => {
|
|
18348
|
+
const sessionId = currentMainSessionIdOf(ctx);
|
|
18349
|
+
const virtualNow = sessionId !== undefined && (0, virtual_id_ts_1.isVirtual)(sessionId);
|
|
18350
|
+
if (virtualNow && offStore === undefined && typeof sessionId === 'string') {
|
|
18351
|
+
offStore = (0, remote_status_ts_1.subscribeIfVirtual)(store, sessionId, applyBridge);
|
|
18352
|
+
applyBridge();
|
|
18353
|
+
return;
|
|
18354
|
+
}
|
|
18355
|
+
if (!virtualNow && offStore !== undefined) {
|
|
18356
|
+
offStore();
|
|
18357
|
+
offStore = undefined;
|
|
18358
|
+
clear();
|
|
18359
|
+
}
|
|
18360
|
+
};
|
|
18361
|
+
retune();
|
|
18362
|
+
const offs = [ctx.sessions.list.subscribe(retune)];
|
|
18363
|
+
const current = ctx.get('uiSession')?.current;
|
|
18364
|
+
if (typeof current?.subscribe === 'function')
|
|
18365
|
+
offs.push(current.subscribe(retune));
|
|
18366
|
+
return () => {
|
|
18367
|
+
for (const off of offs)
|
|
18368
|
+
off();
|
|
18369
|
+
offStore?.();
|
|
18370
|
+
clear();
|
|
18371
|
+
};
|
|
18372
|
+
}, 'dsh-zen-remote: bridge stamp');
|
|
17969
18373
|
}
|
|
17970
18374
|
};
|
|
17971
18375
|
__modules["client-data/model-group-side.js"] = function (require, module, exports) {
|