dsh-ui-tweaks 0.10.2 → 0.10.3

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 dsh-ui-tweaks contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-ui-tweaks contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/cordis.patch.yml CHANGED
@@ -1,12 +1,12 @@
1
- # dsh-ui-tweaks bundle patch
2
- #
3
- # Declared by `dsh.bundle.patch` in package.json: when the plugin is installed
4
- # through the official CLI —
5
- #
6
- # dsh plugin --profile <name> add dsh-ui-tweaks
7
- #
8
- # — the command appends `dsh-ui-tweaks` to the profile's bundle stack and this
9
- # patch mounts the server-side plugin row.
10
- - insert:
11
- - id: ui-tweaks
12
- name: 'dsh-ui-tweaks'
1
+ # dsh-ui-tweaks bundle patch
2
+ #
3
+ # Declared by `dsh.bundle.patch` in package.json: when the plugin is installed
4
+ # through the official CLI —
5
+ #
6
+ # dsh plugin --profile <name> add dsh-ui-tweaks
7
+ #
8
+ # — the command appends `dsh-ui-tweaks` to the profile's bundle stack and this
9
+ # patch mounts the server-side plugin row.
10
+ - insert:
11
+ - id: ui-tweaks
12
+ name: 'dsh-ui-tweaks'
package/lib/client.js CHANGED
@@ -107,27 +107,27 @@ function relativeTime(ms, now, formatter) {
107
107
  return formatter.format(Math.round(diffSeconds / 86400), 'day');
108
108
  return formatter.format(Math.round(diffSeconds / 604800), 'week');
109
109
  }
110
- exports.ARCHIVE_CSS = `
111
- .dut-arc{display:grid;gap:12px;max-width:680px;padding:6px 2px 36px}
112
- .dut-arc-head{display:flex;align-items:center;gap:10px;padding:14px 16px 0}
113
- .dut-arc-head h2{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
114
- .dut-arc-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);padding:2px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
115
- .dut-arc-spacer{flex:1}
116
- .dut-arc-btn{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}
117
- .dut-arc-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
118
- .dut-arc-btn:disabled{opacity:.5;cursor:default}
119
- .dut-arc-btn.dut-arc-del:hover{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
120
- .dut-arc-btn.dut-arc-confirm{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);font-weight:600}
121
- .dut-arc-body{display:grid;gap:2px;margin-top:4px}
122
- .dut-arc-row{display:flex;align-items:center;gap:8px;padding:10px 16px;border-radius:12px}
123
- .dut-arc-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
124
- .dut-arc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left;background:transparent;border:none;padding:0;cursor:default;color:var(--dsw-alias-label-primary);font:inherit}
125
- .dut-arc-title{font-size:13.5px;font-weight:600;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
126
- .dut-arc-sub{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
127
- .dut-arc-empty{padding:32px 16px;text-align:center;font-size:12.5px;color:var(--dsw-alias-label-secondary)}
128
- .dut-arc-alert{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
129
- .dut-arc-off{padding:20px 16px;display:grid;gap:12px}
130
- .dut-arc-off p{margin:0;font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary)}
110
+ exports.ARCHIVE_CSS = `
111
+ .dut-arc{display:grid;gap:12px;max-width:680px;padding:6px 2px 36px}
112
+ .dut-arc-head{display:flex;align-items:center;gap:10px;padding:14px 16px 0}
113
+ .dut-arc-head h2{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
114
+ .dut-arc-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);padding:2px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
115
+ .dut-arc-spacer{flex:1}
116
+ .dut-arc-btn{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}
117
+ .dut-arc-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
118
+ .dut-arc-btn:disabled{opacity:.5;cursor:default}
119
+ .dut-arc-btn.dut-arc-del:hover{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
120
+ .dut-arc-btn.dut-arc-confirm{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);font-weight:600}
121
+ .dut-arc-body{display:grid;gap:2px;margin-top:4px}
122
+ .dut-arc-row{display:flex;align-items:center;gap:8px;padding:10px 16px;border-radius:12px}
123
+ .dut-arc-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
124
+ .dut-arc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left;background:transparent;border:none;padding:0;cursor:default;color:var(--dsw-alias-label-primary);font:inherit}
125
+ .dut-arc-title{font-size:13.5px;font-weight:600;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
126
+ .dut-arc-sub{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
127
+ .dut-arc-empty{padding:32px 16px;text-align:center;font-size:12.5px;color:var(--dsw-alias-label-secondary)}
128
+ .dut-arc-alert{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
129
+ .dut-arc-off{padding:20px 16px;display:grid;gap:12px}
130
+ .dut-arc-off p{margin:0;font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary)}
131
131
  `;
132
132
  /** Install the Archive stylesheet once (idempotent); returns the disposer. */
133
133
  function installArchiveStyles() {
@@ -909,7 +909,12 @@ function warmGitStatus(target) {
909
909
  if (key === '' || snapshotCache.has(key) || warmInflight.has(key))
910
910
  return;
911
911
  const flight = apiGet(`${GIT_ROUTE}/status?${targetQuery(target)}`)
912
- .then(next => { snapshotCache.set(key, next); })
912
+ .then(next => {
913
+ // An unresolved answer (host still starting up) must not poison the
914
+ // cache as a final "not a repo"; the mounting chip re-fetches anyway.
915
+ if (!next.unresolved)
916
+ snapshotCache.set(key, next);
917
+ })
913
918
  .catch(() => {
914
919
  // Warmup is best-effort; the mounting chip re-fetches anyway.
915
920
  })
@@ -924,6 +929,12 @@ function GitWarmup({ sessionId }) {
924
929
  }, [sessionId]);
925
930
  return null;
926
931
  }
932
+ /** Fast retry steps (ms) for the startup race: right after a restart the host
933
+ * has not materialized its session/workspace store yet, so the first /status
934
+ * answers `unresolved`. Without these the UI sat blank for a full POLL_MS. */
935
+ const RETRY_DELAYS_MS = [400, 800, 1600, 3200];
936
+ /** Upper bound on fast retries before falling back to the steady poll. */
937
+ const FAST_RETRY_MAX = 6;
927
938
  /** Load the git snapshot on session change, then poll; returns [snapshot, refresh]. */
928
939
  function useGitStatus(enabled, target) {
929
940
  const key = target.session ?? target.ws ?? '';
@@ -937,27 +948,63 @@ function useGitStatus(enabled, target) {
937
948
  // A remount (panel open) must not regress to blank when the cache is warm.
938
949
  setSnapshot(snapshotCache.get(key) ?? null);
939
950
  let cancelled = false;
940
- let timer;
941
- const refresh = async () => {
951
+ let pollTimer;
952
+ let retryTimer;
953
+ let attempt = 0;
954
+ // Whether a resolved snapshot exists for this target (cache or fetched).
955
+ // Drives the cold-start chase: fast backoff only while it is false.
956
+ let haveGood = snapshotCache.has(key);
957
+ /** Store one fetch result. An `unresolved` answer is transient — never
958
+ * cached, never regressing an already-good snapshot. Returns whether a
959
+ * resolved answer landed. */
960
+ const apply = (next) => {
961
+ if (next.unresolved === true) {
962
+ if (!cancelled && !haveGood)
963
+ setSnapshot(null);
964
+ return false;
965
+ }
966
+ haveGood = true;
967
+ attempt = 0;
968
+ snapshotCache.set(key, next);
969
+ if (!cancelled)
970
+ setSnapshot(next);
971
+ return true;
972
+ };
973
+ const refreshOnce = async () => {
942
974
  try {
943
- const next = await apiGet(`${GIT_ROUTE}/status?${targetQuery(target)}`);
944
- snapshotCache.set(key, next);
945
- if (!cancelled)
946
- setSnapshot(next);
975
+ return apply(await apiGet(`${GIT_ROUTE}/status?${targetQuery(target)}`));
947
976
  }
948
977
  catch {
949
- // Transient git failure: keep the previous snapshot.
978
+ // Transport/transient git failure: chase it too while no good snapshot
979
+ // exists; afterwards keep showing the old one until the next poll.
980
+ return haveGood;
950
981
  }
951
982
  };
952
- void refresh();
953
- timer = window.setInterval(() => {
983
+ /** Cold-start chase: short backoff instead of idling a whole poll period. */
984
+ const scheduleRetry = () => {
985
+ if (cancelled || haveGood || attempt >= FAST_RETRY_MAX)
986
+ return;
987
+ const delay = RETRY_DELAYS_MS[Math.min(attempt, RETRY_DELAYS_MS.length - 1)] ?? 3200;
988
+ attempt += 1;
989
+ retryTimer = window.setTimeout(() => {
990
+ if (cancelled)
991
+ return;
992
+ void refreshOnce().then(settled => { if (!settled)
993
+ scheduleRetry(); });
994
+ }, delay);
995
+ };
996
+ void refreshOnce().then(settled => { if (!settled)
997
+ scheduleRetry(); });
998
+ pollTimer = window.setInterval(() => {
954
999
  if (document.visibilityState === 'visible')
955
- void refresh();
1000
+ void refreshOnce();
956
1001
  }, POLL_MS);
957
1002
  return () => {
958
1003
  cancelled = true;
959
- if (timer !== undefined)
960
- clearInterval(timer);
1004
+ if (pollTimer !== undefined)
1005
+ clearInterval(pollTimer);
1006
+ if (retryTimer !== undefined)
1007
+ clearTimeout(retryTimer);
961
1008
  };
962
1009
  }, [enabled, key]);
963
1010
  const refresh = (0, react_1.useCallback)(async () => {
@@ -965,8 +1012,12 @@ function useGitStatus(enabled, target) {
965
1012
  return;
966
1013
  try {
967
1014
  const next = await apiGet(`${GIT_ROUTE}/status?${targetQuery(target)}`);
968
- snapshotCache.set(key, next);
969
- setSnapshot(next);
1015
+ // Same rule as the hook body: an unresolved answer is transient and must
1016
+ // not evict a good snapshot or enter the cache.
1017
+ if (!next.unresolved) {
1018
+ snapshotCache.set(key, next);
1019
+ setSnapshot(next);
1020
+ }
970
1021
  }
971
1022
  catch {
972
1023
  // Keep the previous snapshot on transient failures.
@@ -1156,13 +1207,17 @@ function BranchChipEntry({ sessionId, sessionsService, controller, t }) {
1156
1207
  }, [branchOpen]);
1157
1208
  if (!enabled || sessionStr === undefined)
1158
1209
  return null;
1159
- if (snapshot !== null && !snapshot.isRepo)
1210
+ // A genuine non-repo directory hides the chip entirely. An UNRESOLVED target
1211
+ // (host still materializing sessions right after a restart) is transient:
1212
+ // fall through to the folder-only paint below instead of going blank.
1213
+ if (snapshot !== null && snapshot.unresolved !== true && !snapshot.isRepo)
1160
1214
  return null;
1161
- // While the first /status round-trip is in flight, paint the folder alone
1162
- // straight off the sessions-list feed (the sidebar's own rows zero
1163
- // network) so the seat never sits empty; the branch capsule joins the
1164
- // moment the usually-pre-warmed snapshot lands.
1165
- if (snapshot === null) {
1215
+ // While the first /status round-trip is in flight or while the host has
1216
+ // not resolved this session yet — paint the folder alone straight off the
1217
+ // sessions-list feed (the sidebar's own rows zero network, so it appears
1218
+ // the moment the sidebar row does); the branch capsule joins as soon as the
1219
+ // backoff chase lands a real snapshot.
1220
+ if (snapshot === null || snapshot.unresolved === true) {
1166
1221
  if (feedCwd === undefined || feedCwd === '')
1167
1222
  return null;
1168
1223
  return ((0, jsx_runtime_1.jsx)("span", { className: "gbar", "data-slot-plugin": "dsh-ui-tweaks-gitbar", children: (0, jsx_runtime_1.jsxs)("span", { className: "gbar-fchip", title: feedCwd, children: [(0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: (0, jsx_runtime_1.jsx)("path", { d: "M1.5 4A1.5 1.5 0 0 1 3 2.5h3l1.5 2H13A1.5 1.5 0 0 1 14.5 6v6A1.5 1.5 0 0 1 13 13.5H3A1.5 1.5 0 0 1 1.5 12V4Z" }) }), (0, jsx_runtime_1.jsx)("span", { className: "gbar-folder", children: basenameOf(feedCwd) })] }) }));
@@ -3296,63 +3351,63 @@ function linesToRecord(text, separator) {
3296
3351
  function recordToLines(record, separator) {
3297
3352
  return Object.entries(record).map(([key, value]) => `${key}${separator}${value}`).join('\n');
3298
3353
  }
3299
- exports.MCP_CSS = `
3300
- .dut-mcp{display:grid;gap:12px;max-width:680px;padding:6px 2px 36px}
3301
- .dut-mcp-head{display:flex;align-items:center;gap:10px;padding:14px 16px 0}
3302
- .dut-mcp-head h2{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
3303
- .dut-mcp-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);padding:2px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
3304
- .dut-mcp-spacer{flex:1}
3305
- .dut-mcp-btn{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}
3306
- .dut-mcp-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
3307
- .dut-mcp-btn:disabled{opacity:.5;cursor:default}
3308
- .dut-mcp-btn.dut-mcp-del:hover{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
3309
- .dut-mcp-btn.dut-mcp-confirm{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);font-weight:600}
3310
- .dut-mcp-body{display:grid;gap:2px;margin-top:4px}
3311
- .dut-mcp-row{display:grid;gap:6px;padding:10px 16px;border-radius:12px}
3312
- .dut-mcp-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
3313
- .dut-mcp-row-main{display:flex;align-items:center;gap:8px;min-width:0}
3314
- .dut-mcp-name{font-size:13.5px;font-weight:600;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
3315
- .dut-mcp-badge{flex:none;font-size:11px;line-height:1;padding:4px 9px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}
3316
- .dut-mcp-badge.dut-mcp-ok{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 12%,transparent);border-color:transparent;color:var(--dsw-alias-state-success-primary)}
3317
- .dut-mcp-badge.dut-mcp-err{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);border-color:transparent;color:var(--dsw-alias-state-error-primary)}
3318
- .dut-mcp-badge.dut-mcp-warn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 14%,transparent);border-color:transparent;color:var(--dsw-alias-state-warn-label)}
3319
- .dut-mcp-sub{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
3320
- .dut-mcp-toggle{flex:none;font-size:11.5px;color:var(--dsw-alias-label-tertiary);background:transparent;border:none;padding:0;cursor:pointer;text-align:left}
3321
- .dut-mcp-toggle:hover{color:var(--dsw-alias-label-primary)}
3322
- .dut-mcp-tools{margin-left:2px;display:flex;flex-wrap:wrap;gap:4px}
3323
- .dut-mcp-tool{font-size:11px;line-height:1;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}
3324
- .dut-mcp-note{margin:2px 16px 0;padding:8px 12px;border-radius:10px;font-size:11.5px;line-height:1.55;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
3325
- .dut-mcp-empty{padding:32px 16px;text-align:center;font-size:12.5px;color:var(--dsw-alias-label-secondary)}
3326
- .dut-mcp-alert{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
3327
- .dut-mcp-notice{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 12%,transparent);color:var(--dsw-alias-state-success-primary)}
3328
- .dut-mcp-off{padding:20px 16px;display:grid;gap:12px}
3329
- .dut-mcp-off p{margin:0;font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary)}
3330
- .dut-mcp-overlay{z-index:1100;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}
3331
- .dut-mcp-mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}
3332
- .dut-mcp-panel{z-index:1;background:var(--dsw-alias-bg-layer-2);box-sizing:border-box;width:720px;max-width:calc(100vw - 48px);height:min(700px,100vh - 48px);box-shadow:var(--dsw-shadow-lv3);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;display:flex;flex-direction:column;overflow:hidden}
3333
- .dut-mcp-panel-head{box-sizing:border-box;flex:none;display:flex;align-items:center;gap:10px;height:52px;padding:0 12px 0 20px}
3334
- .dut-mcp-panel-head h2{margin:0;font-size:16px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
3335
- .dut-mcp-panel-close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:transparent;border:none;border-radius:28px;display:inline-flex;justify-content:center;align-items:center;padding:0;font-size:13px}
3336
- .dut-mcp-panel-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
3337
- .dut-mcp-tabs{flex:none;display:inline-flex;padding:3px;gap:3px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-2);margin:0 20px 10px;align-self:flex-start}
3338
- .dut-mcp-tabs button{border:none;border-radius:7px;padding:5px 14px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;cursor:pointer}
3339
- .dut-mcp-tabs button.dut-mcp-tab-on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,transparent);color:var(--dsw-alias-state-business-primary);font-weight:600}
3340
- .dut-mcp-form{flex:1;min-height:0;overflow-y:auto;padding:0 20px 20px;display:grid;gap:12px;align-content:start}
3341
- .dut-mcp-field{display:grid;gap:6px}
3342
- .dut-mcp-field>span{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}
3343
- .dut-mcp-field small{font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
3344
- .dut-mcp-input{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border-radius:9px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
3345
- .dut-mcp-input:focus{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,transparent)}
3346
- .dut-mcp-input:disabled{opacity:.55;cursor:not-allowed}
3347
- textarea.dut-mcp-input{height:auto;min-height:72px;padding:8px 10px;line-height:1.5;resize:vertical;font-family:var(--dsw-font-markdown-code-block-font-family, Consolas, monospace);font-size:12px}
3348
- textarea.dut-mcp-input-lg{min-height:120px}
3349
- textarea.dut-mcp-yaml{min-height:320px}
3350
- .dut-mcp-row2{display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:start}
3351
- .dut-mcp-field .dut-seg{width:fit-content}
3352
- .dut-mcp-row2 .dut-seg{height:34px;box-sizing:border-box;align-items:center}
3353
- .dut-mcp-row2 .dut-seg button{flex:none;white-space:nowrap;padding:0 16px;height:26px}
3354
- .dut-mcp-foot{flex:none;display:flex;align-items:center;gap:8px;justify-content:flex-end;padding:12px 20px 16px;border-top:1px solid var(--dsw-alias-border-l1)}
3355
- .dut-mcp-error{margin:0 20px 10px;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
3354
+ exports.MCP_CSS = `
3355
+ .dut-mcp{display:grid;gap:12px;max-width:680px;padding:6px 2px 36px}
3356
+ .dut-mcp-head{display:flex;align-items:center;gap:10px;padding:14px 16px 0}
3357
+ .dut-mcp-head h2{margin:0;font-size:15px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
3358
+ .dut-mcp-count{font-size:11.5px;color:var(--dsw-alias-label-secondary);padding:2px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
3359
+ .dut-mcp-spacer{flex:1}
3360
+ .dut-mcp-btn{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}
3361
+ .dut-mcp-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
3362
+ .dut-mcp-btn:disabled{opacity:.5;cursor:default}
3363
+ .dut-mcp-btn.dut-mcp-del:hover{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
3364
+ .dut-mcp-btn.dut-mcp-confirm{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);color:var(--dsw-alias-state-error-primary);font-weight:600}
3365
+ .dut-mcp-body{display:grid;gap:2px;margin-top:4px}
3366
+ .dut-mcp-row{display:grid;gap:6px;padding:10px 16px;border-radius:12px}
3367
+ .dut-mcp-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
3368
+ .dut-mcp-row-main{display:flex;align-items:center;gap:8px;min-width:0}
3369
+ .dut-mcp-name{font-size:13.5px;font-weight:600;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
3370
+ .dut-mcp-badge{flex:none;font-size:11px;line-height:1;padding:4px 9px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}
3371
+ .dut-mcp-badge.dut-mcp-ok{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 12%,transparent);border-color:transparent;color:var(--dsw-alias-state-success-primary)}
3372
+ .dut-mcp-badge.dut-mcp-err{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);border-color:transparent;color:var(--dsw-alias-state-error-primary)}
3373
+ .dut-mcp-badge.dut-mcp-warn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary) 14%,transparent);border-color:transparent;color:var(--dsw-alias-state-warn-label)}
3374
+ .dut-mcp-sub{font-size:11.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
3375
+ .dut-mcp-toggle{flex:none;font-size:11.5px;color:var(--dsw-alias-label-tertiary);background:transparent;border:none;padding:0;cursor:pointer;text-align:left}
3376
+ .dut-mcp-toggle:hover{color:var(--dsw-alias-label-primary)}
3377
+ .dut-mcp-tools{margin-left:2px;display:flex;flex-wrap:wrap;gap:4px}
3378
+ .dut-mcp-tool{font-size:11px;line-height:1;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}
3379
+ .dut-mcp-note{margin:2px 16px 0;padding:8px 12px;border-radius:10px;font-size:11.5px;line-height:1.55;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
3380
+ .dut-mcp-empty{padding:32px 16px;text-align:center;font-size:12.5px;color:var(--dsw-alias-label-secondary)}
3381
+ .dut-mcp-alert{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
3382
+ .dut-mcp-notice{margin:8px 16px 0;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 12%,transparent);color:var(--dsw-alias-state-success-primary)}
3383
+ .dut-mcp-off{padding:20px 16px;display:grid;gap:12px}
3384
+ .dut-mcp-off p{margin:0;font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary)}
3385
+ .dut-mcp-overlay{z-index:1100;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}
3386
+ .dut-mcp-mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}
3387
+ .dut-mcp-panel{z-index:1;background:var(--dsw-alias-bg-layer-2);box-sizing:border-box;width:720px;max-width:calc(100vw - 48px);height:min(700px,100vh - 48px);box-shadow:var(--dsw-shadow-lv3);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;display:flex;flex-direction:column;overflow:hidden}
3388
+ .dut-mcp-panel-head{box-sizing:border-box;flex:none;display:flex;align-items:center;gap:10px;height:52px;padding:0 12px 0 20px}
3389
+ .dut-mcp-panel-head h2{margin:0;font-size:16px;font-weight:600;letter-spacing:-.01em;color:var(--dsw-alias-label-primary)}
3390
+ .dut-mcp-panel-close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:transparent;border:none;border-radius:28px;display:inline-flex;justify-content:center;align-items:center;padding:0;font-size:13px}
3391
+ .dut-mcp-panel-close:hover{background:var(--dsw-alias-interactive-bg-hover)}
3392
+ .dut-mcp-tabs{flex:none;display:inline-flex;padding:3px;gap:3px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-2);margin:0 20px 10px;align-self:flex-start}
3393
+ .dut-mcp-tabs button{border:none;border-radius:7px;padding:5px 14px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;cursor:pointer}
3394
+ .dut-mcp-tabs button.dut-mcp-tab-on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,transparent);color:var(--dsw-alias-state-business-primary);font-weight:600}
3395
+ .dut-mcp-form{flex:1;min-height:0;overflow-y:auto;padding:0 20px 20px;display:grid;gap:12px;align-content:start}
3396
+ .dut-mcp-field{display:grid;gap:6px}
3397
+ .dut-mcp-field>span{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}
3398
+ .dut-mcp-field small{font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
3399
+ .dut-mcp-input{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border-radius:9px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
3400
+ .dut-mcp-input:focus{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 55%,transparent)}
3401
+ .dut-mcp-input:disabled{opacity:.55;cursor:not-allowed}
3402
+ textarea.dut-mcp-input{height:auto;min-height:72px;padding:8px 10px;line-height:1.5;resize:vertical;font-family:var(--dsw-font-markdown-code-block-font-family, Consolas, monospace);font-size:12px}
3403
+ textarea.dut-mcp-input-lg{min-height:120px}
3404
+ textarea.dut-mcp-yaml{min-height:320px}
3405
+ .dut-mcp-row2{display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:start}
3406
+ .dut-mcp-field .dut-seg{width:fit-content}
3407
+ .dut-mcp-row2 .dut-seg{height:34px;box-sizing:border-box;align-items:center}
3408
+ .dut-mcp-row2 .dut-seg button{flex:none;white-space:nowrap;padding:0 16px;height:26px}
3409
+ .dut-mcp-foot{flex:none;display:flex;align-items:center;gap:8px;justify-content:flex-end;padding:12px 20px 16px;border-top:1px solid var(--dsw-alias-border-l1)}
3410
+ .dut-mcp-error{margin:0 20px 10px;padding:8px 12px;border-radius:10px;font-size:12px;line-height:1.5;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
3356
3411
  `;
3357
3412
  /** Install the MCP stylesheet once (idempotent); returns the disposer. */
3358
3413
  function installMcpStyles() {
@@ -4442,28 +4497,28 @@ const WHALE_VIEW_H = 17.04;
4442
4497
  // ---------------------------------------------------------------------------
4443
4498
  // Styles — resident (translucent, still) vs working (opaque, swimming).
4444
4499
  // ---------------------------------------------------------------------------
4445
- exports.WHALE_CSS = `
4446
- .duwi-row{box-sizing:border-box;width:100%;max-width:calc(var(--dsh-composer-card-max-width) + 2 * var(--dsh-composer-side-clearance, 0px));margin:0 auto;padding:0 calc(var(--dsh-composer-side-clearance, 0px) + 14px) 0 var(--dsh-composer-side-clearance, 0px);display:flex;align-items:flex-end;justify-content:flex-end;height:27px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
4447
- .duwi-stack{display:flex;flex-direction:column;align-items:center;transform:translateY(6px)}
4448
- .duwi-whale{display:block;color:var(--dsw-alias-label-primary);transform-origin:50% 60%;pointer-events:auto;cursor:pointer}
4449
- .duwi-row.duwi-swim .duwi-whale,.duwi-whale:hover{animation:duwi-swim 1.9s ease-in-out infinite}
4450
- .duwi-waves{display:block;width:26px;height:9px;margin-top:-3px;overflow:hidden}
4451
- .duwi-wave-back{stroke:#3d8bfd;opacity:.35}
4452
- .duwi-wave-front{stroke:#3d8bfd;opacity:.8}
4453
- .duwi-wave-front,.duwi-wave-back{animation:duwi-drift-front 1.9s linear infinite}
4454
- .duwi-wave-back{animation-duration:3.4s;animation-direction:reverse}
4455
- @keyframes duwi-swim{
4456
- 0%,100%{transform:translateY(0) rotate(-4deg)}
4457
- 50%{transform:translateY(-3.5px) rotate(4deg)}
4458
- }
4459
- @keyframes duwi-drift-front{
4460
- from{transform:translateX(0)}
4461
- to{transform:translateX(-8px)}
4462
- }
4463
- @media (prefers-reduced-motion:reduce){
4464
- .duwi-row.duwi-swim .duwi-whale,.duwi-whale:hover{animation:none}
4465
- .duwi-wave-front,.duwi-wave-back{animation:none}
4466
- }
4500
+ exports.WHALE_CSS = `
4501
+ .duwi-row{box-sizing:border-box;width:100%;max-width:calc(var(--dsh-composer-card-max-width) + 2 * var(--dsh-composer-side-clearance, 0px));margin:0 auto;padding:0 calc(var(--dsh-composer-side-clearance, 0px) + 14px) 0 var(--dsh-composer-side-clearance, 0px);display:flex;align-items:flex-end;justify-content:flex-end;height:27px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
4502
+ .duwi-stack{display:flex;flex-direction:column;align-items:center;transform:translateY(6px)}
4503
+ .duwi-whale{display:block;color:var(--dsw-alias-label-primary);transform-origin:50% 60%;pointer-events:auto;cursor:pointer}
4504
+ .duwi-row.duwi-swim .duwi-whale,.duwi-whale:hover{animation:duwi-swim 1.9s ease-in-out infinite}
4505
+ .duwi-waves{display:block;width:26px;height:9px;margin-top:-3px;overflow:hidden}
4506
+ .duwi-wave-back{stroke:#3d8bfd;opacity:.35}
4507
+ .duwi-wave-front{stroke:#3d8bfd;opacity:.8}
4508
+ .duwi-wave-front,.duwi-wave-back{animation:duwi-drift-front 1.9s linear infinite}
4509
+ .duwi-wave-back{animation-duration:3.4s;animation-direction:reverse}
4510
+ @keyframes duwi-swim{
4511
+ 0%,100%{transform:translateY(0) rotate(-4deg)}
4512
+ 50%{transform:translateY(-3.5px) rotate(4deg)}
4513
+ }
4514
+ @keyframes duwi-drift-front{
4515
+ from{transform:translateX(0)}
4516
+ to{transform:translateX(-8px)}
4517
+ }
4518
+ @media (prefers-reduced-motion:reduce){
4519
+ .duwi-row.duwi-swim .duwi-whale,.duwi-whale:hover{animation:none}
4520
+ .duwi-wave-front,.duwi-wave-back{animation:none}
4521
+ }
4467
4522
  `;
4468
4523
  /** Idempotent installer for the indicator styles (same pattern as the other features). */
4469
4524
  function installWhaleStyles() {