@yfwu2020/dsh-selection-explain 0.13.1 → 0.14.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/lib/client.js CHANGED
@@ -59,7 +59,8 @@ function selectionDetached(range) {
59
59
  function insideEditable(node) {
60
60
  var element = node && node.nodeType === 1 ? node : node && node.parentElement;
61
61
  if (!element || !element.closest) return false;
62
- return !!element.closest("input, textarea");
62
+ if (element.isContentEditable === true) return true;
63
+ return !!element.closest('input, textarea, [contenteditable]:not([contenteditable="false"])');
63
64
  }
64
65
  function formatDuration(ms) {
65
66
  return ms < 1e3 ? ms + "ms" : (ms / 1e3).toFixed(1) + "s";
@@ -983,6 +984,36 @@ function createSettingsController(env) {
983
984
  function createSelectionController(env) {
984
985
  var preferences = env.preferences || createPreferences();
985
986
  var checkPending = false;
987
+ var dismissedRange = null;
988
+ var floatInteracting = false;
989
+ var floatInteractionVersion = 0;
990
+ function beginFloatInteraction() {
991
+ floatInteracting = true;
992
+ floatInteractionVersion += 1;
993
+ }
994
+ function endFloatInteraction() {
995
+ if (!floatInteracting) return;
996
+ var version = floatInteractionVersion;
997
+ env.later(function() {
998
+ if (version !== floatInteractionVersion) return;
999
+ floatInteracting = false;
1000
+ syncPlatformSurface();
1001
+ }, 0);
1002
+ }
1003
+ function sameRange(a, b) {
1004
+ if (!a || !b || !a.startContainer || !b.startContainer) return false;
1005
+ return a.startContainer === b.startContainer && a.startOffset === b.startOffset && a.endContainer === b.endContainer && a.endOffset === b.endOffset;
1006
+ }
1007
+ function currentDocumentRange() {
1008
+ try {
1009
+ var selection = window.getSelection();
1010
+ if (!selection || !selection.rangeCount) return null;
1011
+ var range = selection.getRangeAt(0);
1012
+ return typeof range.cloneRange === "function" ? range.cloneRange() : range;
1013
+ } catch (error) {
1014
+ return null;
1015
+ }
1016
+ }
986
1017
  function scheduleCheck() {
987
1018
  if (checkPending) return;
988
1019
  checkPending = true;
@@ -1068,6 +1099,12 @@ function createSelectionController(env) {
1068
1099
  env.hideQuoteButton();
1069
1100
  return;
1070
1101
  }
1102
+ if (dismissedRange && !sameRange(range, dismissedRange)) dismissedRange = null;
1103
+ if (dismissedRange) {
1104
+ env.state.quoteSelection = null;
1105
+ env.hideQuoteButton();
1106
+ return;
1107
+ }
1071
1108
  var inPanel = insidePanelContent(range.startContainer) || insidePanelContent(range.endContainer);
1072
1109
  env.state.bridgeActive = false;
1073
1110
  if (!inPanel && (insideOwnUI(range.startContainer) || insideOwnUI(range.endContainer))) {
@@ -1138,6 +1175,11 @@ function createSelectionController(env) {
1138
1175
  hideButton();
1139
1176
  return;
1140
1177
  }
1178
+ if (dismissedRange && !sameRange(range, dismissedRange)) dismissedRange = null;
1179
+ if (dismissedRange) {
1180
+ hideButton();
1181
+ return;
1182
+ }
1141
1183
  var rects = range.getClientRects();
1142
1184
  var rect = rects && rects.length > 0 ? rects[rects.length - 1] : range.getBoundingClientRect();
1143
1185
  if (!rect || rect.width === 0 && rect.height === 0) {
@@ -1158,6 +1200,9 @@ function createSelectionController(env) {
1158
1200
  void env.button.offsetWidth;
1159
1201
  setButtonPop(true);
1160
1202
  }
1203
+ watchPlatformSurface();
1204
+ env.later(refreshPlacement, 0);
1205
+ env.later(refreshPlacement, 80);
1161
1206
  env.startMicWatch();
1162
1207
  }
1163
1208
  function hideButton() {
@@ -1165,18 +1210,142 @@ function createSelectionController(env) {
1165
1210
  setButtonPop(false);
1166
1211
  if (!env.vcardState.open) env.stopMicWatch();
1167
1212
  }
1213
+ function surfaceSnapshot() {
1214
+ if (typeof document === "undefined" || typeof document.querySelector !== "function") {
1215
+ return { rect: null, visible: false, dismissed: true };
1216
+ }
1217
+ var node = document.querySelector("[data-text-interaction-surface]");
1218
+ if (!node) return { rect: null, visible: false, dismissed: true };
1219
+ if (node.getAttribute("aria-hidden") === "true") return { rect: null, visible: false, dismissed: true };
1220
+ var view = node.ownerDocument && node.ownerDocument.defaultView;
1221
+ if (view && typeof view.getComputedStyle === "function") {
1222
+ try {
1223
+ if (view.getComputedStyle(node).visibility === "hidden") return { rect: null, visible: false, dismissed: false };
1224
+ } catch (error) {
1225
+ }
1226
+ }
1227
+ var box = typeof node.getBoundingClientRect === "function" ? node.getBoundingClientRect() : null;
1228
+ if (!box || !box.width || !box.height) return { rect: null, visible: false, dismissed: false };
1229
+ return { rect: box, visible: true, dismissed: false };
1230
+ }
1231
+ function platformSurface() {
1232
+ return surfaceSnapshot().rect;
1233
+ }
1168
1234
  function placeFloat(node, rect) {
1235
+ if (floatInteracting) return false;
1169
1236
  var wasHidden = node.style.display !== "inline-flex";
1170
1237
  node.style.display = "inline-flex";
1171
1238
  var width = node.offsetWidth || 62;
1172
- var height = node.offsetHeight || 28;
1173
- var left = clamp(rect.right - width, 8, Math.max(8, window.innerWidth - width - 8));
1174
- var top = rect.top - height - 7;
1175
- if (top < 8) top = Math.min(rect.bottom + 7, window.innerHeight - height - 8);
1239
+ var height = node.offsetHeight || 30;
1240
+ var surface = platformSurface();
1241
+ var left;
1242
+ var top;
1243
+ if (surface) {
1244
+ var gap = 8;
1245
+ left = surface.right + gap;
1246
+ top = surface.top + (surface.height - height) / 2;
1247
+ var dock = "right";
1248
+ if (left + width > window.innerWidth - 8) {
1249
+ left = surface.left - gap - width;
1250
+ dock = "left";
1251
+ if (left < 8) {
1252
+ left = clamp(surface.left, 8, Math.max(8, window.innerWidth - width - 8));
1253
+ top = surface.bottom + gap;
1254
+ dock = "below";
1255
+ }
1256
+ }
1257
+ node.setAttribute("data-dock", dock);
1258
+ } else {
1259
+ left = clamp(rect.right - width, 8, Math.max(8, window.innerWidth - width - 8));
1260
+ top = rect.top - height - 7;
1261
+ if (top < 8) top = Math.min(rect.bottom + 7, window.innerHeight - height - 8);
1262
+ node.removeAttribute("data-dock");
1263
+ }
1264
+ top = clamp(top, 8, Math.max(8, window.innerHeight - height - 8));
1176
1265
  node.style.left = Math.round(left) + "px";
1177
1266
  node.style.top = Math.round(top) + "px";
1178
1267
  return wasHidden;
1179
1268
  }
1269
+ function visibleFloat() {
1270
+ if (env.button.style.display === "inline-flex") {
1271
+ return { node: env.button, rect: env.state.selection && env.state.selection.rect || null };
1272
+ }
1273
+ if (env.quoteButton.style.display === "inline-flex") {
1274
+ return { node: env.quoteButton, rect: env.state.quoteSelection && env.state.quoteSelection.rect || null };
1275
+ }
1276
+ return null;
1277
+ }
1278
+ function refreshPlacement() {
1279
+ var current = visibleFloat();
1280
+ if (!current) return false;
1281
+ if (!current.rect && !platformSurface()) return false;
1282
+ placeFloat(current.node, current.rect);
1283
+ return true;
1284
+ }
1285
+ function followPlatformSurface() {
1286
+ if (!platformSurface()) return false;
1287
+ return refreshPlacement();
1288
+ }
1289
+ var surfaceObserver = null;
1290
+ var surfaceObserved = null;
1291
+ var surfaceVisible = false;
1292
+ var surfaceScrolledOut = false;
1293
+ function restoreFloating() {
1294
+ var stored = env.state.selection;
1295
+ if (!stored || !stored.rect || stored.source !== "document") return;
1296
+ var live = currentDocumentRange();
1297
+ if (!live || !sameRange(live, stored.range)) return;
1298
+ if (dismissedRange && sameRange(live, dismissedRange)) return;
1299
+ if (env.panelOpen) showQuoteButton(stored.rect);
1300
+ else showButton(stored.rect);
1301
+ }
1302
+ function watchPlatformSurface() {
1303
+ if (typeof MutationObserver !== "function") return;
1304
+ var node = typeof document !== "undefined" && typeof document.querySelector === "function" ? document.querySelector("[data-text-interaction-surface]") : null;
1305
+ if (!node) return;
1306
+ if (surfaceObserver && surfaceObserved === node && node.isConnected !== false) return;
1307
+ if (surfaceObserver) {
1308
+ try {
1309
+ surfaceObserver.disconnect();
1310
+ } catch (error) {
1311
+ }
1312
+ surfaceObserver = null;
1313
+ }
1314
+ surfaceObserved = node;
1315
+ surfaceVisible = surfaceSnapshot().visible;
1316
+ surfaceObserver = new MutationObserver(syncPlatformSurface);
1317
+ surfaceObserver.observe(node, {
1318
+ attributes: true,
1319
+ attributeFilter: ["aria-hidden", "style", "class"],
1320
+ childList: true
1321
+ });
1322
+ }
1323
+ function syncPlatformSurface() {
1324
+ if (floatInteracting) return;
1325
+ var next = surfaceSnapshot();
1326
+ var changed = next.visible !== surfaceVisible;
1327
+ surfaceVisible = next.visible;
1328
+ if (changed && !next.visible) {
1329
+ if (next.dismissed) {
1330
+ dismissedRange = currentDocumentRange();
1331
+ surfaceScrolledOut = false;
1332
+ } else {
1333
+ surfaceScrolledOut = true;
1334
+ }
1335
+ hideButton();
1336
+ env.hideQuoteButton();
1337
+ return;
1338
+ }
1339
+ if (changed && next.visible) {
1340
+ var restore = surfaceScrolledOut;
1341
+ surfaceScrolledOut = false;
1342
+ if (restore) {
1343
+ restoreFloating();
1344
+ return;
1345
+ }
1346
+ }
1347
+ refreshPlacement();
1348
+ }
1180
1349
  function setQuotePop(on) {
1181
1350
  if (on) env.quoteButton.setAttribute("data-pop", "1");
1182
1351
  else env.quoteButton.removeAttribute("data-pop");
@@ -1187,8 +1356,21 @@ function createSelectionController(env) {
1187
1356
  void env.quoteButton.offsetWidth;
1188
1357
  setQuotePop(true);
1189
1358
  }
1359
+ watchPlatformSurface();
1360
+ env.later(refreshPlacement, 0);
1361
+ env.later(refreshPlacement, 80);
1190
1362
  env.startMicWatch();
1191
1363
  }
1364
+ function dispose() {
1365
+ if (surfaceObserver) {
1366
+ try {
1367
+ surfaceObserver.disconnect();
1368
+ } catch (error) {
1369
+ }
1370
+ surfaceObserver = null;
1371
+ }
1372
+ surfaceObserved = null;
1373
+ }
1192
1374
  return {
1193
1375
  hideButton,
1194
1376
  showQuoteButton,
@@ -1198,7 +1380,12 @@ function createSelectionController(env) {
1198
1380
  scheduleCheck,
1199
1381
  panelSourceLabel,
1200
1382
  insidePanelContent,
1201
- turnLabelOf
1383
+ turnLabelOf,
1384
+ refreshPlacement,
1385
+ followPlatformSurface,
1386
+ beginFloatInteraction,
1387
+ endFloatInteraction,
1388
+ dispose
1202
1389
  };
1203
1390
  }
1204
1391
 
@@ -1217,11 +1404,15 @@ function describeContainer(element) {
1217
1404
  try {
1218
1405
  if (element.closest("pre") || element.closest("code")) return "\u4EE3\u7801\u5757";
1219
1406
  if (element.closest("table")) return "\u8868\u683C";
1220
- var message = element.closest('[data-message-role],[data-role],[data-testid*="message"],article');
1221
- if (message) {
1222
- var role = message.getAttribute("data-message-role") || message.getAttribute("data-role") || "";
1223
- if (role === "user") return "\u5BF9\u8BDD\u6D88\u606F\uFF08\u7528\u6237\uFF09";
1224
- if (role === "assistant") return "\u5BF9\u8BDD\u6D88\u606F\uFF08\u52A9\u624B\uFF09";
1407
+ if (element.closest('[data-message-role="user"],[data-role="user"],[data-chat-flow-kind="user"]')) return "\u5BF9\u8BDD\u6D88\u606F\uFF08\u7528\u6237\uFF09";
1408
+ if (element.closest(
1409
+ '[data-message-role="assistant"],[data-role="assistant"],[data-chat-flow-kind="assistant"],[data-chat-group-part="response"],[data-chat-group-part="reasoning"]'
1410
+ )) {
1411
+ return "\u5BF9\u8BDD\u6D88\u606F\uFF08\u52A9\u624B\uFF09";
1412
+ }
1413
+ if (element.closest(
1414
+ '[data-message-role],[data-role],[data-chat-flow-kind],[data-chat-group-part],[data-message-bubble],[data-testid*="message"],article'
1415
+ )) {
1225
1416
  return "\u5BF9\u8BDD\u6D88\u606F";
1226
1417
  }
1227
1418
  } catch (error) {
@@ -5156,6 +5347,7 @@ function createPanelController(env) {
5156
5347
  env.sendAsk(options.askFirst, null, refs, note);
5157
5348
  }
5158
5349
  function openPanelWithInner(text, context, label, anchor, keyContext, options) {
5350
+ env.cancelBackground();
5159
5351
  if (options && options.shotRef !== void 0) {
5160
5352
  openPanelForShot(anchor, options);
5161
5353
  return;
@@ -5477,15 +5669,6 @@ function createPanelController(env) {
5477
5669
  env.hideQuoteButton();
5478
5670
  env.state.quoteSelection = null;
5479
5671
  env.cancelVoice();
5480
- if (env.state.request) {
5481
- env.state.aborted = true;
5482
- env.state.request.abort();
5483
- env.state.request = null;
5484
- }
5485
- if (env.state.askRequest) {
5486
- env.state.stopped = true;
5487
- env.state.askRequest.abort();
5488
- }
5489
5672
  releaseRenderedContent();
5490
5673
  env.paintPill();
5491
5674
  env.syncRouteWatch();
@@ -7390,6 +7573,7 @@ function createHistoryController(env) {
7390
7573
  }
7391
7574
  }
7392
7575
  function applyHistoryEntry(entry, source) {
7576
+ env.cancelBackground();
7393
7577
  var parts = entry.parts || {};
7394
7578
  env.state.parts = { translation: parts.translation || "", detail: parts.detail || "" };
7395
7579
  var replayTurns = Array.isArray(entry.turns) ? entry.turns : [];
@@ -7812,6 +7996,24 @@ function createConversationController(env) {
7812
7996
  }
7813
7997
  return false;
7814
7998
  }
7999
+ function cancelBackground() {
8000
+ var request = env.state.request;
8001
+ if (request) {
8002
+ env.state.request = null;
8003
+ try {
8004
+ request.abort();
8005
+ } catch (error) {
8006
+ }
8007
+ }
8008
+ var ask2 = env.state.askRequest;
8009
+ if (ask2) {
8010
+ env.state.askRequest = null;
8011
+ try {
8012
+ ask2.abort();
8013
+ } catch (error) {
8014
+ }
8015
+ }
8016
+ }
7815
8017
  async function ask(question) {
7816
8018
  var generating = env.state.asking || env.state.phase === "loading" || env.state.phase === "streaming";
7817
8019
  if (generating) {
@@ -7919,7 +8121,7 @@ function createConversationController(env) {
7919
8121
  var requestKey = env.state.cacheKey;
7920
8122
  env.state.stopped = false;
7921
8123
  var askController = new AbortController();
7922
- env.state.askRequest = {
8124
+ var askHandle = {
7923
8125
  abort: function() {
7924
8126
  try {
7925
8127
  askController.abort();
@@ -7927,8 +8129,19 @@ function createConversationController(env) {
7927
8129
  }
7928
8130
  }
7929
8131
  };
8132
+ env.state.askRequest = askHandle;
7930
8133
  function finish(error) {
7931
8134
  env.timerScope.clear(timer);
8135
+ if (env.state.askRequest !== askHandle) {
8136
+ answerTurn.asking = false;
8137
+ answerTurn.streaming = false;
8138
+ if (env.state.askRequest === null) {
8139
+ env.state.asking = false;
8140
+ env.state.toolBusy = false;
8141
+ refreshAskState();
8142
+ }
8143
+ return;
8144
+ }
7932
8145
  env.state.asking = false;
7933
8146
  env.state.toolBusy = false;
7934
8147
  env.clearTransientNotice(answerTurn);
@@ -8001,6 +8214,7 @@ function createConversationController(env) {
8001
8214
  var decoder = new TextDecoder();
8002
8215
  var buffer = "";
8003
8216
  function handle(block) {
8217
+ if (env.state.askRequest !== askHandle) return;
8004
8218
  var lines = block.split("\n");
8005
8219
  for (var i = 0; i < lines.length; i++) {
8006
8220
  var line = lines[i];
@@ -8203,7 +8417,7 @@ function createConversationController(env) {
8203
8417
  env.state.cacheState = "miss";
8204
8418
  if (env.state.request) env.state.request.abort();
8205
8419
  var controller = new AbortController();
8206
- env.state.request = {
8420
+ var handle = {
8207
8421
  abort: function() {
8208
8422
  try {
8209
8423
  controller.abort();
@@ -8211,6 +8425,7 @@ function createConversationController(env) {
8211
8425
  }
8212
8426
  }
8213
8427
  };
8428
+ env.state.request = handle;
8214
8429
  env.state.raw = "";
8215
8430
  env.state.phase = "loading";
8216
8431
  env.state.error = "";
@@ -8244,7 +8459,7 @@ function createConversationController(env) {
8244
8459
  env.state.toolBusy = false;
8245
8460
  env.state.elapsed = Date.now() - env.state.startedAt;
8246
8461
  stopTicker();
8247
- env.state.request = null;
8462
+ if (env.state.request === handle) env.state.request = null;
8248
8463
  clearWaiting();
8249
8464
  env.paint();
8250
8465
  }
@@ -8265,7 +8480,7 @@ function createConversationController(env) {
8265
8480
  env.state.chars = env.state.raw.length;
8266
8481
  env.state.toolBusy = false;
8267
8482
  stopTicker();
8268
- env.state.request = null;
8483
+ if (env.state.request === handle) env.state.request = null;
8269
8484
  clearWaiting();
8270
8485
  if (cacheKey) env.cache.set(cacheKey, { parts: { ...env.state.parts }, turns: env.state.turns.slice() });
8271
8486
  if (env.state.turns === requestTurns && env.state.cacheKey === requestKey) env.saveHistory();
@@ -8273,6 +8488,7 @@ function createConversationController(env) {
8273
8488
  }
8274
8489
  var dirty = false;
8275
8490
  function handleEvent(block) {
8491
+ if (env.state.request !== handle) return;
8276
8492
  var lines = block.split("\n");
8277
8493
  for (var i = 0; i < lines.length; i++) {
8278
8494
  var line = lines[i];
@@ -8364,7 +8580,7 @@ function createConversationController(env) {
8364
8580
  if (!response.body || !response.body.getReader) {
8365
8581
  return response.text().then(function(text) {
8366
8582
  text.split("\n\n").forEach(handleEvent);
8367
- succeed();
8583
+ if (env.state.request === handle) succeed();
8368
8584
  });
8369
8585
  }
8370
8586
  var reader = response.body.getReader();
@@ -8374,7 +8590,7 @@ function createConversationController(env) {
8374
8590
  return reader.read().then(function(result) {
8375
8591
  if (result.done) {
8376
8592
  if (buffer.trim()) handleEvent(buffer);
8377
- if (env.state.phase !== "done" && env.state.phase !== "error") succeed();
8593
+ if (env.state.request === handle && env.state.phase !== "done" && env.state.phase !== "error") succeed();
8378
8594
  return;
8379
8595
  }
8380
8596
  buffer += decoder.decode(result.value, { stream: true });
@@ -8393,6 +8609,7 @@ function createConversationController(env) {
8393
8609
  }).catch(function(error) {
8394
8610
  if (error && error.name === "AbortError") {
8395
8611
  if (env.state.aborted) fail("\u4E2D\u6B62");
8612
+ else stopTicker();
8396
8613
  return;
8397
8614
  }
8398
8615
  fail(String(error && error.message || error));
@@ -8484,6 +8701,7 @@ function createConversationController(env) {
8484
8701
  refreshAskState,
8485
8702
  isGenerating,
8486
8703
  stopCurrent,
8704
+ cancelBackground,
8487
8705
  sendAsk,
8488
8706
  runRequest,
8489
8707
  showWaiting,
@@ -10123,6 +10341,9 @@ function createControllers(env) {
10123
10341
  get runRequest() {
10124
10342
  return conversationController.runRequest;
10125
10343
  },
10344
+ get cancelBackground() {
10345
+ return conversationController.cancelBackground;
10346
+ },
10126
10347
  get vcardHandoff() {
10127
10348
  return voiceCardController.vcardHandoff;
10128
10349
  },
@@ -10344,6 +10565,9 @@ function createControllers(env) {
10344
10565
  get clearWaiting() {
10345
10566
  return conversationController.clearWaiting;
10346
10567
  },
10568
+ get cancelBackground() {
10569
+ return conversationController.cancelBackground;
10570
+ },
10347
10571
  get showWaiting() {
10348
10572
  return conversationController.showWaiting;
10349
10573
  },
@@ -10771,10 +10995,20 @@ function bindClientEvents(env) {
10771
10995
  if (env.quoteButton.contains(target)) return;
10772
10996
  if (env.pill.contains(target)) return;
10773
10997
  env.historyController.hideHistoryList();
10998
+ if (target && typeof target.closest === "function" && target.closest("[data-text-interaction-surface]")) return;
10774
10999
  if (env.panel.contains(target)) return;
10775
11000
  env.selectionController.hideButton();
10776
11001
  env.voiceCardController.hideQuoteButton();
10777
11002
  }, true);
11003
+ var offFloatPointerDown = listen(window, "pointerdown", function(event) {
11004
+ if (event.button !== 0) return;
11005
+ if (env.button.contains(event.target) || env.quoteButton.contains(event.target)) {
11006
+ env.selectionController.beginFloatInteraction();
11007
+ }
11008
+ }, true);
11009
+ var offFloatPointerUp = listen(window, "pointerup", env.selectionController.endFloatInteraction, true);
11010
+ var offFloatPointerCancel = listen(window, "pointercancel", env.selectionController.endFloatInteraction, true);
11011
+ var offFloatBlur = listen(window, "blur", env.selectionController.endFloatInteraction);
10778
11012
  var offButtonDown = listen(env.button, "mousedown", function(event) {
10779
11013
  event.preventDefault();
10780
11014
  event.stopPropagation();
@@ -10798,8 +11032,10 @@ function bindClientEvents(env) {
10798
11032
  env.voiceCardController.hideQuoteButton();
10799
11033
  });
10800
11034
  var offScroll = listen(window, "scroll", function(event) {
10801
- env.selectionController.hideButton();
10802
- env.voiceCardController.hideQuoteButton();
11035
+ if (!env.selectionController.followPlatformSurface()) {
11036
+ env.selectionController.hideButton();
11037
+ env.voiceCardController.hideQuoteButton();
11038
+ }
10803
11039
  var target = event && event.target;
10804
11040
  var insideList = !!(target && target.nodeType && env.historyList.contains(target));
10805
11041
  if (env.historyList.style.display === "flex" && !insideList) env.historyController.hideHistoryList();
@@ -10881,8 +11117,10 @@ function bindClientEvents(env) {
10881
11117
  applyPreviewHeight(frame, frame.parentNode && frame.parentNode.parentNode);
10882
11118
  });
10883
11119
  var offResize = listen(window, "resize", function() {
10884
- env.selectionController.hideButton();
10885
- env.voiceCardController.hideQuoteButton();
11120
+ if (!env.selectionController.followPlatformSurface()) {
11121
+ env.selectionController.hideButton();
11122
+ env.voiceCardController.hideQuoteButton();
11123
+ }
10886
11124
  env.conversationController.autoGrowAskBox();
10887
11125
  env.panelController.keepInsideViewport();
10888
11126
  if (env.historyList.style.display === "flex") env.historyController.placeHistoryList();
@@ -11029,6 +11267,10 @@ function bindClientEvents(env) {
11029
11267
  offSelectionChange();
11030
11268
  offVoiceEscape();
11031
11269
  offMouseDown();
11270
+ offFloatPointerDown();
11271
+ offFloatPointerUp();
11272
+ offFloatPointerCancel();
11273
+ offFloatBlur();
11032
11274
  offButtonDown();
11033
11275
  offButtonClick();
11034
11276
  offQuoteDown();
@@ -11082,17 +11324,24 @@ function bindClientEvents(env) {
11082
11324
  var CSS = [
11083
11325
  // 浮现动效:从锚点(右下角 = 贴着选区那侧)弹性放大。
11084
11326
  // 初始 74% + 6px 下移,缓动带过冲;时长 240ms —— 超过 ~300ms 连续划选会显得拖沓。
11085
- ".dsh-sel-btn{transform-origin:100% 100%;position:fixed;display:none;align-items:center;gap:5px;height:28px;padding:0 11px;border:0;border-radius:999px;",
11086
- // 浮标按钮保持**原样**:跟随主题主色(默认蓝 #3b6ef5),不跟面板的青色统一
11087
- "background:var(--dsw-alias-button-primary-fill,#3b6ef5);color:var(--dsw-alias-label-primary-foreground,#fff);",
11088
- "font:500 12px/1 " + FONT + ";cursor:pointer;white-space:nowrap;user-select:none;",
11089
- "box-shadow:0 6px 20px rgba(0,0,0,.24);transition:transform .1s ease,filter .1s ease}",
11327
+ // 与 DSH 内置「引用文本」浮层同族:同一套设计变量、同一尺寸(30px 高 / 13px 字号 / 999 圆角)。
11328
+ // 两枚按钮并排时看起来是一条工具条;深浅色交给宿主的 token 自动切换。
11329
+ ".dsh-sel-btn{transform-origin:100% 100%;position:fixed;display:none;align-items:center;gap:6px;height:30px;padding:5px 10px;border:0;border-radius:999px;",
11330
+ "background:var(--dsw-alias-button-floating-fill,#fff);color:var(--dsw-alias-label-primary,#0f1115);",
11331
+ "font:400 13px/20px var(--dsw-font-family," + FONT + ");cursor:pointer;white-space:nowrap;user-select:none;",
11332
+ "box-shadow:var(--dsw-elevation-panel,0 0 0 .5px #0000001f,0 3px 8px #00000008,0 0 16px #00000005);",
11333
+ "transition:transform .1s ease,background .12s ease}",
11334
+ // 停靠方向决定浮现原点:从贴着内置浮层的那一侧长出来。
11335
+ '.dsh-sel-btn[data-dock="right"]{transform-origin:0% 100%}',
11336
+ '.dsh-sel-btn[data-dock="left"]{transform-origin:100% 100%}',
11337
+ '.dsh-sel-btn[data-dock="below"]{transform-origin:50% 0%}',
11090
11338
  "@keyframes dsh-sel-pop{from{opacity:0;transform:scale(.74) translateY(6px)}to{opacity:1;transform:none}}",
11091
11339
  // 只在"从隐藏到显示"那一次挂上这个类;animationend 后摘掉,
11092
11340
  // 否则 fill:both 会把 transform 钉在 none 上,:active 的按压缩放就永远不生效了
11093
11341
  // 用 data 属性而不是类名:类名不动,外面按 .dsh-sel-btn 找它/量它都不会被影响
11094
11342
  '.dsh-sel-btn[data-pop="1"]{animation:dsh-sel-pop .24s cubic-bezier(.34,1.56,.64,1) both}',
11095
- ".dsh-sel-btn:hover{filter:brightness(1.08)}",
11343
+ // 悬停也走内置浮层的那一档底色(以前是对主色蓝做 brightness,换成浅底会发灰)。
11344
+ ".dsh-sel-btn:hover{background:var(--dsw-alias-button-floating-hover,#f1f3f5)}",
11096
11345
  ".dsh-sel-btn:active{transform:scale(.97)}",
11097
11346
  // 图标:13px 画布 + **左移 1px**。
11098
11347
  // 为什么要有这个负外边距:画布是 16×16、图形墨迹只占中间 ~11px,左右各留 ~2px 空白,
@@ -12034,7 +12283,7 @@ function createClientView() {
12034
12283
  quoteButton.style.zIndex = String(Z_PANEL + 1);
12035
12284
  quoteButton.style.pointerEvents = "auto";
12036
12285
  quoteButton.appendChild(quoteIcon());
12037
- quoteButton.appendChild(el("span", null, "\u5F15\u7528"));
12286
+ quoteButton.appendChild(el("span", null, "\u5F15\u7528\u5230\u5C0F\u7A97"));
12038
12287
  layer.appendChild(quoteButton);
12039
12288
  var pill = el("div", "dsh-sel-pill");
12040
12289
  pill.setAttribute("role", "button");
@@ -12374,6 +12623,24 @@ function createTimerScope() {
12374
12623
 
12375
12624
  // src/client/index.js
12376
12625
  function apply(ctx) {
12626
+ var instanceId = "sel-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 8);
12627
+ try {
12628
+ if (typeof window !== "undefined" && typeof window.__dshSelectionExplainDispose === "function") {
12629
+ window.__dshSelectionExplainDispose();
12630
+ }
12631
+ } catch (error) {
12632
+ }
12633
+ function pruneForeignLayers() {
12634
+ if (typeof document === "undefined" || typeof document.querySelectorAll !== "function") return;
12635
+ var layers = document.querySelectorAll('.dsh-sel-layer[data-dsh-floating-owner="explanation"]');
12636
+ for (var index = 0; index < layers.length; index += 1) {
12637
+ var node = layers[index];
12638
+ if (node && node.getAttribute("data-dsh-sel-instance") !== instanceId && node.parentNode) {
12639
+ node.parentNode.removeChild(node);
12640
+ }
12641
+ }
12642
+ }
12643
+ pruneForeignLayers();
12377
12644
  var state = createClientState();
12378
12645
  var runtime = createPanelRuntime();
12379
12646
  var pillState = createPillState();
@@ -12382,6 +12649,14 @@ function apply(ctx) {
12382
12649
  var timerScope = createTimerScope();
12383
12650
  var view = createClientView();
12384
12651
  var { settingsSheet, modelMenu, settingsNote, settingsNoteText, settingsRail, settingsBody, pill, panel, settingsScrim, settingsButton, layer, body, translationSection, detailSection, button, quoteButton, askRow, quotesBox, expandButton, quote, retryButton, closeButton, askBox, pillName, pillMeta, pillCaret, pillStar, historyButton, chatLog, askSend, promoteButton, attachBox, attachNote, attachInput, modelPillName, modelPillTier, modelPill, webMode, cellMd, cellWeb, voiceActivityText, voiceActivity, voiceDot, voiceActionSlot, captureRow, askTools, askSpacer, micButton, waveSvg, voiceStop, voiceCancel, waveBars, prefSeg, settingsBack, settingsClose, settingsReset, addButton, head, gripCorner, gripRight, gripBottom, styleEl } = view;
12652
+ layer.setAttribute("data-dsh-sel-instance", instanceId);
12653
+ var foreignLayerObserver = null;
12654
+ if (typeof MutationObserver === "function" && typeof document !== "undefined" && document.body) {
12655
+ foreignLayerObserver = new MutationObserver(function() {
12656
+ pruneForeignLayers();
12657
+ });
12658
+ foreignLayerObserver.observe(document.body, { childList: true });
12659
+ }
12385
12660
  var { settingsController, selectionController, voiceCardController, quotesController, previewController, lightboxController, panelController, pillController, historyController, conversationController, attachController, shotController, modelsController, voiceController } = createControllers({
12386
12661
  get preferences() {
12387
12662
  return preferences;
@@ -12881,7 +13156,7 @@ function apply(ctx) {
12881
13156
  return head;
12882
13157
  }
12883
13158
  });
12884
- window.__dshSelectionExplain = {
13159
+ var debugHook = window.__dshSelectionExplain = {
12885
13160
  /** 截图提问的状态(调试/自动化)。 */
12886
13161
  shot: function() {
12887
13162
  return shotController.state();
@@ -13463,6 +13738,12 @@ function apply(ctx) {
13463
13738
  // ⚠️ 必须给数组:设置页渲染列表时会 `[...row.aliases]`,
13464
13739
  // 留成 undefined 会直接抛 TypeError,整个快捷键弹窗打不开(实测踩到)。
13465
13740
  aliases: [],
13741
+ // DSH 0.2.2 起这两个字段是命令契约的一部分(Web 运行时的分发与原生菜单都会读):
13742
+ // · regions:哪些输入区域允许触发。截图是全局动作,页面/输入框/终端都给。
13743
+ // · modals:允许在哪些模态浮层里触发;空数组 = 任何模态开着都不抢键。
13744
+ // 缺了它们桌面端还能靠 priority 短路"侥幸"可用,但 Web 端会直接抛 TypeError。
13745
+ regions: ["page", "editable", "terminal"],
13746
+ modals: [],
13466
13747
  // 键位是**物理键 + 修饰键**的结构(不是 "Mod+Shift+X" 这种字符串):
13467
13748
  // code 必须是 KeyA-Z / Digit0-9 / F1-24 这类物理 code,modifiers 是 meta/control/alt/shift/primary
13468
13749
  // (primary 在 macOS 上是 ⌘、Windows 上是 Ctrl)。
@@ -13497,7 +13778,10 @@ function apply(ctx) {
13497
13778
  if (typeof console !== "undefined" && console.log) {
13498
13779
  console.log("[dsh-selection-explain] \u5212\u8BCD\u89E3\u8BFB\u5DF2\u5C31\u7EEA\uFF08\u9009\u4E2D\u6587\u5B57\u8BD5\u8BD5\uFF09");
13499
13780
  }
13500
- return function() {
13781
+ var cleaned = false;
13782
+ function cleanup() {
13783
+ if (cleaned) return;
13784
+ cleaned = true;
13501
13785
  runtime.panelOpen = false;
13502
13786
  timerScope.dispose();
13503
13787
  cache.dispose();
@@ -13509,6 +13793,7 @@ function apply(ctx) {
13509
13793
  panelController.releaseRenderedContent();
13510
13794
  if (state.askRequest) state.askRequest.abort();
13511
13795
  clientEvents.dispose();
13796
+ selectionController.dispose();
13512
13797
  if (floatWatchId) clearInterval(floatWatchId);
13513
13798
  voiceController.cancelVoice();
13514
13799
  if (historyController.chatGrowWatch) historyController.chatGrowWatch.disconnect();
@@ -13525,6 +13810,13 @@ function apply(ctx) {
13525
13810
  pillState.pillObserver = null;
13526
13811
  }
13527
13812
  if (fullViewObserver) fullViewObserver.disconnect();
13813
+ if (foreignLayerObserver) {
13814
+ try {
13815
+ foreignLayerObserver.disconnect();
13816
+ } catch (error) {
13817
+ }
13818
+ foreignLayerObserver = null;
13819
+ }
13528
13820
  if (typeof offSlot === "function") offSlot();
13529
13821
  if (previewController.bridgeScanId) clearTimeout(previewController.bridgeScanId);
13530
13822
  previewController.bridgeScanId = 0;
@@ -13561,11 +13853,21 @@ function apply(ctx) {
13561
13853
  if (pill.parentNode) pill.parentNode.removeChild(pill);
13562
13854
  if (styleEl.parentNode) styleEl.parentNode.removeChild(styleEl);
13563
13855
  try {
13564
- delete window.__dshSelectionExplain;
13856
+ if (window.__dshSelectionExplain === debugHook) delete window.__dshSelectionExplain;
13565
13857
  } catch (error) {
13566
- window.__dshSelectionExplain = void 0;
13858
+ if (window.__dshSelectionExplain === debugHook) window.__dshSelectionExplain = void 0;
13567
13859
  }
13568
- };
13860
+ try {
13861
+ if (window.__dshSelectionExplainDispose === cleanup) delete window.__dshSelectionExplainDispose;
13862
+ } catch (error) {
13863
+ if (window.__dshSelectionExplainDispose === cleanup) window.__dshSelectionExplainDispose = void 0;
13864
+ }
13865
+ }
13866
+ try {
13867
+ window.__dshSelectionExplainDispose = cleanup;
13868
+ } catch (error) {
13869
+ }
13870
+ return cleanup;
13569
13871
  }, "@yfwu2020/dsh-selection-explain: \u5212\u8BCD\u89E3\u8BFB UI");
13570
13872
  }
13571
13873
  var inject = ["slots", "shortcuts"];