@linxin666/dsh-remote-web-ui 0.4.3 → 0.4.5
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/README.i18n.yaml +2 -2
- package/README.md +23 -8
- package/README.zh.md +22 -7
- package/lib/client.js +194 -140
- package/lib/client.js.map +1 -1
- package/lib/index.js +231 -23
- package/lib/types/client/PluginSettingsCard.d.ts +7 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +2 -2
- package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
- package/lib/types/client/RemoteSettingsCard.js +2 -1
- package/lib/types/client/index.js +3 -3
- package/lib/types/client/locales.d.ts +0 -4
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +5 -9
- package/lib/types/client/mobile-adapt.d.ts +2 -2
- package/lib/types/client/mobile-adapt.d.ts.map +1 -1
- package/lib/types/client/mobile-adapt.js +163 -136
- package/lib/types/client/settings-form.d.ts +16 -0
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +27 -1
- package/lib/types/detached-work.d.ts +45 -0
- package/lib/types/detached-work.d.ts.map +1 -0
- package/lib/types/detached-work.js +54 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +67 -12
- package/lib/types/lan-bind.d.ts +21 -4
- package/lib/types/lan-bind.d.ts.map +1 -1
- package/lib/types/lan-bind.js +103 -17
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +43 -0
- package/lib/types/remote-api.d.ts +13 -1
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +14 -1
- package/package.json +19 -19
- package/src/client/PluginSettingsCard.tsx +9 -0
- package/src/client/RemoteSettingsCard.tsx +2 -0
- package/src/client/index.ts +3 -3
- package/src/client/locales.ts +5 -9
- package/src/client/mobile-adapt.ts +167 -138
- package/src/client/settings-card.module.css +31 -5
- package/src/client/settings-form.ts +27 -1
- package/src/detached-work.ts +57 -0
- package/src/index.ts +67 -12
- package/src/lan-bind.ts +111 -17
- package/src/pairing.ts +44 -1
- package/src/remote-api.ts +14 -1
package/lib/client.js
CHANGED
|
@@ -2114,7 +2114,7 @@ window.__ModuleLoader__.load({
|
|
|
2114
2114
|
}
|
|
2115
2115
|
//#endregion
|
|
2116
2116
|
//#region \0dsh-css:packages/dsh-remote-web-ui/src/client/settings-card.module.css.mjs
|
|
2117
|
-
const css = ".Kwoi6G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Kwoi6G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_header{appearance:none;box-sizing:border-box;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:
|
|
2117
|
+
const css = ".Kwoi6G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Kwoi6G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_header{appearance:none;box-sizing:border-box;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:13px;padding:16px;display:flex}.Kwoi6G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Kwoi6G_headerStatic{box-sizing:border-box;border-radius:12px;align-items:center;gap:13px;width:100%;padding:16px;display:flex}.Kwoi6G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Kwoi6G_mark{background:var(--dsw-alias-bg-module-platform);width:34px;height:34px;color:var(--dsw-alias-label-secondary);border-radius:10px;flex:none;place-items:center;transition:color .16s;display:grid}.Kwoi6G_header:hover .Kwoi6G_mark,.Kwoi6G_cardOpen .Kwoi6G_mark{color:var(--dsw-alias-label-primary)}.Kwoi6G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Kwoi6G_description{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5}.Kwoi6G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;width:16px;height:16px;transition:transform .16s,color .16s}.Kwoi6G_header:hover .Kwoi6G_chevron{color:var(--dsw-alias-label-secondary)}.Kwoi6G_chevronOpen{transform:rotate(180deg)}.Kwoi6G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Kwoi6G_readOnly{color:var(--dsw-alias-label-secondary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Kwoi6G_failed{min-width:0;color:var(--dsw-alias-state-error-primary,#b42318);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Kwoi6G_discard,.Kwoi6G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Kwoi6G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Kwoi6G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Kwoi6G_discard:disabled,.Kwoi6G_save:disabled{opacity:.4;cursor:default}.Kwoi6G_discard:focus-visible,.Kwoi6G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Kwoi6G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Kwoi6G_field+.Kwoi6G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Kwoi6G_head{align-items:center;gap:8px;display:flex}.Kwoi6G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Kwoi6G_badges{align-items:center;gap:8px;display:inline-flex}.Kwoi6G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Kwoi6G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Kwoi6G_reset:disabled{cursor:default}.Kwoi6G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Kwoi6G_input,.Kwoi6G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_input:focus-visible,.Kwoi6G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Kwoi6G_input:disabled,.Kwoi6G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Kwoi6G_inputInvalid{border:1px solid var(--dsw-alias-state-error-primary,#b42318);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-state-error-primary,#b42318);outline-offset:1px;border-color:var(--dsw-alias-state-error-primary,#b42318)}.Kwoi6G_selectWrap{position:relative}.Kwoi6G_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.Kwoi6G_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.Kwoi6G_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Kwoi6G_selectChevronOpen{transform:rotate(180deg)}.Kwoi6G_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.Kwoi6G_selectPopupOpen{opacity:1;transform:none}.Kwoi6G_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.Kwoi6G_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.Kwoi6G_selectOption:hover,.Kwoi6G_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.Kwoi6G_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.Kwoi6G_invalid{color:var(--dsw-alias-state-error-primary,#b42318);margin:0;font-size:12px;line-height:1.5}.Kwoi6G_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Kwoi6G_card,.Kwoi6G_header,.Kwoi6G_mark,.Kwoi6G_chevron,.Kwoi6G_chevronOpen,.Kwoi6G_discard,.Kwoi6G_save,.Kwoi6G_selectChevron,.Kwoi6G_selectChevronOpen,.Kwoi6G_selectPopup{transition:none}}";
|
|
2118
2118
|
const tagId = "@linxin666/dsh-remote-web-ui/packages/dsh-remote-web-ui/src/client/settings-card.module.css";
|
|
2119
2119
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
2120
2120
|
const tag = document.createElement("style");
|
|
@@ -2145,6 +2145,7 @@ window.__ModuleLoader__.load({
|
|
|
2145
2145
|
"inputInvalid": "Kwoi6G_inputInvalid",
|
|
2146
2146
|
"invalid": "Kwoi6G_invalid",
|
|
2147
2147
|
"label": "Kwoi6G_label",
|
|
2148
|
+
"mark": "Kwoi6G_mark",
|
|
2148
2149
|
"name": "Kwoi6G_name",
|
|
2149
2150
|
"notExposed": "Kwoi6G_notExposed",
|
|
2150
2151
|
"pending": "Kwoi6G_pending",
|
|
@@ -2190,22 +2191,30 @@ window.__ModuleLoader__.load({
|
|
|
2190
2191
|
const cardClass = expanded ? `${settings_card_module_css_default.cardOpen} ${settings_card_module_css_default.card}` : settings_card_module_css_default.card;
|
|
2191
2192
|
const header = alwaysOpen === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2192
2193
|
className: settings_card_module_css_default.headerStatic,
|
|
2193
|
-
children: [
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
className: settings_card_module_css_default.
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2194
|
+
children: [
|
|
2195
|
+
props.icon !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2196
|
+
className: settings_card_module_css_default.mark,
|
|
2197
|
+
"aria-hidden": "true",
|
|
2198
|
+
children: props.icon
|
|
2199
|
+
}) : null,
|
|
2200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2201
|
+
className: settings_card_module_css_default.headText,
|
|
2202
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2203
|
+
className: settings_card_module_css_default.name,
|
|
2204
|
+
title,
|
|
2205
|
+
children: title
|
|
2206
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2207
|
+
className: settings_card_module_css_default.description,
|
|
2208
|
+
title: description,
|
|
2209
|
+
children: props.descriptionNode ?? description
|
|
2210
|
+
})]
|
|
2211
|
+
}),
|
|
2212
|
+
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2213
|
+
className: settings_card_module_css_default.pending,
|
|
2214
|
+
title: props.t("settings.unsaved"),
|
|
2215
|
+
children: props.t("settings.unsaved")
|
|
2216
|
+
}) : null
|
|
2217
|
+
]
|
|
2209
2218
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2210
2219
|
type: "button",
|
|
2211
2220
|
className: settings_card_module_css_default.header,
|
|
@@ -2215,6 +2224,11 @@ window.__ModuleLoader__.load({
|
|
|
2215
2224
|
setOpen(!open);
|
|
2216
2225
|
},
|
|
2217
2226
|
children: [
|
|
2227
|
+
props.icon !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2228
|
+
className: settings_card_module_css_default.mark,
|
|
2229
|
+
"aria-hidden": "true",
|
|
2230
|
+
children: props.icon
|
|
2231
|
+
}) : null,
|
|
2218
2232
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2219
2233
|
className: settings_card_module_css_default.headText,
|
|
2220
2234
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2663,6 +2677,8 @@ window.__ModuleLoader__.load({
|
|
|
2663
2677
|
disposeForm;
|
|
2664
2678
|
disposed = false;
|
|
2665
2679
|
saving = false;
|
|
2680
|
+
/** A save asked for while one was in flight; it runs once the first settles (#1754). */
|
|
2681
|
+
saveQueued = false;
|
|
2666
2682
|
failed = false;
|
|
2667
2683
|
failedReason;
|
|
2668
2684
|
/** @param scope - the bound configuration form for this card's namespace. */
|
|
@@ -2738,7 +2754,7 @@ window.__ModuleLoader__.load({
|
|
|
2738
2754
|
});
|
|
2739
2755
|
},
|
|
2740
2756
|
save: () => {
|
|
2741
|
-
this.
|
|
2757
|
+
this.requestSave();
|
|
2742
2758
|
},
|
|
2743
2759
|
discard: () => {
|
|
2744
2760
|
if (this.staged.size === 0 && !this.failed) return;
|
|
@@ -2765,6 +2781,29 @@ window.__ModuleLoader__.load({
|
|
|
2765
2781
|
* the user can correct them instead of retyping.
|
|
2766
2782
|
* @returns settlement after the mutation and the read-back.
|
|
2767
2783
|
*/
|
|
2784
|
+
/**
|
|
2785
|
+
* Run one save, and re-run it once if another was asked for while this one
|
|
2786
|
+
* was still in flight.
|
|
2787
|
+
*
|
|
2788
|
+
* A save is a Host round trip that also drives the profile reconcile, and
|
|
2789
|
+
* the Host runs that write inside one exclusive transaction. Answering a
|
|
2790
|
+
* press that arrives mid-flight by returning immediately dropped the edit
|
|
2791
|
+
* with no explanation, which is the "the save button stops working after a
|
|
2792
|
+
* few rounds" report (#1754). Serializing instead means a save pressed while
|
|
2793
|
+
* another is still settling runs against the settled state - which is what
|
|
2794
|
+
* the operator meant by pressing it again.
|
|
2795
|
+
* @returns settlement after the mutation and the read-back.
|
|
2796
|
+
*/
|
|
2797
|
+
async requestSave() {
|
|
2798
|
+
if (this.saving) {
|
|
2799
|
+
this.saveQueued = true;
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
await this.save();
|
|
2803
|
+
if (!this.saveQueued) return;
|
|
2804
|
+
this.saveQueued = false;
|
|
2805
|
+
await this.requestSave();
|
|
2806
|
+
}
|
|
2768
2807
|
async save() {
|
|
2769
2808
|
const plan = this.plan();
|
|
2770
2809
|
const valid = plan.filter((item) => item.judge !== void 0);
|
|
@@ -2978,6 +3017,7 @@ window.__ModuleLoader__.load({
|
|
|
2978
3017
|
t,
|
|
2979
3018
|
titleKey: "settings.title",
|
|
2980
3019
|
descriptionKey: "settings.description",
|
|
3020
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PhoneIcon, { size: 18 }),
|
|
2981
3021
|
defaultOpen: false,
|
|
2982
3022
|
state,
|
|
2983
3023
|
onSave: props.save,
|
|
@@ -3221,8 +3261,6 @@ window.__ModuleLoader__.load({
|
|
|
3221
3261
|
const zh = {
|
|
3222
3262
|
"entry.label": "远程访问",
|
|
3223
3263
|
"mobile.whale.open": "打开侧边栏",
|
|
3224
|
-
"mobile.composer.pickModel": "选择模型",
|
|
3225
|
-
"mobile.composer.pickEffort": "选择推理等级",
|
|
3226
3264
|
"title": "远程访问",
|
|
3227
3265
|
"subtitle": "通过手机或另一台电脑配对,远程使用同一份 Web 界面(官方界面 + 移动端适配)",
|
|
3228
3266
|
"card.title": "设备配对",
|
|
@@ -3232,7 +3270,7 @@ window.__ModuleLoader__.load({
|
|
|
3232
3270
|
"status.disconnected": "已配对设备离线",
|
|
3233
3271
|
"status.stopped": "已停止远程访问",
|
|
3234
3272
|
"status.lanRequired": "此功能需要局域网绑定或公网地址才能使用",
|
|
3235
|
-
"status.lanRequiredHint": "当前服务仅绑定在 127.0.0.1 且未配置公网地址,手机无法访问。请在本插件的设置卡片中打开“局域网访问”(装在全家桶里时在「设置 → Web 插件 →
|
|
3273
|
+
"status.lanRequiredHint": "当前服务仅绑定在 127.0.0.1 且未配置公网地址,手机无法访问。请在本插件的设置卡片中打开“局域网访问”(装在全家桶里时在「设置 → Web 插件 → 远程访问」,单独安装时在设置 → 插件 → 本插件行),或用 dsh web --host 0.0.0.0 重新启动,或填写内网穿透的公网地址。",
|
|
3236
3274
|
"status.loopbackRequired": "配对面板仅限本机使用",
|
|
3237
3275
|
"status.loopbackRequiredHint": "请通过 http://127.0.0.1 打开此页面后重试;手机请使用配对链接访问。",
|
|
3238
3276
|
"status.unreachable": "无法连接配对服务",
|
|
@@ -3288,8 +3326,8 @@ window.__ModuleLoader__.load({
|
|
|
3288
3326
|
"relay.registering": "正在把临时隧道同步到固定域名(<id>.dsh-market.com)…",
|
|
3289
3327
|
"relay.failed": "固定域名同步失败:{error}。正在自动重试,期间二维码可能使用临时地址。",
|
|
3290
3328
|
"close.label": "关闭远程访问面板",
|
|
3291
|
-
"settings.title": "
|
|
3292
|
-
"settings.description": "
|
|
3329
|
+
"settings.title": "远程访问",
|
|
3330
|
+
"settings.description": "手机配对、公网隧道与设备限额。",
|
|
3293
3331
|
"settings.enabled": "启用远程访问",
|
|
3294
3332
|
"settings.enabledHint": "关闭后移除侧边栏入口并停用配对路由与局域网栅栏。",
|
|
3295
3333
|
"settings.tokenTtlMs": "配对令牌有效期(毫秒)",
|
|
@@ -3343,8 +3381,6 @@ window.__ModuleLoader__.load({
|
|
|
3343
3381
|
const en = {
|
|
3344
3382
|
"entry.label": "Remote access",
|
|
3345
3383
|
"mobile.whale.open": "Open sidebar",
|
|
3346
|
-
"mobile.composer.pickModel": "Pick model",
|
|
3347
|
-
"mobile.composer.pickEffort": "Pick reasoning effort",
|
|
3348
3384
|
"title": "Remote access",
|
|
3349
3385
|
"subtitle": "Pair a phone or another computer to share the same Web GUI (official UI + mobile adaptation)",
|
|
3350
3386
|
"card.title": "Pair a device",
|
|
@@ -3410,8 +3446,8 @@ window.__ModuleLoader__.load({
|
|
|
3410
3446
|
"relay.registering": "Syncing the ephemeral tunnel to the stable hostname (<id>.dsh-market.com)…",
|
|
3411
3447
|
"relay.failed": "Stable-hostname sync failed: {error}. Retrying automatically; the QR may use the ephemeral address meanwhile.",
|
|
3412
3448
|
"close.label": "Close remote access panel",
|
|
3413
|
-
"settings.title": "Remote access
|
|
3414
|
-
"settings.description": "
|
|
3449
|
+
"settings.title": "Remote access",
|
|
3450
|
+
"settings.description": "Phone pairing, public tunnel and device limits.",
|
|
3415
3451
|
"settings.enabled": "Enable remote access",
|
|
3416
3452
|
"settings.enabledHint": "When off, the sidebar entry is removed and pairing routes plus the LAN fence stop.",
|
|
3417
3453
|
"settings.tokenTtlMs": "Pairing token lifetime (ms)",
|
|
@@ -4081,7 +4117,7 @@ window.__ModuleLoader__.load({
|
|
|
4081
4117
|
/** The building package's version, when the bundle carries it. */
|
|
4082
4118
|
function bakedVersion() {
|
|
4083
4119
|
try {
|
|
4084
|
-
return "0.4.
|
|
4120
|
+
return "0.4.5";
|
|
4085
4121
|
} catch {
|
|
4086
4122
|
return;
|
|
4087
4123
|
}
|
|
@@ -4155,14 +4191,18 @@ window.__ModuleLoader__.load({
|
|
|
4155
4191
|
const ACTIVE_CLASS = "dsh-remote-portrait";
|
|
4156
4192
|
/** Body class while the collapsed rail is hidden behind the whale. */
|
|
4157
4193
|
const RAIL_HIDDEN_CLASS = "dsh-remote-rail-hidden";
|
|
4194
|
+
/**
|
|
4195
|
+
* The official sidebar column root. Also the scope of the row sweep: every row
|
|
4196
|
+
* the drag suppression covers is a descendant of it, so the document-wide scan
|
|
4197
|
+
* it replaces only ever returned these.
|
|
4198
|
+
*/
|
|
4199
|
+
const SIDEBAR_SELECTOR = "[class*=\"_sidebarCol\"]";
|
|
4200
|
+
/** The official draggable session/project rows (see disableRowDrag). */
|
|
4201
|
+
const ROW_SELECTOR = "[class*=\"_sessionRow\"], [class*=\"_projectRow\"]";
|
|
4202
|
+
/** The injected stylesheet, addressed through the same cached lookup. */
|
|
4203
|
+
const STYLE_SELECTOR = `style[data-plugin-css="${ADAPT_CSS_ID}"]`;
|
|
4158
4204
|
/** Whale button id. */
|
|
4159
4205
|
const WHALE_ID = "dshRemoteWhale";
|
|
4160
|
-
/** Compact picker: synthesized model button id. */
|
|
4161
|
-
const MODEL_BTN_ID = "dshRemoteModelPick";
|
|
4162
|
-
/** Compact picker: synthesized effort button id. */
|
|
4163
|
-
const EFFORT_BTN_ID = "dshRemoteEffortPick";
|
|
4164
|
-
/** Body class while the compact picker buttons are wired. */
|
|
4165
|
-
const COMPACT_CLASS = "dsh-remote-compact-picker";
|
|
4166
4206
|
/** Body class while the header actions are seated in the tabs row. */
|
|
4167
4207
|
const HEADER_SEATED_CLASS = "dsh-remote-header-seated";
|
|
4168
4208
|
/**
|
|
@@ -4171,16 +4211,6 @@ window.__ModuleLoader__.load({
|
|
|
4171
4211
|
* reserving the space with padding.
|
|
4172
4212
|
*/
|
|
4173
4213
|
const HEADER_RESERVE_VAR = "--dsh-remote-header-actions-reserve";
|
|
4174
|
-
/** Locale-dependent fast path for the official picker cells (zh/en). */
|
|
4175
|
-
const PICKER_CELL_PATTERN = {
|
|
4176
|
-
model: /模型|Model/,
|
|
4177
|
-
effort: /推理等级|Reasoning|Effort/i
|
|
4178
|
-
};
|
|
4179
|
-
/** Position of each drill cell among the sheet's chevron cells. */
|
|
4180
|
-
const DRILL_INDEX = {
|
|
4181
|
-
model: 0,
|
|
4182
|
-
effort: 1
|
|
4183
|
-
};
|
|
4184
4214
|
/**
|
|
4185
4215
|
* The official application frame. The layout column classes are the anchor:
|
|
4186
4216
|
* `_frame` is shared by unrelated official components (the chat turn rail,
|
|
@@ -4256,12 +4286,6 @@ window.__ModuleLoader__.load({
|
|
|
4256
4286
|
"[class$=\"_composerSeat\"]{transform:none !important}",
|
|
4257
4287
|
"[class$=\"_composerSeat\"] [class$=\"_menu\"]{position:fixed !important;left:8px !important;right:8px !important;top:auto !important;bottom:calc(8px + env(safe-area-inset-bottom)) !important;width:auto !important;max-width:none !important;max-height:70dvh !important;overflow-y:auto !important;z-index:2147482000}",
|
|
4258
4288
|
"[class$=\"_composerSeat\"] [class$=\"_menu\"] [class$=\"_cell\"]{height:44px;min-height:44px;font-size:13px}",
|
|
4259
|
-
`body.${COMPACT_CLASS} [class$="_composerSeat"] [class$="_trailing"] [class$="_trigger"]:has([class$="_triggerEffort"]){display:none}`,
|
|
4260
|
-
`body.${COMPACT_CLASS} [class$="_composerSeat"] [class$="_trailing"]{flex-basis:auto;position:static;min-height:0;padding:0;width:0}`,
|
|
4261
|
-
`body.${COMPACT_CLASS} [class$="_composerSeat"] [class$="_trailing"] > [class$="_root"]:has([class$="_track"]){right:44px}`,
|
|
4262
|
-
`#${MODEL_BTN_ID},#${EFFORT_BTN_ID}{width:26px;height:32px;min-width:26px;padding:0;border-radius:9px;background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none;margin-left:4px}`,
|
|
4263
|
-
`#${MODEL_BTN_ID} svg,#${EFFORT_BTN_ID} svg{width:16px;height:16px;display:block}`,
|
|
4264
|
-
`#${MODEL_BTN_ID}:active,#${EFFORT_BTN_ID}:active{opacity:.7}`,
|
|
4265
4289
|
"[class$=\"_composerSeat\"] [data-slot=\"conversation.composer.dock\"] [class$=\"_root\"]{font-size:10px;white-space:normal;word-break:break-word;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-align:left;line-height:13px;letter-spacing:-0.2px;padding-left:0;padding-right:0}",
|
|
4266
4290
|
"[class$=\"_scrollBody\"] [class$=\"_root\"]{font-size:13px}",
|
|
4267
4291
|
"[class*=\"_bubble\"][role=\"tooltip\"]{display:none}",
|
|
@@ -4280,18 +4304,14 @@ window.__ModuleLoader__.load({
|
|
|
4280
4304
|
`body.${ACTIVE_CLASS} [data-dsh-plugin=\"pet\"],`,
|
|
4281
4305
|
`body.${ACTIVE_CLASS} [data-dsh-plugin=\"usage\"]{display:none !important}`,
|
|
4282
4306
|
`body.${ACTIVE_CLASS} [class$=\"_overlayLayer\"] [class$=\"_workbench\"]{display:none !important}`,
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4307
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"]{flex-direction:column;max-height:calc(100dvh - 32px)}`,
|
|
4308
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_nav"]{flex-direction:row;gap:4px;width:100%;padding:12px 12px 0;overflow-x:auto;overflow-y:hidden}`,
|
|
4309
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_navTitle"]{display:none}`,
|
|
4310
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_navList"]{flex-direction:row;gap:4px}`,
|
|
4311
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_navCell"]{height:34px;padding:0 12px;gap:6px;flex:none;border-radius:10px}`,
|
|
4312
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_navLabel"]{font-size:13px}`,
|
|
4313
|
+
`body.${ACTIVE_CLASS} [class$="_overlay"] [class$="_panel"] [class$="_content"]{flex:1;min-height:0}`
|
|
4290
4314
|
];
|
|
4291
|
-
/** Cube glyph for the compact model button (a plain box outline). */
|
|
4292
|
-
const CUBE_ICON = "<path d=\"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\"/><path d=\"m3.3 7 8.7 5 8.7-5\"/><path d=\"M12 22V12\"/>";
|
|
4293
|
-
/** Level glyph for the compact effort button (three rising bars). */
|
|
4294
|
-
const LEVELS_ICON = "<path d=\"M4 6h16\"/><path d=\"M7 12h10\"/><path d=\"M10 18h4\"/>";
|
|
4295
4315
|
/** The DeepSeek fish glyph (the official brand mark path). */
|
|
4296
4316
|
const FISH_PATH = "M22.9168 1.43018C22.6713 1.31018 22.5658 1.53918 22.4223 1.65519C22.3733 1.69269 22.3318 1.74169 22.2903 1.78669C21.9317 2.1697 21.5127 2.42121 20.9657 2.39121C20.1657 2.34621 19.4827 2.59771 18.8787 3.20973C18.7502 2.45521 18.3236 2.0047 17.6746 1.71569C17.3351 1.56568 16.9916 1.41518 16.7536 1.08867C16.5876 0.856163 16.5421 0.597155 16.4591 0.341647C16.4061 0.187643 16.3536 0.0301382 16.1761 0.00363739C15.9836 -0.0263635 15.9081 0.135141 15.8326 0.270145C15.5306 0.822162 15.4136 1.43018 15.4251 2.0462C15.4516 3.43174 16.0366 4.53527 17.1991 5.3203C17.3311 5.4103 17.3651 5.5003 17.3236 5.63181C17.2441 5.90231 17.1501 6.16482 17.0671 6.43533C17.0141 6.60784 16.9351 6.64584 16.7501 6.57033C16.1121 6.30383 15.5611 5.90931 15.074 5.4328C14.2475 4.63328 13.5 3.75075 12.568 3.05973C12.349 2.89822 12.13 2.74822 11.9034 2.60522C10.9524 1.68169 12.028 0.923165 12.277 0.833162C12.5375 0.739159 12.3675 0.41615 11.5259 0.42015C10.6844 0.42365 9.91439 0.705658 8.93286 1.08117C8.78935 1.13767 8.63835 1.17867 8.48384 1.21267C7.59332 1.04367 6.66829 1.00617 5.70226 1.11517C3.88321 1.31768 2.43016 2.1777 1.36213 3.64575C0.0790928 5.4103 -0.222916 7.41536 0.146595 9.50642C0.535106 11.7105 1.66014 13.535 3.38869 14.9616C5.18125 16.4406 7.24581 17.1657 9.60138 17.0266C11.0319 16.9441 12.6245 16.7526 14.421 15.2321C14.874 15.4576 15.3496 15.5476 16.1381 15.6151C16.7456 15.6716 17.3306 15.5851 17.7836 15.4911C18.4931 15.3411 18.4441 14.6841 18.1876 14.5636C16.1081 13.595 16.5646 13.9891 16.1496 13.67C17.2061 12.42 18.8202 10.1979 19.3182 7.17235C19.3672 6.83834 19.4297 6.36783 19.4222 6.09732C19.4182 5.93231 19.4562 5.86831 19.6447 5.84931C20.1657 5.78931 20.6712 5.64681 21.1357 5.3913C22.4833 4.65528 23.0268 3.44624 23.1548 1.9972C23.1738 1.77569 23.1508 1.54668 22.9168 1.43018Z";
|
|
4297
4317
|
/**
|
|
@@ -4322,6 +4342,8 @@ window.__ModuleLoader__.load({
|
|
|
4322
4342
|
let whaleObserver = null;
|
|
4323
4343
|
/** Header subtree observer: marks the geometry measurement dirty on re-render. */
|
|
4324
4344
|
let headerObserver = null;
|
|
4345
|
+
/** Document child-list observer: invalidates the cached-absent selector set. */
|
|
4346
|
+
let domObserver = null;
|
|
4325
4347
|
let observedHeader = null;
|
|
4326
4348
|
/** Whether the seated-actions geometry needs re-measuring (see alignActionsText). */
|
|
4327
4349
|
let headerGeometryDirty = true;
|
|
@@ -4340,16 +4362,19 @@ window.__ModuleLoader__.load({
|
|
|
4340
4362
|
* rail compaction) while the body class stays.
|
|
4341
4363
|
*/
|
|
4342
4364
|
function ensureAdaptStyle() {
|
|
4343
|
-
if (nodeOf(
|
|
4365
|
+
if (nodeOf(STYLE_SELECTOR) !== null) return;
|
|
4344
4366
|
const tag = document.createElement("style");
|
|
4345
4367
|
tag.dataset.plugin = "remote-web-ui";
|
|
4346
4368
|
tag.dataset.pluginCss = ADAPT_CSS_ID;
|
|
4347
4369
|
tag.textContent = ADAPT_CSS.join("");
|
|
4348
4370
|
document.head.appendChild(tag);
|
|
4371
|
+
nodeCache.set(STYLE_SELECTOR, tag);
|
|
4372
|
+
nodeMissCache.delete(STYLE_SELECTOR);
|
|
4349
4373
|
}
|
|
4350
4374
|
function apply() {
|
|
4351
4375
|
if (active) return;
|
|
4352
4376
|
active = true;
|
|
4377
|
+
forgetAbsentSelectors();
|
|
4353
4378
|
document.body.classList.add(ACTIVE_CLASS);
|
|
4354
4379
|
try {
|
|
4355
4380
|
w.__dshRemoteAdapt?.closeDetails?.();
|
|
@@ -4361,6 +4386,7 @@ window.__ModuleLoader__.load({
|
|
|
4361
4386
|
meta.setAttribute("content", `${savedViewportContent}, viewport-fit=cover`);
|
|
4362
4387
|
}
|
|
4363
4388
|
ensureWhale();
|
|
4389
|
+
ensureDomObserver();
|
|
4364
4390
|
syncWhale();
|
|
4365
4391
|
setWhaleTimer(true);
|
|
4366
4392
|
seatHeaderActions();
|
|
@@ -4370,7 +4396,6 @@ window.__ModuleLoader__.load({
|
|
|
4370
4396
|
active = false;
|
|
4371
4397
|
unseatHeaderActions();
|
|
4372
4398
|
restoreRowDrag();
|
|
4373
|
-
removeCompactPicker();
|
|
4374
4399
|
document.body.classList.remove(ACTIVE_CLASS);
|
|
4375
4400
|
document.body.classList.remove(RAIL_HIDDEN_CLASS);
|
|
4376
4401
|
document.querySelector(`style[data-plugin-css="${ADAPT_CSS_ID}"]`)?.remove();
|
|
@@ -4386,87 +4411,48 @@ window.__ModuleLoader__.load({
|
|
|
4386
4411
|
whaleObserver.disconnect();
|
|
4387
4412
|
whaleObserver = null;
|
|
4388
4413
|
}
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
if (on && whaleTimer === null && active) whaleTimer = window.setInterval(syncWhale, 600);
|
|
4393
|
-
else if (!on && whaleTimer !== null) {
|
|
4394
|
-
window.clearInterval(whaleTimer);
|
|
4395
|
-
whaleTimer = null;
|
|
4414
|
+
if (domObserver !== null) {
|
|
4415
|
+
domObserver.disconnect();
|
|
4416
|
+
domObserver = null;
|
|
4396
4417
|
}
|
|
4418
|
+
forgetAbsentSelectors();
|
|
4397
4419
|
}
|
|
4398
4420
|
/**
|
|
4399
|
-
*
|
|
4400
|
-
*
|
|
4401
|
-
*
|
|
4402
|
-
*
|
|
4403
|
-
*
|
|
4404
|
-
*
|
|
4405
|
-
* the
|
|
4421
|
+
* Observe the document for the two kinds of change that can make a cached-absent
|
|
4422
|
+
* target discoverable again (see nodeOf): node insertion/removal, and the
|
|
4423
|
+
* class / compat-stamp attributes the cached selectors match on.
|
|
4424
|
+
*
|
|
4425
|
+
* Body class writes are excluded by target: the layer toggles three body
|
|
4426
|
+
* classes every tick, and counting those would invalidate the negative cache
|
|
4427
|
+
* on every tick and restore the very scan this cache removes. They cannot
|
|
4428
|
+
* create a target either — every cached selector matches an element other
|
|
4429
|
+
* than <body>.
|
|
4430
|
+
*
|
|
4431
|
+
* The record queue is drained on a microtask, so this stays off the layout
|
|
4432
|
+
* path; a chat turn's insertions are a handful per second, not per element.
|
|
4406
4433
|
*/
|
|
4407
|
-
function
|
|
4408
|
-
document.body
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
function drillIntoPicker(kind) {
|
|
4413
|
-
const trigger = document.querySelector("[class$=\"_composerSeat\"] [class$=\"_trailing\"] [class$=\"_trigger\"]:has([class$=\"_triggerEffort\"])");
|
|
4414
|
-
if (trigger === null) return;
|
|
4415
|
-
trigger.click();
|
|
4416
|
-
let tries = 0;
|
|
4417
|
-
const tapCell = () => {
|
|
4418
|
-
tries += 1;
|
|
4419
|
-
const cells = Array.from(document.querySelectorAll("[class$=\"_composerSeat\"] [class$=\"_menu\"] [class$=\"_cell\"]"));
|
|
4420
|
-
const byLabel = cells.find((c) => PICKER_CELL_PATTERN[kind].test(c.textContent ?? ""));
|
|
4421
|
-
const drillable = cells.filter((c) => c.querySelector("[class*=\"_cellChevron\"], [class*=\"_chevron\"]"));
|
|
4422
|
-
const cell = byLabel ?? drillable[DRILL_INDEX[kind]];
|
|
4423
|
-
if (cell !== void 0) {
|
|
4424
|
-
cell.click();
|
|
4434
|
+
function ensureDomObserver() {
|
|
4435
|
+
if (domObserver !== null || typeof MutationObserver === "undefined" || !document.body) return;
|
|
4436
|
+
domObserver = new MutationObserver((records) => {
|
|
4437
|
+
for (const record of records) if (record.type === "childList" || record.target !== document.body) {
|
|
4438
|
+
noteDomChange();
|
|
4425
4439
|
return;
|
|
4426
4440
|
}
|
|
4427
|
-
if (tries < 8) window.setTimeout(tapCell, 150);
|
|
4428
|
-
};
|
|
4429
|
-
window.setTimeout(tapCell, 150);
|
|
4430
|
-
}
|
|
4431
|
-
function makeCompactButton(id, title, icon, kind) {
|
|
4432
|
-
const btn = document.createElement("button");
|
|
4433
|
-
btn.id = id;
|
|
4434
|
-
btn.type = "button";
|
|
4435
|
-
btn.dataset.dshPlugin = "remote-web-ui";
|
|
4436
|
-
btn.title = title;
|
|
4437
|
-
btn.setAttribute("aria-label", title);
|
|
4438
|
-
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${icon}</svg>`;
|
|
4439
|
-
btn.addEventListener("click", () => {
|
|
4440
|
-
drillIntoPicker(kind);
|
|
4441
4441
|
});
|
|
4442
|
-
|
|
4442
|
+
domObserver.observe(document.body, {
|
|
4443
|
+
childList: true,
|
|
4444
|
+
subtree: true,
|
|
4445
|
+
attributes: true,
|
|
4446
|
+
attributeFilter: ["class", "data-dsh-frame"]
|
|
4447
|
+
});
|
|
4443
4448
|
}
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
return;
|
|
4451
|
-
}
|
|
4452
|
-
if (document.getElementById(MODEL_BTN_ID) === null) tools.appendChild(makeCompactButton(MODEL_BTN_ID, surfaceText("mobile.composer.pickModel", "Pick model"), CUBE_ICON, "model"));
|
|
4453
|
-
if (document.getElementById(EFFORT_BTN_ID) === null) tools.appendChild(makeCompactButton(EFFORT_BTN_ID, surfaceText("mobile.composer.pickEffort", "Pick reasoning effort"), LEVELS_ICON, "effort"));
|
|
4454
|
-
for (const [id, key, fallback] of [[
|
|
4455
|
-
MODEL_BTN_ID,
|
|
4456
|
-
"mobile.composer.pickModel",
|
|
4457
|
-
"Pick model"
|
|
4458
|
-
], [
|
|
4459
|
-
EFFORT_BTN_ID,
|
|
4460
|
-
"mobile.composer.pickEffort",
|
|
4461
|
-
"Pick reasoning effort"
|
|
4462
|
-
]]) {
|
|
4463
|
-
const btn = document.getElementById(id);
|
|
4464
|
-
if (btn === null) continue;
|
|
4465
|
-
const label = surfaceText(key, fallback);
|
|
4466
|
-
btn.title = label;
|
|
4467
|
-
btn.setAttribute("aria-label", label);
|
|
4449
|
+
/** Start/stop the 600ms sync tick; a no-op when already in the asked state. */
|
|
4450
|
+
function setWhaleTimer(on) {
|
|
4451
|
+
if (on && whaleTimer === null && active) whaleTimer = window.setInterval(syncWhale, 600);
|
|
4452
|
+
else if (!on && whaleTimer !== null) {
|
|
4453
|
+
window.clearInterval(whaleTimer);
|
|
4454
|
+
whaleTimer = null;
|
|
4468
4455
|
}
|
|
4469
|
-
document.body.classList.add(COMPACT_CLASS);
|
|
4470
4456
|
}
|
|
4471
4457
|
/**
|
|
4472
4458
|
* The official sidebar toggle in the logo row. The row's own `_toggle` class
|
|
@@ -4634,7 +4620,7 @@ window.__ModuleLoader__.load({
|
|
|
4634
4620
|
const dragOverridden = /* @__PURE__ */ new Map();
|
|
4635
4621
|
function disableRowDrag() {
|
|
4636
4622
|
if (!active) return;
|
|
4637
|
-
const rows =
|
|
4623
|
+
const rows = sidebarRows();
|
|
4638
4624
|
for (const row of rows) if (row.getAttribute("draggable") !== "false") {
|
|
4639
4625
|
if (!dragOverridden.has(row)) dragOverridden.set(row, row.getAttribute("draggable"));
|
|
4640
4626
|
row.setAttribute("draggable", "false");
|
|
@@ -4655,14 +4641,83 @@ window.__ModuleLoader__.load({
|
|
|
4655
4641
|
* node on a major re-render, which the isConnected guard detects.
|
|
4656
4642
|
*/
|
|
4657
4643
|
const nodeCache = /* @__PURE__ */ new Map();
|
|
4644
|
+
/**
|
|
4645
|
+
* Selectors known to be absent as of {@link domGeneration}. A miss is the
|
|
4646
|
+
* common case, not the exception: the layer holds no overlay of its own, and
|
|
4647
|
+
* several official surfaces it looks for (tabs row, tools row, sidebar rows)
|
|
4648
|
+
* legitimately do not exist at once. Re-walking a conversation-sized document
|
|
4649
|
+
* for the same absent selector on every 600ms tick was waste that almost
|
|
4650
|
+
* always resolved to null: five document-wide lookups per tick on the measured
|
|
4651
|
+
* fixture, zero after this cache. That is a lookup count — the wall-clock
|
|
4652
|
+
* saving is real while the mounted content is quiet and shrinks to the
|
|
4653
|
+
* row-scope saving when a turn streams (see the Agent Note for the harness and
|
|
4654
|
+
* the before/after numbers).
|
|
4655
|
+
*
|
|
4656
|
+
* A miss is trusted only while the observed content has not changed, so a
|
|
4657
|
+
* surface that appears is still found on the very next tick — the same
|
|
4658
|
+
* discovery latency the unconditional probe had. The cache is never aged by a
|
|
4659
|
+
* clock: it is cleared by {@link noteDomChange} while the observer is live and
|
|
4660
|
+
* by {@link forgetAbsentSelectors} across the windows where it is not (apply
|
|
4661
|
+
* and revert). One blind spot is deliberate and load-bearing: the observer
|
|
4662
|
+
* watches <body>, so a head-resident target is not covered by that signal —
|
|
4663
|
+
* the one such target is seeded into the cache where it is created (see
|
|
4664
|
+
* ensureAdaptStyle).
|
|
4665
|
+
*/
|
|
4666
|
+
const nodeMissCache = /* @__PURE__ */ new Set();
|
|
4667
|
+
/**
|
|
4668
|
+
* Bumped whenever a cached target may have appeared or disappeared. A surface
|
|
4669
|
+
* qualifies on a class token or on the aggregate compat stamp
|
|
4670
|
+
* (`data-dsh-frame`), so both insertions/removals AND those two attributes
|
|
4671
|
+
* can change the answer. Nothing the layer writes per tick lands here: its own
|
|
4672
|
+
* class writes are all on <body>, and its row/transform/label writes touch
|
|
4673
|
+
* other attributes entirely (see ensureDomObserver).
|
|
4674
|
+
*/
|
|
4675
|
+
let domGeneration = 0;
|
|
4676
|
+
/** The generation the negative cache was recorded against. */
|
|
4677
|
+
let missGeneration = -1;
|
|
4678
|
+
function noteDomChange() {
|
|
4679
|
+
domGeneration += 1;
|
|
4680
|
+
}
|
|
4681
|
+
/**
|
|
4682
|
+
* Drop every cached "absent" verdict. Called on apply and revert: while the
|
|
4683
|
+
* observer is disconnected the layer is blind to insertions, so a verdict
|
|
4684
|
+
* recorded before the gap could otherwise outlive the change that falsified
|
|
4685
|
+
* it and be served without a probe. The positive cache needs no equivalent —
|
|
4686
|
+
* its `isConnected` guard already re-resolves a replaced node.
|
|
4687
|
+
*/
|
|
4688
|
+
function forgetAbsentSelectors() {
|
|
4689
|
+
nodeMissCache.clear();
|
|
4690
|
+
domGeneration += 1;
|
|
4691
|
+
}
|
|
4658
4692
|
function nodeOf(selector) {
|
|
4659
4693
|
const cached = nodeCache.get(selector);
|
|
4660
|
-
if (cached !== void 0
|
|
4694
|
+
if (cached !== void 0) {
|
|
4695
|
+
if (cached.isConnected) return cached;
|
|
4696
|
+
nodeCache.delete(selector);
|
|
4697
|
+
}
|
|
4698
|
+
if (missGeneration !== domGeneration) {
|
|
4699
|
+
nodeMissCache.clear();
|
|
4700
|
+
missGeneration = domGeneration;
|
|
4701
|
+
}
|
|
4702
|
+
if (nodeMissCache.has(selector)) return null;
|
|
4661
4703
|
const found = document.querySelector(selector);
|
|
4662
|
-
if (found === null)
|
|
4663
|
-
|
|
4704
|
+
if (found === null) {
|
|
4705
|
+
nodeMissCache.add(selector);
|
|
4706
|
+
return null;
|
|
4707
|
+
}
|
|
4708
|
+
nodeCache.set(selector, found);
|
|
4664
4709
|
return found;
|
|
4665
4710
|
}
|
|
4711
|
+
/**
|
|
4712
|
+
* The session/project rows the drag suppression covers, scoped to the cached
|
|
4713
|
+
* sidebar root. Each row carries the sidebar column class as an ancestor, so
|
|
4714
|
+
* the document-wide scan this replaces only ever returned rows from here.
|
|
4715
|
+
*/
|
|
4716
|
+
function sidebarRows() {
|
|
4717
|
+
const sidebar = nodeOf(SIDEBAR_SELECTOR);
|
|
4718
|
+
if (sidebar === null) return [];
|
|
4719
|
+
return Array.from(sidebar.querySelectorAll(ROW_SELECTOR));
|
|
4720
|
+
}
|
|
4666
4721
|
/** The official application frame, through the same cached lookup. */
|
|
4667
4722
|
function frameEl() {
|
|
4668
4723
|
const frame = nodeOf(APP_FRAME_SELECTOR);
|
|
@@ -4688,7 +4743,6 @@ window.__ModuleLoader__.load({
|
|
|
4688
4743
|
disableRowDrag();
|
|
4689
4744
|
seatHeaderActions();
|
|
4690
4745
|
alignActionsText();
|
|
4691
|
-
syncCompactPicker();
|
|
4692
4746
|
}
|
|
4693
4747
|
function seatHeaderActions() {
|
|
4694
4748
|
if (!active) return;
|