@yfwu2020/dsh-selection-explain 0.11.1 → 0.13.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/README.md +25 -5
- package/lib/chat-context.js +86 -0
- package/lib/chat-context.js.map +1 -0
- package/lib/client.js +1978 -168
- package/lib/client.js.map +4 -4
- package/lib/config-values.js +6 -0
- package/lib/config-values.js.map +1 -1
- package/lib/host/analyze.js +199 -47
- package/lib/host/analyze.js.map +1 -1
- package/lib/host/chat-context.js +160 -0
- package/lib/host/chat-context.js.map +1 -0
- package/lib/host/chat-materials.js +64 -0
- package/lib/host/chat-materials.js.map +1 -0
- package/lib/host/config.js +8 -1
- package/lib/host/config.js.map +1 -1
- package/lib/host/history-attachments.js +17 -53
- package/lib/host/history-attachments.js.map +1 -1
- package/lib/host/history-store.js +93 -68
- package/lib/host/history-store.js.map +1 -1
- package/lib/host/http.js +2 -1
- package/lib/host/http.js.map +1 -1
- package/lib/host/plugin.js +31 -0
- package/lib/host/plugin.js.map +1 -1
- package/lib/host/promote.js +0 -1
- package/lib/host/promote.js.map +1 -1
- package/lib/host/settings-store.js +17 -18
- package/lib/host/settings-store.js.map +1 -1
- package/lib/host/shot.js +968 -0
- package/lib/host/shot.js.map +1 -0
- package/lib/prompts.js +54 -1
- package/lib/prompts.js.map +1 -1
- package/lib/settings.js +14 -1
- package/lib/settings.js.map +1 -1
- package/lib/types/chat-context.d.ts +53 -0
- package/lib/types/config-values.d.ts +30 -0
- package/lib/types/host/chat-context.d.ts +31 -0
- package/lib/types/host/chat-materials.d.ts +16 -0
- package/lib/types/host/config.d.ts +16 -2
- package/lib/types/host/history-attachments.d.ts +7 -40
- package/lib/types/host/history-store.d.ts +1 -0
- package/lib/types/host/shot.d.ts +73 -0
- package/lib/types/host/types.d.ts +17 -0
- package/lib/types/prompts.d.ts +21 -0
- package/package.json +4 -2
package/lib/client.js
CHANGED
|
@@ -119,6 +119,9 @@ var HISTORY = "/selection-explain/api/history";
|
|
|
119
119
|
var MODELS = "/selection-explain/api/models";
|
|
120
120
|
var ATTACH = "/selection-explain/api/attach";
|
|
121
121
|
var ATTACHMENT = "/selection-explain/api/attachment";
|
|
122
|
+
var SHOT_START = "/selection-explain/api/shot/start";
|
|
123
|
+
var SHOT_PENDING = "/selection-explain/api/shot/pending";
|
|
124
|
+
var SHOT_IMAGE = "/selection-explain/api/shot/image";
|
|
122
125
|
var ATTACH_MAX_ITEMS = 10;
|
|
123
126
|
var ATTACH_MAX_ITEM_BYTES = 20 * 1024 * 1024;
|
|
124
127
|
var SETTINGS = "/selection-explain/api/settings";
|
|
@@ -151,7 +154,6 @@ var VOICE_SILENCE_RMS = 0.012;
|
|
|
151
154
|
var CONTEXT_WINDOW = 1500;
|
|
152
155
|
var KEY_CONTEXT_CHARS = 300;
|
|
153
156
|
var MAX_QUOTES = 4;
|
|
154
|
-
var MAX_QUOTE_CHARS = 3e3;
|
|
155
157
|
var MAX_QUOTE_SELECTION = 2e4;
|
|
156
158
|
var QUOTE_ONLY_QUESTION = "\u5C31\u4E0A\u9762\u5F15\u7528\u7684\u6587\u5B57\uFF0C\u8BF4\u8BF4\u5B83\u5728\u8FD9\u91CC\u662F\u4EC0\u4E48\u610F\u601D\u3002";
|
|
157
159
|
var QUOTE_CONTEXT_API = "/selection-explain/api/quote-context";
|
|
@@ -283,6 +285,9 @@ var CSS = [
|
|
|
283
285
|
// 可以自动换行、超过 84px 内部滚动,但**不显示滚动条**。
|
|
284
286
|
// ⚠️ flex:0 0 auto 不能省:滚动区是 flex 列,而 overflow 非 visible 的 flex 项目
|
|
285
287
|
// 自动最小高度是 0 → 内容一多就被压扁,文字直接看不全(实测踩过)。
|
|
288
|
+
// 顶部那行是"生成的标题"时:两行封顶,超出用省略号(而不是出现滚动条)
|
|
289
|
+
'.dsh-sel-quote[data-title="1"]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;',
|
|
290
|
+
"overflow:hidden;white-space:normal;max-height:none}",
|
|
286
291
|
".dsh-sel-quote{margin:0;padding:7px 11px;border-radius:10px;flex:0 0 auto;border-left:3px solid var(--sel-a1,#0d9488);",
|
|
287
292
|
"background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.11));color:var(--dsw-alias-label-secondary,inherit);",
|
|
288
293
|
"font-size:12.5px;line-height:1.6;box-sizing:border-box;max-width:100%;max-height:84px;",
|
|
@@ -459,6 +464,10 @@ var CSS = [
|
|
|
459
464
|
"display:grid;place-items:center;color:var(--dsw-alias-label-secondary,inherit)}",
|
|
460
465
|
".dsh-sel-thumb img{width:100%;height:100%;object-fit:cover;display:block}",
|
|
461
466
|
".dsh-sel-thumb svg{width:15px;height:15px;opacity:.55}",
|
|
467
|
+
// 可预览的缩略图:放大镜光标 + 键盘焦点环(它是 role=button,能 Tab 到)
|
|
468
|
+
'.dsh-sel-thumb[data-preview="1"]{cursor:zoom-in}',
|
|
469
|
+
'.dsh-sel-thumb[data-preview="1"]:focus-visible{outline:2px solid var(--sel-a1,#0d9488);outline-offset:1px}',
|
|
470
|
+
'.dsh-sel-thumb[data-preview="1"]:hover img{filter:brightness(1.06)}',
|
|
462
471
|
'.dsh-sel-thumb[data-state="error"]{border-color:color-mix(in srgb,var(--sel-bad,#e5534b) 70%,transparent)}',
|
|
463
472
|
'.dsh-sel-thumb[data-state="error"] svg{opacity:.9;color:var(--sel-bad,#e5534b)}',
|
|
464
473
|
// 转圈 + 不定态进度条:fetch 拿不到上传进度,所以**不假装知道百分比**
|
|
@@ -536,6 +545,50 @@ var CSS = [
|
|
|
536
545
|
"border-radius:5px;line-height:15px;background:var(--sel-fill,#0f766e);color:var(--sel-fill-fg,#fff)}",
|
|
537
546
|
".dsh-sel-bqfilename{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:190px}",
|
|
538
547
|
".dsh-sel-bqfilemeta{flex:0 0 auto;font-size:10.5px;opacity:.6}",
|
|
548
|
+
// ── 图片预览(灯箱)──
|
|
549
|
+
// 挂在 layer 上、铺满整扇窗口:小窗只有 540 宽,把截图塞回小窗里预览等于没放大。
|
|
550
|
+
// 层级压过面板与语音卡;pointer-events 必须显式打开 —— layer 自己是穿透的,
|
|
551
|
+
// 不写这一行的话灯箱看得见却点不着。
|
|
552
|
+
".dsh-sel-lightbox{position:fixed;inset:0;z-index:" + String(Z_PANEL + 2) + ";display:none;flex-direction:column;",
|
|
553
|
+
"align-items:center;justify-content:center;padding:48px 32px 34px;box-sizing:border-box;",
|
|
554
|
+
"background:rgba(9,11,13,.8);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);",
|
|
555
|
+
"pointer-events:auto;-webkit-app-region:no-drag;color:#fff;font:400 12.5px/1.5 " + FONT + "}",
|
|
556
|
+
'.dsh-sel-lightbox[data-open="1"]{display:flex;animation:dsh-sel-lightfade .12s ease}',
|
|
557
|
+
"@keyframes dsh-sel-lightfade{from{opacity:0}to{opacity:1}}",
|
|
558
|
+
// 顶栏:文件名(左,过长省略)+ 关闭键(右)。用 absolute 让它压在图片上方,
|
|
559
|
+
// 不用再给图片让出一条高度 —— 纵向空间本来就紧张。
|
|
560
|
+
".dsh-sel-lightbox-bar{position:absolute;top:0;left:0;right:0;height:44px;display:flex;align-items:center;gap:12px;",
|
|
561
|
+
"padding:0 14px 0 20px;box-sizing:border-box;background:linear-gradient(rgba(0,0,0,.5),transparent)}",
|
|
562
|
+
".dsh-sel-lightbox-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",
|
|
563
|
+
"font-size:12.5px;opacity:.9}",
|
|
564
|
+
// 计数(3 / 7):只在多图时出现,等宽数字 —— 翻页时数字不跟着左右抖。
|
|
565
|
+
".dsh-sel-lightbox-count{flex:0 0 auto;font-size:11.5px;opacity:.62;font-variant-numeric:tabular-nums;letter-spacing:.3px}",
|
|
566
|
+
'.dsh-sel-lightbox[data-gallery="0"] .dsh-sel-lightbox-count{display:none}',
|
|
567
|
+
".dsh-sel-lightbox-x{flex:0 0 auto;width:28px;height:28px;padding:0;border:0;border-radius:14px;cursor:pointer;",
|
|
568
|
+
"display:grid;place-items:center;background:rgba(255,255,255,.12);color:#fff;font:inherit;opacity:.9}",
|
|
569
|
+
".dsh-sel-lightbox-x:hover{background:rgba(255,255,255,.22);opacity:1}",
|
|
570
|
+
".dsh-sel-lightbox-x svg{width:14px;height:14px;display:block}",
|
|
571
|
+
".dsh-sel-lightbox-x:focus-visible{outline:2px solid #fff;outline-offset:2px}",
|
|
572
|
+
// 左右翻页:贴两侧垂直居中,和 ✕ 同一套半透明白底。
|
|
573
|
+
// 「到头」用 disabled 表示(22% 不透明)—— 小窗/窗口里没有滑动手势,全靠这两个键 + 方向键。
|
|
574
|
+
".dsh-sel-lightbox-nav{position:absolute;top:50%;margin-top:-20px;width:40px;height:40px;padding:0;border:0;border-radius:20px;",
|
|
575
|
+
"display:grid;place-items:center;cursor:pointer;background:rgba(255,255,255,.12);color:#fff;opacity:.9;",
|
|
576
|
+
"box-shadow:0 4px 16px rgba(0,0,0,.3);transition:background .12s ease,opacity .12s ease}",
|
|
577
|
+
".dsh-sel-lightbox-nav:hover:not(:disabled){background:rgba(255,255,255,.24);opacity:1}",
|
|
578
|
+
".dsh-sel-lightbox-nav:focus-visible{outline:2px solid #fff;outline-offset:2px}",
|
|
579
|
+
".dsh-sel-lightbox-nav:disabled{opacity:.22;cursor:default}",
|
|
580
|
+
'.dsh-sel-lightbox-nav[data-dir="prev"]{left:12px}',
|
|
581
|
+
'.dsh-sel-lightbox-nav[data-dir="next"]{right:12px}',
|
|
582
|
+
'.dsh-sel-lightbox[data-gallery="0"] .dsh-sel-lightbox-nav{display:none}',
|
|
583
|
+
// 舞台:占满可用高度,图 contain 到最大;点击图片本身不算"点空白",不会误关。
|
|
584
|
+
".dsh-sel-lightbox-stage{flex:1 1 auto;width:100%;min-height:0;display:flex;align-items:center;justify-content:center}",
|
|
585
|
+
".dsh-sel-lightbox-img{display:block;max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;",
|
|
586
|
+
"border-radius:10px;background:rgba(255,255,255,.04);box-shadow:0 24px 64px rgba(0,0,0,.55);cursor:default}",
|
|
587
|
+
'.dsh-sel-lightbox[data-error="1"] .dsh-sel-lightbox-img{display:none}',
|
|
588
|
+
".dsh-sel-lightbox-fail{display:none;margin:0;font-size:12.5px;opacity:.72}",
|
|
589
|
+
'.dsh-sel-lightbox[data-error="1"] .dsh-sel-lightbox-fail{display:block}',
|
|
590
|
+
".dsh-sel-lightbox-hint{margin:0;font-size:11px;opacity:.5}",
|
|
591
|
+
'@media (prefers-reduced-motion:reduce){.dsh-sel-lightbox[data-open="1"]{animation:none}}',
|
|
539
592
|
// 「引用整条」:贴在助手气泡末尾,**平时不出现**(正文优先),鼠标移到这条消息上才浮出来。
|
|
540
593
|
// 键盘 Tab 也能到(:focus-visible 同样点亮),触屏上另有浮标那条路。
|
|
541
594
|
".dsh-sel-bubquote{margin-top:7px;display:inline-flex;align-items:center;gap:4px;padding:2px 9px;",
|
|
@@ -693,6 +746,7 @@ var CSS = [
|
|
|
693
746
|
"background:var(--sel-warn-bg,rgba(255,166,87,.14));color:var(--sel-warn-fg,#b45309);font-size:11.5px;line-height:1.4}",
|
|
694
747
|
".dsh-sel-undo button{margin-left:auto;border:0;background:transparent;color:inherit;font:inherit;font-weight:600;cursor:pointer;text-decoration:underline}",
|
|
695
748
|
".dsh-sel-historytext{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
749
|
+
".dsh-sel-historysub{font-size:10.5px;line-height:1.4;opacity:.62;margin-top:1px;}",
|
|
696
750
|
".dsh-sel-historymeta{display:flex;align-items:center;gap:6px;font-size:11px;opacity:.5;font-variant-numeric:tabular-nums;min-width:0}",
|
|
697
751
|
".dsh-sel-historymeta>span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
698
752
|
".dsh-sel-historymeta .dsh-sel-historypin{opacity:.9}",
|
|
@@ -957,6 +1011,32 @@ var CSS = [
|
|
|
957
1011
|
".dsh-sel-uline{position:fixed;height:2px;border-radius:1px;pointer-events:none}",
|
|
958
1012
|
// 牵引线是 **SVG 贝塞尔**(原来是一根 2px 竖条 → 控制点和端点同 x,退化成直线,
|
|
959
1013
|
// 用户一眼看出"这不是贝塞尔")。现在两端各给一个反向的横向控制量,做出可见的 S 形。
|
|
1014
|
+
// ── 截图提问:选框(选区落在窗口内时)/ 贴边缩略图(落在窗口外时)──
|
|
1015
|
+
".dsh-sel-shotframe{position:fixed;z-index:2147482997;display:none;pointer-events:none;",
|
|
1016
|
+
"border:2px solid var(--sel-a1,#0d9488);border-radius:3px;",
|
|
1017
|
+
"box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 4px color-mix(in srgb,var(--sel-a1,#0d9488) 14%,transparent)}",
|
|
1018
|
+
'.dsh-sel-shotframe[data-show="1"]{display:block}',
|
|
1019
|
+
".dsh-sel-shotframe-tag{position:absolute;left:-2px;bottom:-19px;padding:1px 6px;border-radius:5px;",
|
|
1020
|
+
"background:var(--sel-fill,#0f766e);color:var(--sel-fill-fg,#fff);font-size:10px;line-height:1.6;white-space:nowrap}",
|
|
1021
|
+
".dsh-sel-shotchip{position:fixed;z-index:2147482997;display:none;align-items:center;gap:7px;box-sizing:border-box;",
|
|
1022
|
+
"padding:5px 9px 5px 5px;border-radius:11px;background:var(--dsw-alias-bg-base,#fff);",
|
|
1023
|
+
"border:.5px solid var(--dsw-alias-border-l3,rgba(140,140,140,.35));box-shadow:0 8px 22px rgba(0,0,0,.24)}",
|
|
1024
|
+
'.dsh-sel-shotchip[data-show="1"]{display:flex}',
|
|
1025
|
+
".dsh-sel-shotchip img{display:block;width:56px;height:32px;object-fit:cover;border-radius:6px;",
|
|
1026
|
+
"border:1.5px solid #e5484d;flex:0 0 auto;background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.14))}",
|
|
1027
|
+
".dsh-sel-shotchip-tx{display:flex;flex-direction:column;font-size:10.5px;line-height:1.35;white-space:nowrap;",
|
|
1028
|
+
"color:var(--dsw-alias-label-secondary,rgba(90,90,90,.9))}",
|
|
1029
|
+
".dsh-sel-shotchip-tx b{font-size:10.5px;font-weight:600;color:var(--dsw-alias-label-primary,inherit)}",
|
|
1030
|
+
// ── 截图失败的可见提示(Toast)──
|
|
1031
|
+
// 以前所有失败都只写进 /shot/status 诊断,用户按了快捷键没反应。这条浮层是唯一的用户可见面:
|
|
1032
|
+
// 位置放在底部中间(不挡卡片/面板),点击可立刻关掉,4.5 秒后自动消失。
|
|
1033
|
+
".dsh-sel-shot-toast{position:fixed;left:50%;bottom:76px;transform:translate(-50%,6px);z-index:2147483005;",
|
|
1034
|
+
"box-sizing:border-box;max-width:min(82vw,460px);padding:9px 14px;border-radius:10px;",
|
|
1035
|
+
"background:rgba(28,32,36,.95);color:#fff;font-size:12px;line-height:1.55;text-align:left;",
|
|
1036
|
+
"box-shadow:0 10px 28px rgba(0,0,0,.28);opacity:0;pointer-events:none;",
|
|
1037
|
+
"transition:opacity .16s ease,transform .16s ease}",
|
|
1038
|
+
'.dsh-sel-shot-toast[data-show="1"]{opacity:1;pointer-events:auto;transform:translate(-50%,0)}',
|
|
1039
|
+
'.dsh-sel-shot-toast[data-tone="error"]{background:rgba(153,27,27,.96)}',
|
|
960
1040
|
".dsh-sel-thread{position:fixed;left:0;top:0;width:100%;height:100%;pointer-events:none;",
|
|
961
1041
|
"z-index:2147482998;overflow:visible;display:none;opacity:0;transition:opacity .14s ease}",
|
|
962
1042
|
".dsh-sel-thread path{fill:none;stroke-width:1.6;stroke-dasharray:4 4;",
|
|
@@ -1836,6 +1916,13 @@ function adaptPreviewHtml(code, previewId) {
|
|
|
1836
1916
|
}
|
|
1837
1917
|
var previewSeq = 0;
|
|
1838
1918
|
var previewFrames = {};
|
|
1919
|
+
function releasePreviewFrames(root) {
|
|
1920
|
+
if (!root) return;
|
|
1921
|
+
for (var key in previewFrames) {
|
|
1922
|
+
var frame = previewFrames[key].frame;
|
|
1923
|
+
if (frame === root || root.contains && root.contains(frame)) delete previewFrames[key];
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1839
1926
|
function registerPreviewFrame(frame) {
|
|
1840
1927
|
previewSeq += 1;
|
|
1841
1928
|
var id = "pv" + previewSeq;
|
|
@@ -1919,6 +2006,7 @@ function webPreviewBlock(code) {
|
|
|
1919
2006
|
return { root: wrap, pre, frame };
|
|
1920
2007
|
}
|
|
1921
2008
|
function renderRich(parent2, text, options) {
|
|
2009
|
+
releasePreviewFrames(parent2);
|
|
1922
2010
|
parent2.textContent = "";
|
|
1923
2011
|
var lines = String(text || "").replace(/\r/g, "").split("\n");
|
|
1924
2012
|
var current = null;
|
|
@@ -2099,7 +2187,17 @@ function createSettingsController(env) {
|
|
|
2099
2187
|
var settingsRailButtons = [];
|
|
2100
2188
|
var settingsPanes = [];
|
|
2101
2189
|
var settingsSegs = [];
|
|
2102
|
-
var SLOT_LABEL = {
|
|
2190
|
+
var SLOT_LABEL = {
|
|
2191
|
+
off: "\u5173",
|
|
2192
|
+
low: "\u4F4E",
|
|
2193
|
+
high: "\u9AD8",
|
|
2194
|
+
max: "\u6700\u9AD8",
|
|
2195
|
+
// 截图取景范围(与推理档位共用 seg 标签表;值互不重叠)
|
|
2196
|
+
screen: "\u5168\u5C4F",
|
|
2197
|
+
window: "\u6574\u7A97",
|
|
2198
|
+
margin: "\u9009\u533A+\u7559\u767D",
|
|
2199
|
+
selection: "\u4EC5\u9009\u533A"
|
|
2200
|
+
};
|
|
2103
2201
|
var SLOT_ORDER = ["off", "low", "high", "max"];
|
|
2104
2202
|
var settingsEffortHints = [];
|
|
2105
2203
|
var settingsMenus = [];
|
|
@@ -3152,7 +3250,8 @@ function createVoiceCardController(env) {
|
|
|
3152
3250
|
var vcardHandoff = null;
|
|
3153
3251
|
var panelGrowTimer = null;
|
|
3154
3252
|
var panelGrowHandler = null;
|
|
3155
|
-
var quoteVoiceHandled
|
|
3253
|
+
var quoteVoiceHandled;
|
|
3254
|
+
var voiceSeenSinceOpen = false;
|
|
3156
3255
|
var selMark = null;
|
|
3157
3256
|
var selThread = null;
|
|
3158
3257
|
var selThreadPath = null;
|
|
@@ -3259,7 +3358,7 @@ function createVoiceCardController(env) {
|
|
|
3259
3358
|
}
|
|
3260
3359
|
function unpinVCardBox() {
|
|
3261
3360
|
if (vcardState.unpinTimer) {
|
|
3262
|
-
|
|
3361
|
+
env.timerScope.clear(vcardState.unpinTimer);
|
|
3263
3362
|
vcardState.unpinTimer = null;
|
|
3264
3363
|
}
|
|
3265
3364
|
if (vcard && vcardState.unpinHandler) {
|
|
@@ -3273,7 +3372,7 @@ function createVoiceCardController(env) {
|
|
|
3273
3372
|
function scheduleVCardUnpin() {
|
|
3274
3373
|
if (!vcard) return;
|
|
3275
3374
|
if (vcardState.unpinTimer) {
|
|
3276
|
-
|
|
3375
|
+
env.timerScope.clear(vcardState.unpinTimer);
|
|
3277
3376
|
vcardState.unpinTimer = null;
|
|
3278
3377
|
}
|
|
3279
3378
|
if (vcardState.unpinHandler) {
|
|
@@ -3361,11 +3460,13 @@ function createVoiceCardController(env) {
|
|
|
3361
3460
|
function openVCard() {
|
|
3362
3461
|
if (vcardState.open || env.panelOpen) return;
|
|
3363
3462
|
if (!env.state.selection) return;
|
|
3364
|
-
if (!env.state.selection.range && env.state.selection.source !== "iframe") return;
|
|
3365
|
-
if (!hasFocusEvidence(env.state.selection)) return;
|
|
3366
|
-
if (env.isTypingTarget(document.activeElement)) return;
|
|
3463
|
+
if (!env.state.selection.range && env.state.selection.source !== "iframe" && env.state.selection.source !== "shot") return;
|
|
3464
|
+
if (env.state.selection.source !== "shot" && !hasFocusEvidence(env.state.selection)) return;
|
|
3465
|
+
if (env.state.selection.source !== "shot" && env.isTypingTarget(document.activeElement)) return;
|
|
3466
|
+
if (env.ensureLayer) env.ensureLayer();
|
|
3367
3467
|
ensureVCard();
|
|
3368
3468
|
if (vcardState.closing || vcardState.crtTimer) finishCrtOff();
|
|
3469
|
+
voiceSeenSinceOpen = false;
|
|
3369
3470
|
vcardState.range = env.state.selection.range || null;
|
|
3370
3471
|
vcardState.anchorRect = env.state.selection.rect || null;
|
|
3371
3472
|
var geo = vcardGeometry();
|
|
@@ -3410,7 +3511,7 @@ function createVoiceCardController(env) {
|
|
|
3410
3511
|
vcard.setAttribute("data-voiced", on ? "1" : "0");
|
|
3411
3512
|
if (!vcardBox) return;
|
|
3412
3513
|
if (String(vcardBox.value || "").trim()) return;
|
|
3413
|
-
vcardBox.placeholder = on ? "\u6B63\u5728\u542C\u2026" : env.settingsLive.voiceCancelOnSilence !== false ? "\u6B63\u5728\u5173\u95ED\u2026" : "\u76F4\u63A5\u6253\u5B57\u4E5F\u884C\u2026";
|
|
3514
|
+
vcardBox.placeholder = on ? "\u6B63\u5728\u542C\u2026" : isShotSelection() ? "\u5C31\u8FD9\u5F20\u622A\u56FE\u63D0\u95EE\u2026" : env.settingsLive.voiceCancelOnSilence !== false ? "\u6B63\u5728\u5173\u95ED\u2026" : "\u76F4\u63A5\u6253\u5B57\u4E5F\u884C\u2026";
|
|
3414
3515
|
}
|
|
3415
3516
|
function focusVCard() {
|
|
3416
3517
|
if (!vcardBox) return;
|
|
@@ -3582,7 +3683,7 @@ function createVoiceCardController(env) {
|
|
|
3582
3683
|
line.setAttribute("data-off", "a");
|
|
3583
3684
|
var done = function() {
|
|
3584
3685
|
if (vcardState.crtTimer) {
|
|
3585
|
-
|
|
3686
|
+
env.timerScope.clear(vcardState.crtTimer);
|
|
3586
3687
|
vcardState.crtTimer = null;
|
|
3587
3688
|
}
|
|
3588
3689
|
if (vcard && vcardState.crtHandler) {
|
|
@@ -3601,7 +3702,7 @@ function createVoiceCardController(env) {
|
|
|
3601
3702
|
function finishCrtOff() {
|
|
3602
3703
|
vcardState.closing = false;
|
|
3603
3704
|
if (vcardState.crtTimer) {
|
|
3604
|
-
|
|
3705
|
+
env.timerScope.clear(vcardState.crtTimer);
|
|
3605
3706
|
vcardState.crtTimer = null;
|
|
3606
3707
|
}
|
|
3607
3708
|
if (vcard && vcardState.crtHandler) {
|
|
@@ -3643,14 +3744,22 @@ function createVoiceCardController(env) {
|
|
|
3643
3744
|
vcardState.settleStableAt = 0;
|
|
3644
3745
|
vcardState.settleDeadline = 0;
|
|
3645
3746
|
}
|
|
3747
|
+
function isShotSelection() {
|
|
3748
|
+
var sel = env.state && env.state.selection;
|
|
3749
|
+
return !!(sel && sel.source === "shot");
|
|
3750
|
+
}
|
|
3646
3751
|
function finishVoiceSettle(text) {
|
|
3647
3752
|
if (!vcardState.open) return;
|
|
3648
3753
|
var spoken = String(text || "").trim();
|
|
3649
|
-
if (spoken && env.settingsLive.voiceAutoSend !== false) {
|
|
3754
|
+
if (spoken && voiceSeenSinceOpen && env.settingsLive.voiceAutoSend !== false) {
|
|
3650
3755
|
sendVCard();
|
|
3651
3756
|
return;
|
|
3652
3757
|
}
|
|
3653
3758
|
if (!spoken && env.settingsLive.voiceCancelOnSilence !== false) {
|
|
3759
|
+
if (isShotSelection()) {
|
|
3760
|
+
markVCardListening(false);
|
|
3761
|
+
return;
|
|
3762
|
+
}
|
|
3654
3763
|
closeVCard(true);
|
|
3655
3764
|
return;
|
|
3656
3765
|
}
|
|
@@ -3680,6 +3789,7 @@ function createVoiceCardController(env) {
|
|
|
3680
3789
|
}
|
|
3681
3790
|
function closeVCard(animate) {
|
|
3682
3791
|
if (!vcardState.open) return;
|
|
3792
|
+
if (isShotSelection() && env.clearShotMarkers) env.clearShotMarkers();
|
|
3683
3793
|
vcardState.open = false;
|
|
3684
3794
|
if (vcardBox && !String(vcardBox.value || "").trim()) vcardBox.placeholder = "\u6B63\u5728\u5173\u95ED\u2026";
|
|
3685
3795
|
vcardState.range = null;
|
|
@@ -3707,6 +3817,36 @@ function createVoiceCardController(env) {
|
|
|
3707
3817
|
closeVCard();
|
|
3708
3818
|
return;
|
|
3709
3819
|
}
|
|
3820
|
+
if (selection.source === "shot") {
|
|
3821
|
+
var shotRect = selection.rect;
|
|
3822
|
+
var openShotPanel = function(ref) {
|
|
3823
|
+
if (!env.state.selection || env.state.selection !== selection) return;
|
|
3824
|
+
var why = env.shotFailed ? env.shotFailed() : "";
|
|
3825
|
+
if (!ref && why) {
|
|
3826
|
+
if (env.shotNotice) {
|
|
3827
|
+
env.shotNotice(
|
|
3828
|
+
why === "upload-failed" ? "\u622A\u56FE\u4E0A\u4F20\u5931\u8D25\uFF0C\u53EF\u4EE5\u518D\u6309\u4E00\u6B21\u53D1\u9001\u91CD\u8BD5" : "\u622A\u56FE\u6CA1\u6709\u751F\u6210\u6210\u529F\uFF0C\u8FD9\u6761\u5148\u4E0D\u53D1\u9001\uFF1B\u8BF7\u6309 Esc \u540E\u91CD\u65B0\u622A\u56FE"
|
|
3829
|
+
);
|
|
3830
|
+
}
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3833
|
+
vcardHandoff = vcardRect();
|
|
3834
|
+
closeVCard();
|
|
3835
|
+
env.openPanelWith("", "", "\u622A\u56FE", shotRect, "", {
|
|
3836
|
+
askFirst: asked,
|
|
3837
|
+
shotRef: ref,
|
|
3838
|
+
// 取景范围要跟着图一起进小窗:提示词要按"图里有没有红框 / 图是不是只有选区"换口径
|
|
3839
|
+
shotScope: selection.shotScope || "",
|
|
3840
|
+
shotFrame: selection.shotFrame !== false
|
|
3841
|
+
});
|
|
3842
|
+
if (env.clearShotMarkers) env.clearShotMarkers();
|
|
3843
|
+
env.state.selection = null;
|
|
3844
|
+
env.state.bridgeActive = false;
|
|
3845
|
+
};
|
|
3846
|
+
if (env.shotRefAsync) env.shotRefAsync().then(openShotPanel);
|
|
3847
|
+
else openShotPanel(selection.ref || null);
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3710
3850
|
var contextInfo = selection.source === "iframe" || !selection.range ? {
|
|
3711
3851
|
context: String(selection.context || ""),
|
|
3712
3852
|
keyContext: String(selection.keyContext || ""),
|
|
@@ -3755,14 +3895,13 @@ function createVoiceCardController(env) {
|
|
|
3755
3895
|
function startMicWatch() {
|
|
3756
3896
|
if (vcardState.timer) return;
|
|
3757
3897
|
if (!env.state.selection && !env.state.quoteSelection) return;
|
|
3758
|
-
vcardState.timer =
|
|
3759
|
-
env.timers.push(vcardState.timer);
|
|
3898
|
+
vcardState.timer = env.timerScope.interval(micTick, MIC_POLL_MS);
|
|
3760
3899
|
micTick();
|
|
3761
3900
|
}
|
|
3762
3901
|
function stopMicWatch() {
|
|
3763
3902
|
quoteVoiceHandled = false;
|
|
3764
3903
|
if (!vcardState.timer) return;
|
|
3765
|
-
|
|
3904
|
+
env.timerScope.clear(vcardState.timer);
|
|
3766
3905
|
vcardState.timer = null;
|
|
3767
3906
|
}
|
|
3768
3907
|
function micTick() {
|
|
@@ -3788,6 +3927,7 @@ function createVoiceCardController(env) {
|
|
|
3788
3927
|
return;
|
|
3789
3928
|
}
|
|
3790
3929
|
if (vcardState.suppress) return;
|
|
3930
|
+
voiceSeenSinceOpen = true;
|
|
3791
3931
|
stopVoiceSettle();
|
|
3792
3932
|
if (!vcardState.open) openVCard();
|
|
3793
3933
|
else markVCardListening(true);
|
|
@@ -3809,7 +3949,7 @@ function createVoiceCardController(env) {
|
|
|
3809
3949
|
var selection = env.state.quoteSelection;
|
|
3810
3950
|
if (!env.panelOpen || !selection) return;
|
|
3811
3951
|
if (!hasFocusEvidence(selection)) return;
|
|
3812
|
-
if (env.isTypingTarget(document.activeElement)) return;
|
|
3952
|
+
if (env.state.selection.source !== "shot" && env.isTypingTarget(document.activeElement)) return;
|
|
3813
3953
|
if (env.voice && env.voice.phase !== "idle") return;
|
|
3814
3954
|
env.addQuote(selection.text, selection.label, env.quoteContextFor(selection));
|
|
3815
3955
|
hideQuoteButton();
|
|
@@ -3882,8 +4022,6 @@ function createQuotesController(env) {
|
|
|
3882
4022
|
function addQuote(text, label, extra) {
|
|
3883
4023
|
var clean = String(text || "").replace(/\u00a0/g, " ").trim();
|
|
3884
4024
|
if (!clean) return false;
|
|
3885
|
-
var truncated = clean.length > MAX_QUOTE_CHARS;
|
|
3886
|
-
if (truncated) clean = clean.slice(0, MAX_QUOTE_CHARS) + "\u2026";
|
|
3887
4025
|
for (var i = 0; i < env.state.quotes.length; i += 1) {
|
|
3888
4026
|
if (env.state.quotes[i].text === clean) {
|
|
3889
4027
|
env.setStatus("\u8FD9\u6BB5\u5DF2\u7ECF\u5728\u5F15\u7528\u91CC\u4E86");
|
|
@@ -3896,19 +4034,27 @@ function createQuotesController(env) {
|
|
|
3896
4034
|
}
|
|
3897
4035
|
quoteSeq += 1;
|
|
3898
4036
|
var context = extra && typeof extra.context === "string" ? extra.context.trim() : "";
|
|
3899
|
-
|
|
4037
|
+
var quote = {
|
|
3900
4038
|
id: quoteSeq,
|
|
3901
4039
|
label: label || "\u5F15\u7528",
|
|
3902
4040
|
text: clean,
|
|
3903
4041
|
context,
|
|
3904
|
-
session: !!(extra && extra.session === true)
|
|
3905
|
-
|
|
4042
|
+
session: !!(extra && extra.session === true),
|
|
4043
|
+
sessionId: extra && extra.sessionId || env.currentSessionId(),
|
|
4044
|
+
contextJob: null
|
|
4045
|
+
};
|
|
4046
|
+
env.state.quotes.push(quote);
|
|
4047
|
+
if (quote.session && quote.sessionId) {
|
|
4048
|
+
quote.contextJob = fetchQuoteContext(quote.sessionId, quote.text).then(function(context2) {
|
|
4049
|
+
if (context2) quote.context = context2;
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
3906
4052
|
renderQuotes();
|
|
3907
4053
|
env.refreshAskState();
|
|
3908
4054
|
env.askRow.style.display = "";
|
|
3909
4055
|
if (env.composerVisible()) env.focusAskBox();
|
|
3910
4056
|
env.setStatus(
|
|
3911
|
-
"\u5DF2\u52A0\u5165\u5F15\u7528\uFF08" + quoteLabelOf(env.state.quotes[env.state.quotes.length - 1]) + " \xB7 " + clean.length + " \u5B57" + (
|
|
4057
|
+
"\u5DF2\u52A0\u5165\u5F15\u7528\uFF08" + quoteLabelOf(env.state.quotes[env.state.quotes.length - 1]) + " \xB7 " + clean.length + " \u5B57" + (context ? " \xB7 \u5E26\u4E0A\u4E0B\u6587 " + context.length + " \u5B57" : "") + "\uFF09"
|
|
3912
4058
|
);
|
|
3913
4059
|
return true;
|
|
3914
4060
|
}
|
|
@@ -3973,6 +4119,22 @@ function createQuotesController(env) {
|
|
|
3973
4119
|
function quoteContextFor(selection) {
|
|
3974
4120
|
if (!selection) return { context: "", session: false };
|
|
3975
4121
|
if (selection.source === "panel") {
|
|
4122
|
+
var index = selection.turnIndex;
|
|
4123
|
+
var through = env.state.contextState && env.state.contextState.through || 0;
|
|
4124
|
+
var visibleBefore = env.state.turns.slice(0, index).filter(function(t) {
|
|
4125
|
+
return !t.hidden;
|
|
4126
|
+
}).length;
|
|
4127
|
+
if (index >= 0 && visibleBefore < through) {
|
|
4128
|
+
var pieces = env.state.turns.slice(Math.max(0, index - 1), index + 2).filter(function(t) {
|
|
4129
|
+
return !t.hidden;
|
|
4130
|
+
}).map(function(t) {
|
|
4131
|
+
var text = t.role === "assistant" ? env.foldForHistory(t.text || "") : sentTextOf(t);
|
|
4132
|
+
var at = text.indexOf(selection.text);
|
|
4133
|
+
var start = at >= 0 ? Math.max(0, at - 500) : 0;
|
|
4134
|
+
return (t.role === "user" ? "\u7528\u6237\uFF1A" : "\u52A9\u624B\uFF1A") + text.slice(start, start + Math.max(1500, selection.text.length + 500));
|
|
4135
|
+
});
|
|
4136
|
+
return { context: pieces.join("\n"), session: false };
|
|
4137
|
+
}
|
|
3976
4138
|
return { context: "", session: false };
|
|
3977
4139
|
}
|
|
3978
4140
|
if (selection.source === "iframe") {
|
|
@@ -3993,22 +4155,16 @@ function createQuotesController(env) {
|
|
|
3993
4155
|
for (var i = 0; i < quotes.length; i += 1) {
|
|
3994
4156
|
if (quotes[i].session === true) jobs.push(quotes[i]);
|
|
3995
4157
|
}
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
return fetchQuoteContext(sessionId, quote.text).then(function(context) {
|
|
4001
|
-
if (context) quote.context = context;
|
|
4002
|
-
return quote;
|
|
4003
|
-
});
|
|
4004
|
-
})
|
|
4005
|
-
).then(function() {
|
|
4158
|
+
if (jobs.length === 0) return Promise.resolve(quotes);
|
|
4159
|
+
return Promise.all(jobs.map(function(quote) {
|
|
4160
|
+
return quote.contextJob || Promise.resolve();
|
|
4161
|
+
})).then(function() {
|
|
4006
4162
|
return quotes;
|
|
4007
4163
|
});
|
|
4008
4164
|
}
|
|
4009
4165
|
function fetchQuoteContext(sessionId, text) {
|
|
4010
4166
|
var controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
4011
|
-
var timer =
|
|
4167
|
+
var timer = env.timerScope.later(function() {
|
|
4012
4168
|
if (controller) {
|
|
4013
4169
|
try {
|
|
4014
4170
|
controller.abort();
|
|
@@ -4016,7 +4172,6 @@ function createQuotesController(env) {
|
|
|
4016
4172
|
}
|
|
4017
4173
|
}
|
|
4018
4174
|
}, QUOTE_CONTEXT_TIMEOUT);
|
|
4019
|
-
env.timers.push(timer);
|
|
4020
4175
|
return fetch(QUOTE_CONTEXT_API, {
|
|
4021
4176
|
method: "POST",
|
|
4022
4177
|
headers: { "content-type": "application/json" },
|
|
@@ -4025,11 +4180,11 @@ function createQuotesController(env) {
|
|
|
4025
4180
|
}).then(function(response) {
|
|
4026
4181
|
return response.ok ? response.json() : null;
|
|
4027
4182
|
}).then(function(data) {
|
|
4028
|
-
|
|
4183
|
+
env.timerScope.clear(timer);
|
|
4029
4184
|
if (data && data.ok === true && data.matched === true && typeof data.context === "string") return data.context;
|
|
4030
4185
|
return "";
|
|
4031
4186
|
}).catch(function() {
|
|
4032
|
-
|
|
4187
|
+
env.timerScope.clear(timer);
|
|
4033
4188
|
return "";
|
|
4034
4189
|
});
|
|
4035
4190
|
}
|
|
@@ -4071,6 +4226,7 @@ function createQuotesController(env) {
|
|
|
4071
4226
|
out.push({
|
|
4072
4227
|
role: turn.role,
|
|
4073
4228
|
text: sentTextOf(turn),
|
|
4229
|
+
...turn.contextExcluded === true ? { contextExcluded: true } : {},
|
|
4074
4230
|
...turn.attachments && turn.attachments.length > 0 ? { attachments: turn.attachments } : {}
|
|
4075
4231
|
});
|
|
4076
4232
|
}
|
|
@@ -4146,6 +4302,20 @@ function createAttachController(env) {
|
|
|
4146
4302
|
env.attachNote.textContent = env.state.attachNote || "";
|
|
4147
4303
|
env.attachNote.setAttribute("data-show", env.state.attachNote ? "1" : "0");
|
|
4148
4304
|
}
|
|
4305
|
+
function ensureUploaded() {
|
|
4306
|
+
var pending = items().filter(function(item) {
|
|
4307
|
+
return !item.ref && item.file;
|
|
4308
|
+
});
|
|
4309
|
+
if (pending.length === 0) return Promise.resolve();
|
|
4310
|
+
var chain = Promise.resolve();
|
|
4311
|
+
pending.forEach(function(item) {
|
|
4312
|
+
chain = chain.then(function() {
|
|
4313
|
+
item.started = false;
|
|
4314
|
+
return upload(item);
|
|
4315
|
+
});
|
|
4316
|
+
});
|
|
4317
|
+
return chain;
|
|
4318
|
+
}
|
|
4149
4319
|
function busy() {
|
|
4150
4320
|
var list = items();
|
|
4151
4321
|
for (var i = 0; i < list.length; i += 1) if (list[i].status === "uploading") return true;
|
|
@@ -4222,15 +4392,6 @@ function createAttachController(env) {
|
|
|
4222
4392
|
note(rejected.join("\uFF1B"));
|
|
4223
4393
|
render();
|
|
4224
4394
|
env.refreshAskState();
|
|
4225
|
-
var pending = list.filter(function(item) {
|
|
4226
|
-
return item.status === "uploading" && !item.started;
|
|
4227
|
-
});
|
|
4228
|
-
var chain = Promise.resolve();
|
|
4229
|
-
pending.forEach(function(item) {
|
|
4230
|
-
chain = chain.then(function() {
|
|
4231
|
-
return upload(item);
|
|
4232
|
-
});
|
|
4233
|
-
});
|
|
4234
4395
|
}
|
|
4235
4396
|
function createItem(file) {
|
|
4236
4397
|
var isImage = /^image\//.test(String(file.type || ""));
|
|
@@ -4243,8 +4404,10 @@ function createAttachController(env) {
|
|
|
4243
4404
|
file,
|
|
4244
4405
|
// 缩略图先用本地 object URL 显示(不必等上传完),失败时回落到图标
|
|
4245
4406
|
url: isImage && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : "",
|
|
4246
|
-
|
|
4247
|
-
|
|
4407
|
+
// 'local' = 只在内存里,还没落盘。用户要求:叉掉的不该在附件库留垃圾,
|
|
4408
|
+
// 所以**点发送时才真正上传**(见 ensureUploaded)。
|
|
4409
|
+
status: "local",
|
|
4410
|
+
started: true,
|
|
4248
4411
|
ref: null,
|
|
4249
4412
|
error: ""
|
|
4250
4413
|
};
|
|
@@ -4256,6 +4419,7 @@ function createAttachController(env) {
|
|
|
4256
4419
|
item.status = "uploading";
|
|
4257
4420
|
item.error = "";
|
|
4258
4421
|
render();
|
|
4422
|
+
env.refreshAskState();
|
|
4259
4423
|
return fileToBase64(item.file).then(function(data) {
|
|
4260
4424
|
return fetch(ATTACH, {
|
|
4261
4425
|
method: "POST",
|
|
@@ -4325,10 +4489,44 @@ function createAttachController(env) {
|
|
|
4325
4489
|
});
|
|
4326
4490
|
return button;
|
|
4327
4491
|
}
|
|
4492
|
+
function previewItem(item) {
|
|
4493
|
+
if (!item || item.status === "error") return;
|
|
4494
|
+
var list = items();
|
|
4495
|
+
var images = [];
|
|
4496
|
+
var start = 0;
|
|
4497
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4498
|
+
var other = list[i];
|
|
4499
|
+
if (!other || other.kind !== "image" || other.status === "error") continue;
|
|
4500
|
+
var src = other.url || (other.ref ? attachmentUrl("image", other.ref) : "");
|
|
4501
|
+
if (!src) continue;
|
|
4502
|
+
if (other === item) start = images.length;
|
|
4503
|
+
images.push({ src, name: other.name });
|
|
4504
|
+
}
|
|
4505
|
+
if (images.length === 0 || typeof env.previewImage !== "function") return;
|
|
4506
|
+
env.previewImage({ images, index: start });
|
|
4507
|
+
}
|
|
4328
4508
|
function thumbNode(item) {
|
|
4329
4509
|
var node = el("span", "dsh-sel-thumb");
|
|
4330
4510
|
node.setAttribute("data-state", item.status);
|
|
4331
|
-
|
|
4511
|
+
var previewable = item.status !== "error" && !!(item.url || item.ref);
|
|
4512
|
+
if (previewable) {
|
|
4513
|
+
node.setAttribute("data-preview", "1");
|
|
4514
|
+
node.setAttribute("role", "button");
|
|
4515
|
+
node.tabIndex = 0;
|
|
4516
|
+
node.title = item.name + "\uFF08\u70B9\u51FB\u9884\u89C8\uFF09";
|
|
4517
|
+
node.setAttribute("aria-label", "\u9884\u89C8\u56FE\u7247 " + item.name);
|
|
4518
|
+
listen(node, "click", function() {
|
|
4519
|
+
previewItem(item);
|
|
4520
|
+
});
|
|
4521
|
+
listen(node, "keydown", function(event) {
|
|
4522
|
+
if (event.target !== node) return;
|
|
4523
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
4524
|
+
event.preventDefault();
|
|
4525
|
+
previewItem(item);
|
|
4526
|
+
});
|
|
4527
|
+
} else {
|
|
4528
|
+
node.title = item.name;
|
|
4529
|
+
}
|
|
4332
4530
|
if (item.url) {
|
|
4333
4531
|
var img = document.createElement("img");
|
|
4334
4532
|
img.src = item.url;
|
|
@@ -4459,6 +4657,7 @@ function createAttachController(env) {
|
|
|
4459
4657
|
retry,
|
|
4460
4658
|
render,
|
|
4461
4659
|
busy,
|
|
4660
|
+
ensureUploaded,
|
|
4462
4661
|
payload,
|
|
4463
4662
|
describe,
|
|
4464
4663
|
note,
|
|
@@ -4468,6 +4667,891 @@ function createAttachController(env) {
|
|
|
4468
4667
|
};
|
|
4469
4668
|
}
|
|
4470
4669
|
|
|
4670
|
+
// src/client/lightbox.js
|
|
4671
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
4672
|
+
function createLightboxController(env) {
|
|
4673
|
+
var root = null;
|
|
4674
|
+
var stage = null;
|
|
4675
|
+
var img = null;
|
|
4676
|
+
var nameEl = null;
|
|
4677
|
+
var failEl = null;
|
|
4678
|
+
var closeBtn = null;
|
|
4679
|
+
var prevBtn = null;
|
|
4680
|
+
var nextBtn = null;
|
|
4681
|
+
var countEl = null;
|
|
4682
|
+
var hintEl = null;
|
|
4683
|
+
var built = false;
|
|
4684
|
+
var open = false;
|
|
4685
|
+
var gallery = [];
|
|
4686
|
+
var index = 0;
|
|
4687
|
+
var lastFocus = null;
|
|
4688
|
+
function within(node, element) {
|
|
4689
|
+
if (!node || !element) return false;
|
|
4690
|
+
if (node === element) return true;
|
|
4691
|
+
if (typeof element.contains !== "function") return false;
|
|
4692
|
+
try {
|
|
4693
|
+
return element.contains(
|
|
4694
|
+
/** @type {Node} */
|
|
4695
|
+
node
|
|
4696
|
+
);
|
|
4697
|
+
} catch (error) {
|
|
4698
|
+
return false;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
function chevron(dir) {
|
|
4702
|
+
var svg = document.createElementNS(SVG_NS, "svg");
|
|
4703
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
4704
|
+
svg.setAttribute("width", "16");
|
|
4705
|
+
svg.setAttribute("height", "16");
|
|
4706
|
+
svg.setAttribute("fill", "none");
|
|
4707
|
+
svg.setAttribute("stroke", "currentColor");
|
|
4708
|
+
svg.setAttribute("stroke-width", "1.9");
|
|
4709
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
4710
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
4711
|
+
svg.setAttribute("aria-hidden", "true");
|
|
4712
|
+
var path = document.createElementNS(SVG_NS, "path");
|
|
4713
|
+
path.setAttribute("d", dir === "prev" ? "M10 3.5L5.5 8l4.5 4.5" : "M6 3.5L10.5 8 6 12.5");
|
|
4714
|
+
svg.appendChild(path);
|
|
4715
|
+
return svg;
|
|
4716
|
+
}
|
|
4717
|
+
function navButton(dir) {
|
|
4718
|
+
var button = el("button", "dsh-sel-lightbox-nav");
|
|
4719
|
+
button.type = "button";
|
|
4720
|
+
button.setAttribute("data-dir", dir);
|
|
4721
|
+
var label = dir === "prev" ? "\u4E0A\u4E00\u5F20\u56FE\u7247\uFF08\u2190\uFF09" : "\u4E0B\u4E00\u5F20\u56FE\u7247\uFF08\u2192\uFF09";
|
|
4722
|
+
button.title = label;
|
|
4723
|
+
button.setAttribute("aria-label", label);
|
|
4724
|
+
button.appendChild(chevron(dir));
|
|
4725
|
+
listen(button, "click", function() {
|
|
4726
|
+
step(dir === "prev" ? -1 : 1);
|
|
4727
|
+
});
|
|
4728
|
+
return button;
|
|
4729
|
+
}
|
|
4730
|
+
function build() {
|
|
4731
|
+
if (built) return;
|
|
4732
|
+
built = true;
|
|
4733
|
+
root = el("div", "dsh-sel-lightbox");
|
|
4734
|
+
root.setAttribute("data-open", "0");
|
|
4735
|
+
root.setAttribute("data-error", "0");
|
|
4736
|
+
root.setAttribute("data-gallery", "0");
|
|
4737
|
+
root.setAttribute("role", "dialog");
|
|
4738
|
+
root.setAttribute("aria-label", "\u56FE\u7247\u9884\u89C8");
|
|
4739
|
+
var bar = el("div", "dsh-sel-lightbox-bar");
|
|
4740
|
+
nameEl = el("span", "dsh-sel-lightbox-name");
|
|
4741
|
+
countEl = el("span", "dsh-sel-lightbox-count");
|
|
4742
|
+
closeBtn = el("button", "dsh-sel-lightbox-x");
|
|
4743
|
+
closeBtn.type = "button";
|
|
4744
|
+
closeBtn.title = "\u5173\u95ED\u9884\u89C8\uFF08Esc\uFF09";
|
|
4745
|
+
closeBtn.setAttribute("aria-label", "\u5173\u95ED\u56FE\u7247\u9884\u89C8");
|
|
4746
|
+
closeBtn.appendChild(closeIcon());
|
|
4747
|
+
listen(closeBtn, "click", function() {
|
|
4748
|
+
close();
|
|
4749
|
+
});
|
|
4750
|
+
bar.appendChild(nameEl);
|
|
4751
|
+
bar.appendChild(countEl);
|
|
4752
|
+
bar.appendChild(closeBtn);
|
|
4753
|
+
stage = el("div", "dsh-sel-lightbox-stage");
|
|
4754
|
+
img = document.createElement("img");
|
|
4755
|
+
img.className = "dsh-sel-lightbox-img";
|
|
4756
|
+
img.alt = "";
|
|
4757
|
+
img.decoding = "async";
|
|
4758
|
+
img.draggable = false;
|
|
4759
|
+
img.addEventListener("error", function() {
|
|
4760
|
+
if (!open) return;
|
|
4761
|
+
root.setAttribute("data-error", "1");
|
|
4762
|
+
failEl.textContent = "\u8FD9\u5F20\u56FE\u6253\u4E0D\u5F00\u4E86\uFF08\u9644\u4EF6\u53EF\u80FD\u5DF2\u88AB\u6E05\u7406\uFF09";
|
|
4763
|
+
});
|
|
4764
|
+
stage.appendChild(img);
|
|
4765
|
+
failEl = el("p", "dsh-sel-lightbox-fail");
|
|
4766
|
+
prevBtn = navButton("prev");
|
|
4767
|
+
nextBtn = navButton("next");
|
|
4768
|
+
hintEl = el("p", "dsh-sel-lightbox-hint");
|
|
4769
|
+
root.appendChild(bar);
|
|
4770
|
+
root.appendChild(stage);
|
|
4771
|
+
root.appendChild(failEl);
|
|
4772
|
+
root.appendChild(prevBtn);
|
|
4773
|
+
root.appendChild(nextBtn);
|
|
4774
|
+
root.appendChild(hintEl);
|
|
4775
|
+
listen(root, "click", function(event) {
|
|
4776
|
+
if (!open) return;
|
|
4777
|
+
var target = event.target;
|
|
4778
|
+
if (target === img) return;
|
|
4779
|
+
if (within(target, bar) || within(target, prevBtn) || within(target, nextBtn)) return;
|
|
4780
|
+
close();
|
|
4781
|
+
});
|
|
4782
|
+
listen(root, "keydown", function(event) {
|
|
4783
|
+
if (!open || event.key !== "Tab") return;
|
|
4784
|
+
var order = [prevBtn, nextBtn, closeBtn].filter(function(button) {
|
|
4785
|
+
return !button.disabled;
|
|
4786
|
+
});
|
|
4787
|
+
var current = order.indexOf(document.activeElement);
|
|
4788
|
+
var next = event.shiftKey ? current - 1 : current + 1;
|
|
4789
|
+
if (next < 0) next = order.length - 1;
|
|
4790
|
+
if (next >= order.length) next = 0;
|
|
4791
|
+
event.preventDefault();
|
|
4792
|
+
if (order[next] && typeof order[next].focus === "function") order[next].focus();
|
|
4793
|
+
});
|
|
4794
|
+
env.layer.appendChild(root);
|
|
4795
|
+
}
|
|
4796
|
+
function paintImage() {
|
|
4797
|
+
var item = gallery[index] || { src: "", name: "\u56FE\u7247" };
|
|
4798
|
+
var many = gallery.length > 1;
|
|
4799
|
+
root.setAttribute("data-error", "0");
|
|
4800
|
+
root.setAttribute("data-gallery", many ? "1" : "0");
|
|
4801
|
+
failEl.textContent = "";
|
|
4802
|
+
nameEl.textContent = item.name || "\u56FE\u7247";
|
|
4803
|
+
img.alt = nameEl.textContent;
|
|
4804
|
+
img.src = item.src;
|
|
4805
|
+
countEl.textContent = many ? index + 1 + " / " + gallery.length : "";
|
|
4806
|
+
prevBtn.disabled = !many || index === 0;
|
|
4807
|
+
nextBtn.disabled = !many || index === gallery.length - 1;
|
|
4808
|
+
hintEl.textContent = many ? "\u2190 / \u2192 \u5207\u6362 \xB7 Esc \u6216\u70B9\u51FB\u7A7A\u767D\u5904\u5173\u95ED" : "Esc \u6216\u70B9\u51FB\u7A7A\u767D\u5904\u5173\u95ED";
|
|
4809
|
+
root.setAttribute("aria-label", "\u56FE\u7247\u9884\u89C8" + (many ? "\uFF08" + (index + 1) + " / " + gallery.length + "\uFF09" : ""));
|
|
4810
|
+
}
|
|
4811
|
+
function step(delta) {
|
|
4812
|
+
if (!open || gallery.length < 2) return;
|
|
4813
|
+
var next = index + delta;
|
|
4814
|
+
if (next < 0 || next >= gallery.length) return;
|
|
4815
|
+
index = next;
|
|
4816
|
+
paintImage();
|
|
4817
|
+
}
|
|
4818
|
+
function normalize(items, fallback) {
|
|
4819
|
+
var list = [];
|
|
4820
|
+
if (items && items.length) {
|
|
4821
|
+
for (var i = 0; i < items.length; i += 1) {
|
|
4822
|
+
var item = items[i] || {};
|
|
4823
|
+
if (!item.src) continue;
|
|
4824
|
+
list.push({ src: String(item.src), name: item.name ? String(item.name) : "\u56FE\u7247" });
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
if (list.length === 0 && fallback && fallback.src) {
|
|
4828
|
+
list.push({ src: String(fallback.src), name: fallback.name ? String(fallback.name) : "\u56FE\u7247" });
|
|
4829
|
+
}
|
|
4830
|
+
return list;
|
|
4831
|
+
}
|
|
4832
|
+
function openImage(options) {
|
|
4833
|
+
build();
|
|
4834
|
+
var given = options && Array.isArray(options.images) ? options.images : null;
|
|
4835
|
+
var list = normalize(given, options);
|
|
4836
|
+
if (list.length === 0) return;
|
|
4837
|
+
var start = options && typeof options.index === "number" ? Math.floor(options.index) : 0;
|
|
4838
|
+
if (!(start >= 0 && start < list.length)) start = 0;
|
|
4839
|
+
gallery = list;
|
|
4840
|
+
index = start;
|
|
4841
|
+
lastFocus = document.activeElement;
|
|
4842
|
+
open = true;
|
|
4843
|
+
root.setAttribute("data-open", "1");
|
|
4844
|
+
root.setAttribute("aria-modal", "true");
|
|
4845
|
+
paintImage();
|
|
4846
|
+
if (typeof closeBtn.focus === "function") closeBtn.focus();
|
|
4847
|
+
}
|
|
4848
|
+
function close() {
|
|
4849
|
+
if (!open || !built) return;
|
|
4850
|
+
open = false;
|
|
4851
|
+
gallery = [];
|
|
4852
|
+
index = 0;
|
|
4853
|
+
root.setAttribute("data-open", "0");
|
|
4854
|
+
root.setAttribute("data-error", "0");
|
|
4855
|
+
root.setAttribute("data-gallery", "0");
|
|
4856
|
+
root.removeAttribute("aria-modal");
|
|
4857
|
+
img.removeAttribute("src");
|
|
4858
|
+
img.alt = "";
|
|
4859
|
+
countEl.textContent = "";
|
|
4860
|
+
prevBtn.disabled = true;
|
|
4861
|
+
nextBtn.disabled = true;
|
|
4862
|
+
var back = lastFocus;
|
|
4863
|
+
lastFocus = null;
|
|
4864
|
+
if (back && back.isConnected !== false && typeof back.focus === "function") {
|
|
4865
|
+
try {
|
|
4866
|
+
back.focus();
|
|
4867
|
+
} catch (error) {
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
function foreignModalOpen() {
|
|
4872
|
+
var modals = document.querySelectorAll('[role="dialog"][aria-modal="true"]');
|
|
4873
|
+
for (var i = 0; i < modals.length; i += 1) if (modals[i] !== root) return true;
|
|
4874
|
+
return false;
|
|
4875
|
+
}
|
|
4876
|
+
var offKey = listen(document, "keydown", function(event) {
|
|
4877
|
+
if (!open) return;
|
|
4878
|
+
if (event.defaultPrevented || event.isComposing || event.keyCode === 229) return;
|
|
4879
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
4880
|
+
if (gallery.length < 2 || foreignModalOpen()) return;
|
|
4881
|
+
event.preventDefault();
|
|
4882
|
+
event.stopPropagation();
|
|
4883
|
+
step(event.key === "ArrowLeft" ? -1 : 1);
|
|
4884
|
+
return;
|
|
4885
|
+
}
|
|
4886
|
+
if (event.key !== "Escape") return;
|
|
4887
|
+
if (foreignModalOpen()) return;
|
|
4888
|
+
event.preventDefault();
|
|
4889
|
+
event.stopPropagation();
|
|
4890
|
+
close();
|
|
4891
|
+
}, true);
|
|
4892
|
+
return {
|
|
4893
|
+
open: openImage,
|
|
4894
|
+
close,
|
|
4895
|
+
get isOpen() {
|
|
4896
|
+
return open;
|
|
4897
|
+
},
|
|
4898
|
+
/** 卸载时摘掉 document 上的监听(灯箱 DOM 随 layer 一起销毁)。 */
|
|
4899
|
+
dispose: function() {
|
|
4900
|
+
close();
|
|
4901
|
+
offKey();
|
|
4902
|
+
}
|
|
4903
|
+
};
|
|
4904
|
+
}
|
|
4905
|
+
|
|
4906
|
+
// src/client/shot.js
|
|
4907
|
+
var MAX_ANNOTATED = 2048;
|
|
4908
|
+
var FRAME_COLOR = "#e5484d";
|
|
4909
|
+
var FRAME_WIDTH_MIN_PX = 3;
|
|
4910
|
+
var SHOT_INSIDE_SLOP = 2;
|
|
4911
|
+
function pageRectOf(rect, origin) {
|
|
4912
|
+
if (!rect) return null;
|
|
4913
|
+
var sx = origin && typeof origin.screenX === "number" ? origin.screenX : 0;
|
|
4914
|
+
var sy = origin && typeof origin.screenY === "number" ? origin.screenY : 0;
|
|
4915
|
+
return asRect({
|
|
4916
|
+
left: rect.x - sx,
|
|
4917
|
+
top: rect.y - sy,
|
|
4918
|
+
width: rect.width,
|
|
4919
|
+
height: rect.height
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
function asRect(box) {
|
|
4923
|
+
var left = typeof box.left === "number" ? box.left : 0;
|
|
4924
|
+
var top = typeof box.top === "number" ? box.top : 0;
|
|
4925
|
+
var width = typeof box.width === "number" ? box.width : 0;
|
|
4926
|
+
var height = typeof box.height === "number" ? box.height : 0;
|
|
4927
|
+
return { left, top, width, height, right: left + width, bottom: top + height, x: left, y: top };
|
|
4928
|
+
}
|
|
4929
|
+
function insideViewport(rect, viewport, slop) {
|
|
4930
|
+
if (!rect || !viewport) return false;
|
|
4931
|
+
var pad = typeof slop === "number" ? slop : SHOT_INSIDE_SLOP;
|
|
4932
|
+
return rect.left >= -pad && rect.top >= -pad && rect.left + rect.width <= viewport.width + pad && rect.top + rect.height <= viewport.height + pad;
|
|
4933
|
+
}
|
|
4934
|
+
function nearestEdge(rect, viewport) {
|
|
4935
|
+
var cx = rect.left + rect.width / 2;
|
|
4936
|
+
var cy = rect.top + rect.height / 2;
|
|
4937
|
+
var d = { left: Math.abs(cx), right: Math.abs(viewport.width - cx), top: Math.abs(cy), bottom: Math.abs(viewport.height - cy) };
|
|
4938
|
+
var best = "left";
|
|
4939
|
+
var bestValue = Infinity;
|
|
4940
|
+
for (var key in d) {
|
|
4941
|
+
if (!Object.prototype.hasOwnProperty.call(d, key)) continue;
|
|
4942
|
+
if (d[key] < bestValue) {
|
|
4943
|
+
bestValue = d[key];
|
|
4944
|
+
best = key;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
return best;
|
|
4948
|
+
}
|
|
4949
|
+
function chipRectFor(edge, anchorRect, viewport, size) {
|
|
4950
|
+
var w = size && size.width || 158;
|
|
4951
|
+
var h = size && size.height || 48;
|
|
4952
|
+
var pad = 12;
|
|
4953
|
+
var cx = anchorRect.left + anchorRect.width / 2;
|
|
4954
|
+
var cy = anchorRect.top + anchorRect.height / 2;
|
|
4955
|
+
var left;
|
|
4956
|
+
var top;
|
|
4957
|
+
if (edge === "left" || edge === "right") {
|
|
4958
|
+
left = edge === "left" ? pad : viewport.width - w - pad;
|
|
4959
|
+
top = Math.max(pad, Math.min(cy - h / 2, viewport.height - h - pad));
|
|
4960
|
+
} else {
|
|
4961
|
+
top = edge === "top" ? pad : viewport.height - h - pad;
|
|
4962
|
+
left = Math.max(pad, Math.min(cx - w / 2, viewport.width - w - pad));
|
|
4963
|
+
}
|
|
4964
|
+
return { left, top, width: w, height: h };
|
|
4965
|
+
}
|
|
4966
|
+
function findContentBox(g, width, height) {
|
|
4967
|
+
var probe = Math.min(400, width, height);
|
|
4968
|
+
function hasOpaque(data, count, stride, offset) {
|
|
4969
|
+
for (var i = 0; i < count; i += 1) {
|
|
4970
|
+
if (data[i * stride + offset + 3] > 128) return true;
|
|
4971
|
+
}
|
|
4972
|
+
return false;
|
|
4973
|
+
}
|
|
4974
|
+
var box = { left: 0, top: 0, right: width, bottom: height };
|
|
4975
|
+
var strip = g.getImageData(0, 0, width, probe);
|
|
4976
|
+
for (var y = 0; y < probe; y += 1) {
|
|
4977
|
+
if (hasOpaque(strip.data.subarray(y * width * 4), width, 4, 0)) {
|
|
4978
|
+
box.top = y;
|
|
4979
|
+
break;
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
strip = g.getImageData(0, Math.max(0, height - probe), width, probe);
|
|
4983
|
+
for (var y2 = probe - 1; y2 >= 0; y2 -= 1) {
|
|
4984
|
+
if (hasOpaque(strip.data.subarray(y2 * width * 4), width, 4, 0)) {
|
|
4985
|
+
box.bottom = Math.max(0, height - probe) + y2 + 1;
|
|
4986
|
+
break;
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
strip = g.getImageData(0, 0, probe, height);
|
|
4990
|
+
for (var x = 0; x < probe; x += 1) {
|
|
4991
|
+
if (hasOpaque(strip.data.subarray(x * 4), height, probe * 4, 0)) {
|
|
4992
|
+
box.left = x;
|
|
4993
|
+
break;
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
strip = g.getImageData(Math.max(0, width - probe), 0, probe, height);
|
|
4997
|
+
for (var x2 = probe - 1; x2 >= 0; x2 -= 1) {
|
|
4998
|
+
if (hasOpaque(strip.data.subarray(x2 * 4), height, probe * 4, 0)) {
|
|
4999
|
+
box.right = Math.max(0, width - probe) + x2 + 1;
|
|
5000
|
+
break;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
if (box.right - box.left < 16 || box.bottom - box.top < 16) return null;
|
|
5004
|
+
return box;
|
|
5005
|
+
}
|
|
5006
|
+
function annotate(image, rect, scale, shot) {
|
|
5007
|
+
var iw = image.naturalWidth || image.width;
|
|
5008
|
+
var ih = image.naturalHeight || image.height;
|
|
5009
|
+
var k = iw > MAX_ANNOTATED ? MAX_ANNOTATED / iw : 1;
|
|
5010
|
+
var canvas = document.createElement("canvas");
|
|
5011
|
+
canvas.width = Math.max(1, Math.round(iw * k));
|
|
5012
|
+
canvas.height = Math.max(1, Math.round(ih * k));
|
|
5013
|
+
var g = canvas.getContext("2d");
|
|
5014
|
+
var needContentBox = !shot || !shot.scope || shot.scope === "window";
|
|
5015
|
+
var content = null;
|
|
5016
|
+
if (needContentBox) {
|
|
5017
|
+
g.imageSmoothingEnabled = false;
|
|
5018
|
+
g.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
5019
|
+
try {
|
|
5020
|
+
content = findContentBox(g, canvas.width, canvas.height);
|
|
5021
|
+
} catch (error) {
|
|
5022
|
+
content = null;
|
|
5023
|
+
}
|
|
5024
|
+
g.clearRect(0, 0, canvas.width, canvas.height);
|
|
5025
|
+
}
|
|
5026
|
+
g.imageSmoothingEnabled = true;
|
|
5027
|
+
g.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
5028
|
+
if (content && shot && shot.windowRect && shot.selectionRect) {
|
|
5029
|
+
var win = shot.windowRect;
|
|
5030
|
+
var sel = shot.selectionRect;
|
|
5031
|
+
var left = content.left / k;
|
|
5032
|
+
var top = content.top / k;
|
|
5033
|
+
var right = content.right / k;
|
|
5034
|
+
var realScale = (right - left) / win.width;
|
|
5035
|
+
if (realScale > 0.2 && realScale < 5) {
|
|
5036
|
+
rect = {
|
|
5037
|
+
x: left + (sel.x - win.x) * realScale,
|
|
5038
|
+
y: top + (sel.y - win.y) * realScale,
|
|
5039
|
+
width: sel.width * realScale,
|
|
5040
|
+
height: sel.height * realScale
|
|
5041
|
+
};
|
|
5042
|
+
scale = realScale;
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
rect = { x: rect.x * k, y: rect.y * k, width: rect.width * k, height: rect.height * k };
|
|
5046
|
+
var line = Math.max(FRAME_WIDTH_MIN_PX, Math.round(3 * (scale || 1) * k));
|
|
5047
|
+
g.strokeStyle = FRAME_COLOR;
|
|
5048
|
+
g.lineWidth = line;
|
|
5049
|
+
var inset = line / 2;
|
|
5050
|
+
g.strokeRect(rect.x + inset, rect.y + inset, Math.max(0, rect.width - line), Math.max(0, rect.height - line));
|
|
5051
|
+
return new Promise(function(resolve) {
|
|
5052
|
+
canvas.toBlob(function(blob) {
|
|
5053
|
+
resolve(blob);
|
|
5054
|
+
}, "image/png");
|
|
5055
|
+
});
|
|
5056
|
+
}
|
|
5057
|
+
function report(payload) {
|
|
5058
|
+
try {
|
|
5059
|
+
fetch("/selection-explain/api/shot/hello", { method: "POST", body: JSON.stringify(payload) }).catch(function() {
|
|
5060
|
+
});
|
|
5061
|
+
} catch (error) {
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
function createShotController(env) {
|
|
5065
|
+
var host = env.window || (typeof window !== "undefined" ? window : null) || {};
|
|
5066
|
+
var myEpoch = 0;
|
|
5067
|
+
try {
|
|
5068
|
+
host.__dshSelShotEpoch = (host.__dshSelShotEpoch || 0) + 1;
|
|
5069
|
+
myEpoch = host.__dshSelShotEpoch;
|
|
5070
|
+
} catch (error) {
|
|
5071
|
+
myEpoch = 1;
|
|
5072
|
+
}
|
|
5073
|
+
function superseded() {
|
|
5074
|
+
try {
|
|
5075
|
+
return host.__dshSelShotEpoch !== myEpoch;
|
|
5076
|
+
} catch (error) {
|
|
5077
|
+
return false;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
var timer = 0;
|
|
5081
|
+
var busy = false;
|
|
5082
|
+
var lastId = "";
|
|
5083
|
+
var current = null;
|
|
5084
|
+
var frameEl = null;
|
|
5085
|
+
var chipEl = null;
|
|
5086
|
+
var chipImg = null;
|
|
5087
|
+
var chipUrl = "";
|
|
5088
|
+
var toastEl = null;
|
|
5089
|
+
var toastTimer = 0;
|
|
5090
|
+
function errorText(info) {
|
|
5091
|
+
var code = info && (info.code || info.reason) ? String(info.code || info.reason) : "";
|
|
5092
|
+
if (info && typeof info.message === "string" && info.message.trim()) return info.message;
|
|
5093
|
+
if (code === "not-macos") return "\u622A\u56FE\u529F\u80FD\u76EE\u524D\u4EC5\u652F\u6301 macOS";
|
|
5094
|
+
if (code === "no-screen-permission") return "\u6CA1\u6709\u5C4F\u5E55\u5F55\u5236\u6743\u9650\uFF1A\u8BF7\u5728 \u7CFB\u7EDF\u8BBE\u7F6E \u2192 \u9690\u79C1\u4E0E\u5B89\u5168\u6027 \u2192 \u5C4F\u5E55\u5F55\u5236 \u4E2D\u5141\u8BB8 DSH\uFF0C\u7136\u540E\u91CD\u8BD5";
|
|
5095
|
+
if (code === "busy") return "\u6B63\u5728\u622A\u56FE\uFF0C\u8BF7\u7A0D\u5019";
|
|
5096
|
+
if (code === "probe-missing") return "\u622A\u56FE\u7EC4\u4EF6\u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u6784\u5EFA\u63D2\u4EF6\u540E\u91CD\u8BD5";
|
|
5097
|
+
if (code === "spawn-failed") return "\u7CFB\u7EDF\u622A\u56FE\u547D\u4EE4\u542F\u52A8\u5931\u8D25";
|
|
5098
|
+
if (code === "timeout") return "\u6846\u9009\u8D85\u65F6\uFF0C\u5DF2\u53D6\u6D88\u8FD9\u6B21\u622A\u56FE";
|
|
5099
|
+
if (code === "capture-failed") return "\u622A\u56FE\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5C4F\u5E55\u5F55\u5236\u6743\u9650\u540E\u91CD\u8BD5";
|
|
5100
|
+
if (code === "image-missing" || code === "finish-failed") return "\u622A\u56FE\u5DF2\u62CD\u4E0B\uFF0C\u4F46\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5";
|
|
5101
|
+
if (code.indexOf("tmp-failed") === 0) return "\u622A\u56FE\u4E34\u65F6\u76EE\u5F55\u4E0D\u53EF\u7528\uFF0C\u8BF7\u91CD\u8BD5";
|
|
5102
|
+
if (code.indexOf("start-failed") === 0) return "\u622A\u56FE\u542F\u52A8\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5";
|
|
5103
|
+
return "\u622A\u56FE\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5";
|
|
5104
|
+
}
|
|
5105
|
+
function ensureToast() {
|
|
5106
|
+
if (toastEl) {
|
|
5107
|
+
if (env.layer && toastEl.parentNode !== env.layer) env.layer.appendChild(toastEl);
|
|
5108
|
+
return toastEl;
|
|
5109
|
+
}
|
|
5110
|
+
var d = doc();
|
|
5111
|
+
toastEl = d.createElement("div");
|
|
5112
|
+
toastEl.className = "dsh-sel-shot-toast";
|
|
5113
|
+
toastEl.setAttribute("data-show", "0");
|
|
5114
|
+
toastEl.setAttribute("role", "status");
|
|
5115
|
+
toastEl.setAttribute("aria-live", "polite");
|
|
5116
|
+
toastEl.addEventListener("click", function() {
|
|
5117
|
+
hideNotice();
|
|
5118
|
+
});
|
|
5119
|
+
env.layer.appendChild(toastEl);
|
|
5120
|
+
return toastEl;
|
|
5121
|
+
}
|
|
5122
|
+
function notice(text, tone) {
|
|
5123
|
+
if (!text) return;
|
|
5124
|
+
if (env.ensureLayer) env.ensureLayer();
|
|
5125
|
+
var el2 = ensureToast();
|
|
5126
|
+
el2.textContent = String(text);
|
|
5127
|
+
el2.setAttribute("data-tone", tone || "error");
|
|
5128
|
+
el2.setAttribute("data-show", "1");
|
|
5129
|
+
if (toastTimer) clearTimeout(toastTimer);
|
|
5130
|
+
toastTimer = setTimeout(function() {
|
|
5131
|
+
toastTimer = 0;
|
|
5132
|
+
hideNotice();
|
|
5133
|
+
}, 4500);
|
|
5134
|
+
}
|
|
5135
|
+
function hideNotice() {
|
|
5136
|
+
if (toastTimer) {
|
|
5137
|
+
clearTimeout(toastTimer);
|
|
5138
|
+
toastTimer = 0;
|
|
5139
|
+
}
|
|
5140
|
+
if (toastEl) toastEl.setAttribute("data-show", "0");
|
|
5141
|
+
}
|
|
5142
|
+
function doc() {
|
|
5143
|
+
return env.document;
|
|
5144
|
+
}
|
|
5145
|
+
function viewport() {
|
|
5146
|
+
var win = env.window || window;
|
|
5147
|
+
return { width: win && win.innerWidth || 1200, height: win && win.innerHeight || 800 };
|
|
5148
|
+
}
|
|
5149
|
+
function origin() {
|
|
5150
|
+
var win = env.window || window;
|
|
5151
|
+
return { screenX: win && win.screenX ? win.screenX : 0, screenY: win && win.screenY ? win.screenY : 0 };
|
|
5152
|
+
}
|
|
5153
|
+
function ensureFrame() {
|
|
5154
|
+
if (frameEl) return frameEl;
|
|
5155
|
+
var d = doc();
|
|
5156
|
+
frameEl = d.createElement("div");
|
|
5157
|
+
frameEl.className = "dsh-sel-shotframe";
|
|
5158
|
+
frameEl.setAttribute("data-show", "0");
|
|
5159
|
+
var tag = d.createElement("span");
|
|
5160
|
+
tag.className = "dsh-sel-shotframe-tag";
|
|
5161
|
+
tag.textContent = "\u5DF2\u9009\u4E2D\u7684\u533A\u57DF";
|
|
5162
|
+
frameEl.appendChild(tag);
|
|
5163
|
+
env.layer.appendChild(frameEl);
|
|
5164
|
+
return frameEl;
|
|
5165
|
+
}
|
|
5166
|
+
function ensureChip() {
|
|
5167
|
+
if (chipEl) return chipEl;
|
|
5168
|
+
var d = doc();
|
|
5169
|
+
chipEl = d.createElement("div");
|
|
5170
|
+
chipEl.className = "dsh-sel-shotchip";
|
|
5171
|
+
chipEl.setAttribute("data-show", "0");
|
|
5172
|
+
chipImg = d.createElement("img");
|
|
5173
|
+
chipImg.alt = "\u622A\u56FE";
|
|
5174
|
+
chipEl.appendChild(chipImg);
|
|
5175
|
+
var tx = d.createElement("span");
|
|
5176
|
+
tx.className = "dsh-sel-shotchip-tx";
|
|
5177
|
+
var b = d.createElement("b");
|
|
5178
|
+
b.textContent = "\u6574\u7A97 + \u7EA2\u6846";
|
|
5179
|
+
var meta = d.createElement("span");
|
|
5180
|
+
meta.className = "dsh-sel-shotchip-meta";
|
|
5181
|
+
tx.appendChild(b);
|
|
5182
|
+
tx.appendChild(meta);
|
|
5183
|
+
chipEl.appendChild(tx);
|
|
5184
|
+
env.layer.appendChild(chipEl);
|
|
5185
|
+
return chipEl;
|
|
5186
|
+
}
|
|
5187
|
+
function scopeText(shot) {
|
|
5188
|
+
var scope = shot && shot.scope;
|
|
5189
|
+
if (!shot || shot.frame === false || scope === "selection") return "\u4EC5\u9009\u533A";
|
|
5190
|
+
if (scope === "screen") return "\u5168\u5C4F + \u7EA2\u6846";
|
|
5191
|
+
if (scope === "margin") return "\u9009\u533A + \u7559\u767D";
|
|
5192
|
+
return "\u6574\u7A97 + \u7EA2\u6846";
|
|
5193
|
+
}
|
|
5194
|
+
function hideMarkers() {
|
|
5195
|
+
if (frameEl) frameEl.setAttribute("data-show", "0");
|
|
5196
|
+
if (chipEl) chipEl.setAttribute("data-show", "0");
|
|
5197
|
+
}
|
|
5198
|
+
function clearAll() {
|
|
5199
|
+
hideNotice();
|
|
5200
|
+
hideMarkers();
|
|
5201
|
+
if (chipUrl) {
|
|
5202
|
+
URL.revokeObjectURL(chipUrl);
|
|
5203
|
+
chipUrl = "";
|
|
5204
|
+
}
|
|
5205
|
+
if (chipImg) chipImg.removeAttribute("src");
|
|
5206
|
+
current = null;
|
|
5207
|
+
if (env.state.selection && env.state.selection.source === "shot") env.state.selection = null;
|
|
5208
|
+
if (env.vcardOpen && env.vcardOpen() && env.closeVCard) env.closeVCard(true);
|
|
5209
|
+
}
|
|
5210
|
+
var cancelBound = false;
|
|
5211
|
+
function bindCancel() {
|
|
5212
|
+
if (cancelBound) return;
|
|
5213
|
+
var d = doc();
|
|
5214
|
+
if (!d || !d.addEventListener) return;
|
|
5215
|
+
cancelBound = true;
|
|
5216
|
+
var showing = function() {
|
|
5217
|
+
return !!current || frameEl && frameEl.getAttribute("data-show") === "1" || chipEl && chipEl.getAttribute("data-show") === "1" || toastEl && toastEl.getAttribute("data-show") === "1";
|
|
5218
|
+
};
|
|
5219
|
+
d.addEventListener("keydown", function(event) {
|
|
5220
|
+
if (event.key === "Escape" && showing()) clearAll();
|
|
5221
|
+
}, true);
|
|
5222
|
+
d.addEventListener("pointerdown", function(event) {
|
|
5223
|
+
if (!showing()) return;
|
|
5224
|
+
var target = event.target;
|
|
5225
|
+
var card = env.vcardElement && env.vcardElement();
|
|
5226
|
+
if (card && target && card.contains && card.contains(target)) return;
|
|
5227
|
+
if (chipEl && target && chipEl.contains && chipEl.contains(target)) return;
|
|
5228
|
+
if (toastEl && target && toastEl.contains && toastEl.contains(target)) return;
|
|
5229
|
+
clearAll();
|
|
5230
|
+
}, true);
|
|
5231
|
+
}
|
|
5232
|
+
function startCapture() {
|
|
5233
|
+
clearAll();
|
|
5234
|
+
var mode = env.panelOpen ? "panel" : "card";
|
|
5235
|
+
fetch(SHOT_START + "?mode=" + mode, { method: "POST", body: "" }).then(function(response) {
|
|
5236
|
+
if (!response.ok) throw new Error("HTTP " + response.status);
|
|
5237
|
+
return response.json();
|
|
5238
|
+
}).then(function(info) {
|
|
5239
|
+
if (!info || info.ok !== true) notice(errorText(info));
|
|
5240
|
+
}).catch(function() {
|
|
5241
|
+
notice("\u622A\u56FE\u670D\u52A1\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
5242
|
+
});
|
|
5243
|
+
}
|
|
5244
|
+
async function handle(shot) {
|
|
5245
|
+
if (!shot || shot.id === lastId) return;
|
|
5246
|
+
lastId = shot.id;
|
|
5247
|
+
var t0 = Date.now();
|
|
5248
|
+
current = { shot, file: null, blob: null, ref: null, refReady: null, uploading: null, error: "", t0 };
|
|
5249
|
+
var mine = current;
|
|
5250
|
+
var step = "geometry";
|
|
5251
|
+
try {
|
|
5252
|
+
if (env.ensureLayer) env.ensureLayer();
|
|
5253
|
+
var page = pageRectOf(shot.selectionRect, origin());
|
|
5254
|
+
var view = viewport();
|
|
5255
|
+
var inside = !!(page && insideViewport(page, view));
|
|
5256
|
+
var anchor = null;
|
|
5257
|
+
var panelMode = !!env.panelOpen && (!shot.mode || shot.mode === "panel");
|
|
5258
|
+
if (panelMode) {
|
|
5259
|
+
if (frameEl) frameEl.setAttribute("data-show", "0");
|
|
5260
|
+
if (chipEl) chipEl.setAttribute("data-show", "0");
|
|
5261
|
+
} else if (inside) {
|
|
5262
|
+
var frame = ensureFrame();
|
|
5263
|
+
frame.style.left = Math.round(page.left) + "px";
|
|
5264
|
+
frame.style.top = Math.round(page.top) + "px";
|
|
5265
|
+
frame.style.width = Math.round(page.width) + "px";
|
|
5266
|
+
frame.style.height = Math.round(page.height) + "px";
|
|
5267
|
+
frame.setAttribute("data-show", "1");
|
|
5268
|
+
if (chipEl) chipEl.setAttribute("data-show", "0");
|
|
5269
|
+
anchor = page;
|
|
5270
|
+
} else {
|
|
5271
|
+
var chip = ensureChip();
|
|
5272
|
+
var edge = nearestEdge(page || { left: 0, top: 0, width: 0, height: 0 }, view);
|
|
5273
|
+
var box = chipRectFor(edge, page || { left: 0, top: 0, width: 0, height: 0 }, view);
|
|
5274
|
+
chip.style.left = Math.round(box.left) + "px";
|
|
5275
|
+
chip.style.top = Math.round(box.top) + "px";
|
|
5276
|
+
var chipTitle = chip.querySelector(".dsh-sel-shotchip-tx b");
|
|
5277
|
+
if (chipTitle) chipTitle.textContent = scopeText(shot);
|
|
5278
|
+
var meta = chip.querySelector(".dsh-sel-shotchip-meta");
|
|
5279
|
+
if (meta) meta.textContent = Math.round(shot.selectionRect.width) + " \xD7 " + Math.round(shot.selectionRect.height);
|
|
5280
|
+
chip.setAttribute("data-show", "1");
|
|
5281
|
+
if (frameEl) frameEl.setAttribute("data-show", "0");
|
|
5282
|
+
anchor = asRect(box);
|
|
5283
|
+
}
|
|
5284
|
+
var md0 = Date.now();
|
|
5285
|
+
step = "annotate";
|
|
5286
|
+
var imageUrl = SHOT_IMAGE + "?id=" + encodeURIComponent(shot.id);
|
|
5287
|
+
var applyBlob = function(blob) {
|
|
5288
|
+
if (!blob) return null;
|
|
5289
|
+
if (current !== mine) return null;
|
|
5290
|
+
mine.blob = blob;
|
|
5291
|
+
mine.file = new File([blob], "\u622A\u56FE.png", { type: blob.type || "image/png" });
|
|
5292
|
+
mine.error = "";
|
|
5293
|
+
if (chipImg) {
|
|
5294
|
+
if (chipUrl) URL.revokeObjectURL(chipUrl);
|
|
5295
|
+
chipUrl = URL.createObjectURL(blob);
|
|
5296
|
+
chipImg.src = chipUrl;
|
|
5297
|
+
}
|
|
5298
|
+
report({ stage: "bg-done", ms: Date.now() - md0, frame: shot.frame !== false, scope: shot.scope || "" });
|
|
5299
|
+
return null;
|
|
5300
|
+
};
|
|
5301
|
+
var markWorkFailed = function(message, error) {
|
|
5302
|
+
if (current === mine) {
|
|
5303
|
+
mine.error = "image-failed";
|
|
5304
|
+
notice(message);
|
|
5305
|
+
}
|
|
5306
|
+
report({ stage: "bg-fail", ms: Date.now() - md0, error: String(error && error.message || error) });
|
|
5307
|
+
return null;
|
|
5308
|
+
};
|
|
5309
|
+
var startWork = function() {
|
|
5310
|
+
if (shot.frame === false) {
|
|
5311
|
+
return fetch(imageUrl, { cache: "no-store" }).then(function(response) {
|
|
5312
|
+
if (!response.ok) throw new Error("HTTP " + response.status);
|
|
5313
|
+
return response.blob();
|
|
5314
|
+
}).then(applyBlob).catch(function(error) {
|
|
5315
|
+
return markWorkFailed("\u622A\u56FE\u5DF2\u62CD\u4E0B\uFF0C\u4F46\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5", error);
|
|
5316
|
+
});
|
|
5317
|
+
}
|
|
5318
|
+
return loadImage(imageUrl).then(function(img) {
|
|
5319
|
+
return annotate(img, shot.rect, shot.scale, shot);
|
|
5320
|
+
}).then(applyBlob).catch(function(error) {
|
|
5321
|
+
return markWorkFailed("\u622A\u56FE\u5DF2\u62CD\u4E0B\uFF0C\u4F46\u5904\u7406\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5", error);
|
|
5322
|
+
});
|
|
5323
|
+
};
|
|
5324
|
+
current.refReady = new Promise(function(resolve) {
|
|
5325
|
+
var kick = function() {
|
|
5326
|
+
resolve(startWork());
|
|
5327
|
+
};
|
|
5328
|
+
if (panelMode) {
|
|
5329
|
+
kick();
|
|
5330
|
+
return;
|
|
5331
|
+
}
|
|
5332
|
+
if (shot.frame === false) {
|
|
5333
|
+
kick();
|
|
5334
|
+
return;
|
|
5335
|
+
}
|
|
5336
|
+
if (typeof window !== "undefined" && window.requestIdleCallback) {
|
|
5337
|
+
setTimeout(function() {
|
|
5338
|
+
window.requestIdleCallback(kick, { timeout: 1200 });
|
|
5339
|
+
}, 350);
|
|
5340
|
+
} else {
|
|
5341
|
+
setTimeout(kick, 350);
|
|
5342
|
+
}
|
|
5343
|
+
});
|
|
5344
|
+
if (panelMode) {
|
|
5345
|
+
var ready = current.refReady;
|
|
5346
|
+
if (ready) ready.then(function() {
|
|
5347
|
+
if (current !== mine) return;
|
|
5348
|
+
if (mine.file) env.addFiles([mine.file]);
|
|
5349
|
+
else if (!mine.error) notice("\u622A\u56FE\u6CA1\u6709\u751F\u6210\u6210\u529F\uFF0C\u8BF7\u91CD\u8BD5");
|
|
5350
|
+
if (env.focusAskBox) env.focusAskBox();
|
|
5351
|
+
});
|
|
5352
|
+
report({ stage: "done", branch: "panel", scope: shot.scope || "", ms: Date.now() - t0 });
|
|
5353
|
+
return;
|
|
5354
|
+
}
|
|
5355
|
+
env.state.selection = {
|
|
5356
|
+
source: "shot",
|
|
5357
|
+
text: "",
|
|
5358
|
+
label: "\u622A\u56FE",
|
|
5359
|
+
context: "",
|
|
5360
|
+
keyContext: "",
|
|
5361
|
+
range: null,
|
|
5362
|
+
rect: anchor,
|
|
5363
|
+
shot: current,
|
|
5364
|
+
ref: null,
|
|
5365
|
+
// 后续提示词要按取景范围换口径(仅选区没有红框,留白/全屏/整窗口径不同)
|
|
5366
|
+
shotScope: shot.scope || "",
|
|
5367
|
+
shotFrame: shot.frame !== false
|
|
5368
|
+
};
|
|
5369
|
+
step = "openVCard";
|
|
5370
|
+
env.openVCard();
|
|
5371
|
+
var opened = !env.vcardOpen || env.vcardOpen();
|
|
5372
|
+
report({ stage: "done", branch: inside ? "frame" : "chip", opened, ms: Date.now() - t0 });
|
|
5373
|
+
setTimeout(function() {
|
|
5374
|
+
try {
|
|
5375
|
+
var card = env.vcardElement && env.vcardElement();
|
|
5376
|
+
var cinfo = null;
|
|
5377
|
+
if (card) {
|
|
5378
|
+
var cr = card.getBoundingClientRect();
|
|
5379
|
+
var cs = getComputedStyle(card);
|
|
5380
|
+
cinfo = { rect: [Math.round(cr.left), Math.round(cr.top), Math.round(cr.width), Math.round(cr.height)], display: cs.display, opacity: cs.opacity, z: cs.zIndex };
|
|
5381
|
+
}
|
|
5382
|
+
var layerEl = env.layer;
|
|
5383
|
+
report({
|
|
5384
|
+
stage: "probe",
|
|
5385
|
+
open: env.vcardOpen ? !!env.vcardOpen() : null,
|
|
5386
|
+
card: cinfo,
|
|
5387
|
+
layer: layerEl ? { connected: !!layerEl.isConnected, parent: layerEl.parentNode ? layerEl.parentNode.className || layerEl.parentNode.nodeName : null } : null,
|
|
5388
|
+
frame: frameEl ? { show: frameEl.getAttribute("data-show"), connected: !!frameEl.isConnected } : null,
|
|
5389
|
+
vp: [window.innerWidth, window.innerHeight]
|
|
5390
|
+
});
|
|
5391
|
+
} catch (error) {
|
|
5392
|
+
report({ stage: "probe", error: String(error && error.message || error) });
|
|
5393
|
+
}
|
|
5394
|
+
}, 500);
|
|
5395
|
+
if (!opened) {
|
|
5396
|
+
hideMarkers();
|
|
5397
|
+
env.state.selection = null;
|
|
5398
|
+
current = null;
|
|
5399
|
+
}
|
|
5400
|
+
} catch (error) {
|
|
5401
|
+
report({ stage: step, error: String(error && error.stack || error && error.message || error) });
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
function uploadAnnotated(blob) {
|
|
5405
|
+
return new Promise(function(resolve) {
|
|
5406
|
+
var reader = new FileReader();
|
|
5407
|
+
reader.onload = function() {
|
|
5408
|
+
var data = String(reader.result || "");
|
|
5409
|
+
var base64 = data.indexOf(",") >= 0 ? data.slice(data.indexOf(",") + 1) : data;
|
|
5410
|
+
fetch(ATTACH, {
|
|
5411
|
+
method: "POST",
|
|
5412
|
+
headers: { "content-type": "application/json" },
|
|
5413
|
+
body: JSON.stringify({ items: [{ kind: "image", name: "\u622A\u56FE.png", mediaType: "image/png", data: base64 }] })
|
|
5414
|
+
}).then(function(r) {
|
|
5415
|
+
return r.ok ? r.json() : null;
|
|
5416
|
+
}).then(function(json) {
|
|
5417
|
+
var item = json && json.items && json.items[0];
|
|
5418
|
+
resolve(item && item.ref ? item.ref : null);
|
|
5419
|
+
}).catch(function() {
|
|
5420
|
+
resolve(null);
|
|
5421
|
+
});
|
|
5422
|
+
};
|
|
5423
|
+
reader.onerror = function() {
|
|
5424
|
+
resolve(null);
|
|
5425
|
+
};
|
|
5426
|
+
reader.readAsDataURL(blob);
|
|
5427
|
+
});
|
|
5428
|
+
}
|
|
5429
|
+
function loadImage(src, attempt) {
|
|
5430
|
+
var tries = attempt || 0;
|
|
5431
|
+
return new Promise(function(resolve, reject) {
|
|
5432
|
+
var img = new Image();
|
|
5433
|
+
img.onload = function() {
|
|
5434
|
+
resolve(img);
|
|
5435
|
+
};
|
|
5436
|
+
img.onerror = function(error) {
|
|
5437
|
+
if (tries >= 8) {
|
|
5438
|
+
reject(error);
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
5441
|
+
setTimeout(function() {
|
|
5442
|
+
loadImage(src, tries + 1).then(resolve, reject);
|
|
5443
|
+
}, 120);
|
|
5444
|
+
};
|
|
5445
|
+
img.src = src + (src.indexOf("?") >= 0 ? "&" : "?") + "r=" + tries;
|
|
5446
|
+
});
|
|
5447
|
+
}
|
|
5448
|
+
async function poll() {
|
|
5449
|
+
if (busy) return;
|
|
5450
|
+
busy = true;
|
|
5451
|
+
try {
|
|
5452
|
+
var res = await fetch(SHOT_PENDING, { cache: "no-store" });
|
|
5453
|
+
if (res.ok) {
|
|
5454
|
+
var data = await res.json();
|
|
5455
|
+
if (data && data.error) notice(errorText(data.error));
|
|
5456
|
+
if (data && data.shot) await handle(data.shot);
|
|
5457
|
+
}
|
|
5458
|
+
} catch (error) {
|
|
5459
|
+
} finally {
|
|
5460
|
+
busy = false;
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
function loop() {
|
|
5464
|
+
if (superseded()) return;
|
|
5465
|
+
timer = setTimeout(function() {
|
|
5466
|
+
timer = 0;
|
|
5467
|
+
if (superseded()) return;
|
|
5468
|
+
var d = doc();
|
|
5469
|
+
if (d && d.visibilityState === "hidden") return loop();
|
|
5470
|
+
poll().then(loop, loop);
|
|
5471
|
+
}, 30);
|
|
5472
|
+
}
|
|
5473
|
+
return {
|
|
5474
|
+
start: function() {
|
|
5475
|
+
bindCancel();
|
|
5476
|
+
if (timer) return;
|
|
5477
|
+
loop();
|
|
5478
|
+
},
|
|
5479
|
+
stop: function() {
|
|
5480
|
+
if (timer) clearTimeout(timer);
|
|
5481
|
+
timer = 0;
|
|
5482
|
+
hideNotice();
|
|
5483
|
+
},
|
|
5484
|
+
/** 快捷键按下时调用。 */
|
|
5485
|
+
capture: startCapture,
|
|
5486
|
+
clear: clearAll,
|
|
5487
|
+
clearMarkers: hideMarkers,
|
|
5488
|
+
/** 发送时用:当前这张已经画好红框的图(File 与持久引用)。 */
|
|
5489
|
+
takeFile: function() {
|
|
5490
|
+
return current ? current.file : null;
|
|
5491
|
+
},
|
|
5492
|
+
/**
|
|
5493
|
+
* 发送时用:等后台把红框图画好,**此刻才上传**并返回持久引用。
|
|
5494
|
+
*
|
|
5495
|
+
* 为什么不在截完就传:附件库(~/.dsh/attachments)是内容寻址、**只增不删**,
|
|
5496
|
+
* 没有 GC。用户按了 Esc / 点空白取消时,我们不该往里面塞一张永远不会被用到的图。
|
|
5497
|
+
*/
|
|
5498
|
+
takeRefAsync: function() {
|
|
5499
|
+
var here = current;
|
|
5500
|
+
if (!here) return Promise.resolve(null);
|
|
5501
|
+
if (here.error && here.error !== "upload-failed") return Promise.resolve(null);
|
|
5502
|
+
if (here.ref) return Promise.resolve(here.ref);
|
|
5503
|
+
if (here.uploading) return here.uploading;
|
|
5504
|
+
if (!here.refReady) {
|
|
5505
|
+
here.error = "image-failed";
|
|
5506
|
+
notice("\u622A\u56FE\u5DF2\u62CD\u4E0B\uFF0C\u4F46\u8FD8\u6CA1\u6709\u51C6\u5907\u597D\uFF0C\u8BF7\u91CD\u8BD5");
|
|
5507
|
+
return Promise.resolve(null);
|
|
5508
|
+
}
|
|
5509
|
+
here.uploading = here.refReady.then(function() {
|
|
5510
|
+
if (here.ref) return here.ref;
|
|
5511
|
+
if (!here.blob) {
|
|
5512
|
+
if (!here.error) {
|
|
5513
|
+
here.error = "image-failed";
|
|
5514
|
+
notice("\u622A\u56FE\u5DF2\u62CD\u4E0B\uFF0C\u4F46\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
5515
|
+
}
|
|
5516
|
+
return null;
|
|
5517
|
+
}
|
|
5518
|
+
return uploadAnnotated(here.blob).then(function(ref) {
|
|
5519
|
+
if (!ref) {
|
|
5520
|
+
here.error = "upload-failed";
|
|
5521
|
+
here.uploading = null;
|
|
5522
|
+
return null;
|
|
5523
|
+
}
|
|
5524
|
+
here.ref = ref;
|
|
5525
|
+
here.blob = null;
|
|
5526
|
+
here.error = "";
|
|
5527
|
+
report({ stage: "uploaded", ref: true });
|
|
5528
|
+
return ref;
|
|
5529
|
+
});
|
|
5530
|
+
});
|
|
5531
|
+
return here.uploading;
|
|
5532
|
+
},
|
|
5533
|
+
/** 当前这张图是否已经失败(供语音卡在发送时决定"先别开小窗")。 */
|
|
5534
|
+
failed: function() {
|
|
5535
|
+
return current && current.error ? String(current.error) : "";
|
|
5536
|
+
},
|
|
5537
|
+
/** 给语音卡/其它控制器用的提示入口。 */
|
|
5538
|
+
notice,
|
|
5539
|
+
takeRef: function() {
|
|
5540
|
+
return current ? current.ref : null;
|
|
5541
|
+
},
|
|
5542
|
+
state: function() {
|
|
5543
|
+
return {
|
|
5544
|
+
polling: !!timer,
|
|
5545
|
+
lastId,
|
|
5546
|
+
hasFile: !!(current && current.file),
|
|
5547
|
+
error: current && current.error ? String(current.error) : "",
|
|
5548
|
+
frame: frameEl ? frameEl.getAttribute("data-show") === "1" : false,
|
|
5549
|
+
chip: chipEl ? chipEl.getAttribute("data-show") === "1" : false
|
|
5550
|
+
};
|
|
5551
|
+
}
|
|
5552
|
+
};
|
|
5553
|
+
}
|
|
5554
|
+
|
|
4471
5555
|
// src/client/bridge.js
|
|
4472
5556
|
function bridgeBody() {
|
|
4473
5557
|
if (window.__dshSelBridge) return;
|
|
@@ -4613,7 +5697,7 @@ function bridgeBody() {
|
|
|
4613
5697
|
if (!now) return "";
|
|
4614
5698
|
return now.text + "\0" + now.rect.x + "," + now.rect.y + "," + now.rect.bottom + "," + now.rect.right;
|
|
4615
5699
|
}
|
|
4616
|
-
function
|
|
5700
|
+
function report2(allowClear) {
|
|
4617
5701
|
var now = read();
|
|
4618
5702
|
if (!now) {
|
|
4619
5703
|
if (last && allowClear !== false) {
|
|
@@ -4659,10 +5743,10 @@ function bridgeBody() {
|
|
|
4659
5743
|
}, true);
|
|
4660
5744
|
window.addEventListener("mouseup", function() {
|
|
4661
5745
|
pressed = false;
|
|
4662
|
-
setTimeout(
|
|
5746
|
+
setTimeout(report2, 0);
|
|
4663
5747
|
}, true);
|
|
4664
5748
|
window.addEventListener("keyup", function() {
|
|
4665
|
-
setTimeout(
|
|
5749
|
+
setTimeout(report2, 0);
|
|
4666
5750
|
}, true);
|
|
4667
5751
|
window.addEventListener("blur", function() {
|
|
4668
5752
|
pressed = false;
|
|
@@ -4670,29 +5754,29 @@ function bridgeBody() {
|
|
|
4670
5754
|
window.addEventListener("scroll", function() {
|
|
4671
5755
|
if (pressed) return;
|
|
4672
5756
|
setTimeout(function() {
|
|
4673
|
-
|
|
5757
|
+
report2(false);
|
|
4674
5758
|
}, 60);
|
|
4675
5759
|
}, true);
|
|
4676
5760
|
window.addEventListener("resize", function() {
|
|
4677
|
-
if (!pressed)
|
|
5761
|
+
if (!pressed) report2(false);
|
|
4678
5762
|
});
|
|
4679
5763
|
window.addEventListener("message", function(event) {
|
|
4680
5764
|
var data = event && event.data;
|
|
4681
5765
|
if (!data || data.__dshSel !== 1) return;
|
|
4682
5766
|
if (data.kind === "ping") {
|
|
4683
5767
|
send("hello", null);
|
|
4684
|
-
|
|
5768
|
+
report2();
|
|
4685
5769
|
}
|
|
4686
|
-
if (data.kind === "rescan")
|
|
5770
|
+
if (data.kind === "rescan") report2();
|
|
4687
5771
|
});
|
|
4688
5772
|
try {
|
|
4689
5773
|
document.addEventListener("selectionchange", function() {
|
|
4690
|
-
if (!pressed)
|
|
5774
|
+
if (!pressed) report2();
|
|
4691
5775
|
});
|
|
4692
5776
|
} catch (error) {
|
|
4693
5777
|
}
|
|
4694
5778
|
setInterval(tick, 250);
|
|
4695
|
-
setTimeout(
|
|
5779
|
+
setTimeout(report2, 60);
|
|
4696
5780
|
}
|
|
4697
5781
|
var BRIDGE_MARK = "data-dsh-sel-bridge";
|
|
4698
5782
|
function bridgeIntoHtml(html) {
|
|
@@ -4919,7 +6003,7 @@ function createPreviewController(env) {
|
|
|
4919
6003
|
// src/client/panel.js
|
|
4920
6004
|
function createPanelController(env) {
|
|
4921
6005
|
function panelOrCurrentSessionId() {
|
|
4922
|
-
return env.panelSessionId || currentSessionId();
|
|
6006
|
+
return env.state.contextState && typeof env.state.contextState.sessionId === "string" ? env.state.contextState.sessionId : env.panelSessionId || currentSessionId();
|
|
4923
6007
|
}
|
|
4924
6008
|
function currentSessionId() {
|
|
4925
6009
|
try {
|
|
@@ -5014,15 +6098,93 @@ function createPanelController(env) {
|
|
|
5014
6098
|
clearDocSelection();
|
|
5015
6099
|
env.state.selection = null;
|
|
5016
6100
|
}
|
|
6101
|
+
function shotScopeLabel(scope, frame) {
|
|
6102
|
+
if (frame === false || scope === "selection") return "\u4EC5\u9009\u533A";
|
|
6103
|
+
if (scope === "screen") return "\u5168\u5C4F + \u7EA2\u6846";
|
|
6104
|
+
if (scope === "margin") return "\u9009\u533A + \u7559\u767D";
|
|
6105
|
+
return "\u6574\u7A97 + \u7EA2\u6846";
|
|
6106
|
+
}
|
|
6107
|
+
function setQuoteLine(text) {
|
|
6108
|
+
if (!env.quote) return;
|
|
6109
|
+
var title = env.state.shotTitle;
|
|
6110
|
+
if (title) {
|
|
6111
|
+
env.quote.textContent = String(title);
|
|
6112
|
+
env.quote.setAttribute("data-title", "1");
|
|
6113
|
+
return;
|
|
6114
|
+
}
|
|
6115
|
+
env.quote.removeAttribute("data-title");
|
|
6116
|
+
env.quote.textContent = text;
|
|
6117
|
+
}
|
|
6118
|
+
function applyShotTitle(title) {
|
|
6119
|
+
if (!title) return;
|
|
6120
|
+
env.state.shotTitle = String(title);
|
|
6121
|
+
setQuoteLine(String(title));
|
|
6122
|
+
if (env.paintPill) env.paintPill();
|
|
6123
|
+
}
|
|
6124
|
+
function openPanelForShot(anchor, options) {
|
|
6125
|
+
env.panelSessionId = currentSessionId();
|
|
6126
|
+
env.state.contextState = null;
|
|
6127
|
+
env.state.skipInterpretation = true;
|
|
6128
|
+
env.state.payload = buildPayload(String(options.askFirst || ""), "", "\u622A\u56FE");
|
|
6129
|
+
env.state.payload.kind = "shot";
|
|
6130
|
+
env.state.shotScope = options.shotScope || "";
|
|
6131
|
+
env.state.shotFrame = options.shotFrame !== false;
|
|
6132
|
+
if (env.state.shotScope) {
|
|
6133
|
+
env.state.payload.shotScope = env.state.shotScope;
|
|
6134
|
+
env.state.payload.shotFrame = env.state.shotFrame;
|
|
6135
|
+
}
|
|
6136
|
+
env.state.kind = "shot";
|
|
6137
|
+
var shotIdentity = "";
|
|
6138
|
+
if (options && options.shotRef && typeof options.shotRef === "object") {
|
|
6139
|
+
var ref = options.shotRef;
|
|
6140
|
+
shotIdentity = String(ref.attachmentId || ref.id || ref.hash || "");
|
|
6141
|
+
}
|
|
6142
|
+
if (!shotIdentity) {
|
|
6143
|
+
shotIdentity = "shot-" + Date.now().toString(36);
|
|
6144
|
+
}
|
|
6145
|
+
env.state.cacheKey = cacheKeyOf(String(options.askFirst || ""), "shot:" + shotIdentity);
|
|
6146
|
+
env.hideButton();
|
|
6147
|
+
showPanel(anchor);
|
|
6148
|
+
env.state.shotTitle = "";
|
|
6149
|
+
setQuoteLine("\u622A\u56FE \xB7 " + shotScopeLabel(env.state.shotScope, env.state.shotFrame));
|
|
6150
|
+
if (env.paintPill) env.paintPill();
|
|
6151
|
+
env.clearQuotes();
|
|
6152
|
+
env.clearAttachments();
|
|
6153
|
+
env.state.turns = [];
|
|
6154
|
+
env.state.parts = { translation: "", detail: "" };
|
|
6155
|
+
env.state.stage = "";
|
|
6156
|
+
env.state.raw = "";
|
|
6157
|
+
env.state.phase = "done";
|
|
6158
|
+
env.state.chars = 0;
|
|
6159
|
+
env.state.error = "";
|
|
6160
|
+
env.state.fromCache = false;
|
|
6161
|
+
env.state.sharedCache = false;
|
|
6162
|
+
env.state.elapsed = 0;
|
|
6163
|
+
env.state.toolBusy = false;
|
|
6164
|
+
env.state.cacheState = "";
|
|
6165
|
+
env.state.toolDigest = "";
|
|
6166
|
+
env.renderTurns();
|
|
6167
|
+
paint();
|
|
6168
|
+
var refs = options.shotRef ? [{ kind: "image", ref: options.shotRef }] : [];
|
|
6169
|
+
var note = options.shotRef ? "\u3010\u672C\u6761\u6D88\u606F\u5E26\u4E86\u9644\u4EF6\u3011\u622A\u56FE\uFF08\u56FE\u7247\uFF09" : "";
|
|
6170
|
+
env.sendAsk(options.askFirst, null, refs, note);
|
|
6171
|
+
}
|
|
5017
6172
|
function openPanelWithInner(text, context, label, anchor, keyContext, options) {
|
|
6173
|
+
if (options && options.shotRef !== void 0) {
|
|
6174
|
+
openPanelForShot(anchor, options);
|
|
6175
|
+
return;
|
|
6176
|
+
}
|
|
5018
6177
|
env.state.skipInterpretation = !!(options && options.askFirst);
|
|
5019
6178
|
env.panelSessionId = currentSessionId();
|
|
6179
|
+
env.state.contextState = null;
|
|
5020
6180
|
env.state.payload = buildPayload(text, context, label);
|
|
5021
6181
|
env.state.kind = env.state.payload.kind;
|
|
5022
6182
|
env.hideButton();
|
|
5023
6183
|
showPanel(anchor);
|
|
5024
|
-
env.
|
|
6184
|
+
env.state.shotTitle = "";
|
|
6185
|
+
setQuoteLine(text.length > 240 ? text.slice(0, 240) + " \u2026" : text);
|
|
5025
6186
|
applySectionTitle(env.state.kind, text);
|
|
6187
|
+
if (env.paintPill) env.paintPill();
|
|
5026
6188
|
var cacheKey = cacheKeyOf(text, keyContext === void 0 ? context : keyContext);
|
|
5027
6189
|
var cached = env.cache.get(cacheKey);
|
|
5028
6190
|
env.state.cacheKey = cacheKey;
|
|
@@ -5282,12 +6444,12 @@ function createPanelController(env) {
|
|
|
5282
6444
|
env.panel.style.transition = "transform " + PANEL_GROW_MS + "ms " + PANEL_GROW_EASE;
|
|
5283
6445
|
env.panel.style.transform = "none";
|
|
5284
6446
|
if (env.panelGrowTimer) {
|
|
5285
|
-
|
|
6447
|
+
env.timerScope.clear(env.panelGrowTimer);
|
|
5286
6448
|
env.panelGrowTimer = null;
|
|
5287
6449
|
}
|
|
5288
6450
|
var settle = function() {
|
|
5289
6451
|
if (env.panelGrowTimer) {
|
|
5290
|
-
|
|
6452
|
+
env.timerScope.clear(env.panelGrowTimer);
|
|
5291
6453
|
env.panelGrowTimer = null;
|
|
5292
6454
|
}
|
|
5293
6455
|
if (env.panelGrowHandler) {
|
|
@@ -5306,11 +6468,12 @@ function createPanelController(env) {
|
|
|
5306
6468
|
}
|
|
5307
6469
|
function closePanel() {
|
|
5308
6470
|
env.closeModelMenu();
|
|
6471
|
+
if (typeof env.closeLightbox === "function") env.closeLightbox();
|
|
5309
6472
|
env.closeSettings();
|
|
5310
6473
|
env.panelOpen = false;
|
|
5311
6474
|
env.panel.style.display = "none";
|
|
5312
6475
|
if (env.panelGrowTimer) {
|
|
5313
|
-
|
|
6476
|
+
env.timerScope.clear(env.panelGrowTimer);
|
|
5314
6477
|
env.panelGrowTimer = null;
|
|
5315
6478
|
}
|
|
5316
6479
|
if (env.panelGrowHandler) {
|
|
@@ -5325,9 +6488,16 @@ function createPanelController(env) {
|
|
|
5325
6488
|
env.hideQuoteButton();
|
|
5326
6489
|
env.state.quoteSelection = null;
|
|
5327
6490
|
env.cancelVoice();
|
|
5328
|
-
if (env.state.request)
|
|
5329
|
-
|
|
5330
|
-
|
|
6491
|
+
if (env.state.request) {
|
|
6492
|
+
env.state.aborted = true;
|
|
6493
|
+
env.state.request.abort();
|
|
6494
|
+
env.state.request = null;
|
|
6495
|
+
}
|
|
6496
|
+
if (env.state.askRequest) {
|
|
6497
|
+
env.state.stopped = true;
|
|
6498
|
+
env.state.askRequest.abort();
|
|
6499
|
+
}
|
|
6500
|
+
releaseRenderedContent();
|
|
5331
6501
|
env.paintPill();
|
|
5332
6502
|
env.syncRouteWatch();
|
|
5333
6503
|
}
|
|
@@ -5356,7 +6526,20 @@ function createPanelController(env) {
|
|
|
5356
6526
|
var what = env.state.toolBusy ? "\u6B63\u5728\u68C0\u7D22\u8D44\u6599\u2026" : env.state.phase === "loading" ? "\u6B63\u5728\u5206\u6790\u2026" : "\u751F\u6210\u4E2D\u2026";
|
|
5357
6527
|
return what + " " + seconds + "s";
|
|
5358
6528
|
}
|
|
6529
|
+
function releaseRenderedContent() {
|
|
6530
|
+
env.clearWaiting();
|
|
6531
|
+
env.clearRenderedTurns();
|
|
6532
|
+
for (var content of [env.translationSection.content, env.detailSection.content]) {
|
|
6533
|
+
releasePreviewFrames(content);
|
|
6534
|
+
content.textContent = "";
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
5359
6537
|
function paint() {
|
|
6538
|
+
if (!env.panelOpen) {
|
|
6539
|
+
releaseRenderedContent();
|
|
6540
|
+
env.paintPill();
|
|
6541
|
+
return;
|
|
6542
|
+
}
|
|
5360
6543
|
var streaming = env.state.phase === "loading" || env.state.phase === "streaming";
|
|
5361
6544
|
var stage = env.state.stage;
|
|
5362
6545
|
var selected = env.state.payload && env.state.payload.text ? env.state.payload.text : env.state.selection ? env.state.selection.text : "";
|
|
@@ -5555,6 +6738,8 @@ function createPanelController(env) {
|
|
|
5555
6738
|
env.panel.style.top = Math.round(top) + "px";
|
|
5556
6739
|
}
|
|
5557
6740
|
return {
|
|
6741
|
+
applyShotTitle,
|
|
6742
|
+
releaseRenderedContent,
|
|
5558
6743
|
openForSelection,
|
|
5559
6744
|
currentSessionId,
|
|
5560
6745
|
loadPanelSize,
|
|
@@ -5606,7 +6791,9 @@ function createPanelController(env) {
|
|
|
5606
6791
|
// src/client/pill.js
|
|
5607
6792
|
function createPillController(env) {
|
|
5608
6793
|
function pillSnippet() {
|
|
5609
|
-
var
|
|
6794
|
+
var title = env.state.shotTitle ? String(env.state.shotTitle) : "";
|
|
6795
|
+
title = title.replace(/\s+/g, " ").trim();
|
|
6796
|
+
var text = title || (env.state.payload && env.state.payload.text ? String(env.state.payload.text) : "");
|
|
5610
6797
|
text = text.replace(/\s+/g, " ").trim();
|
|
5611
6798
|
if (!text) return "";
|
|
5612
6799
|
return text.length > 12 ? text.slice(0, 12) + "\u2026" : text;
|
|
@@ -5642,7 +6829,7 @@ function createPillController(env) {
|
|
|
5642
6829
|
var caret = env.panelOpen ? "\u25BE" : "\u25B4";
|
|
5643
6830
|
if (env.pillCaret.textContent !== caret) env.pillCaret.textContent = caret;
|
|
5644
6831
|
env.pill.title = [
|
|
5645
|
-
snippet ? "\u6700\u8FD1\u4E00\u6B21\u5212\u8BCD\uFF1A" + snippet : "\u8FD8\u6CA1\u6709\u5212\u8FC7\u8BCD",
|
|
6832
|
+
snippet ? (env.state.shotTitle ? "\u6700\u8FD1\u4E00\u6B21\u622A\u56FE\uFF1A" : "\u6700\u8FD1\u4E00\u6B21\u5212\u8BCD\uFF1A") + snippet : "\u8FD8\u6CA1\u6709\u5212\u8FC7\u8BCD",
|
|
5646
6833
|
status.text,
|
|
5647
6834
|
env.panelOpen ? "\u70B9\u51FB\u6536\u8D77\u5C0F\u7A97" : "\u70B9\u51FB\u56DE\u5230\u8FD9\u4E2A\u5C0F\u7A97",
|
|
5648
6835
|
"\u53EF\u62D6\u52A8\uFF08\u62D6\u5230\u53F3\u4E0B\u89D2\u9644\u8FD1\u4F1A\u5438\u9644\u56DE\u53BB\uFF09"
|
|
@@ -6143,10 +7330,71 @@ function createPillController(env) {
|
|
|
6143
7330
|
};
|
|
6144
7331
|
}
|
|
6145
7332
|
|
|
7333
|
+
// src/chat-context.ts
|
|
7334
|
+
var CHAT_REQUEST_BYTES = 512 * 1024;
|
|
7335
|
+
var EMPTY_DIGEST = "811c9dc5";
|
|
7336
|
+
function readContextState(raw) {
|
|
7337
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7338
|
+
const s = raw;
|
|
7339
|
+
if (s.version !== 1 || !Number.isSafeInteger(s.through) || s.through < 0 || !/^[a-f0-9]{8}$/.test(s.source) || !/^[a-f0-9]{8}$/.test(s.digest) || typeof s.summary !== "string" || s.summary.length > 12e3 || s.through > 0 && !s.summary.trim()) return null;
|
|
7340
|
+
if (s.background !== null && (!s.background || typeof s.background.transcript !== "string" || s.background.transcript.length > 2e5 || typeof s.background.marked !== "boolean")) return null;
|
|
7341
|
+
if (s.sessionId !== void 0 && (typeof s.sessionId !== "string" || s.sessionId.length > 500)) return null;
|
|
7342
|
+
return {
|
|
7343
|
+
version: 1,
|
|
7344
|
+
...typeof s.sessionId === "string" ? { sessionId: s.sessionId } : {},
|
|
7345
|
+
source: s.source,
|
|
7346
|
+
through: s.through,
|
|
7347
|
+
digest: s.digest,
|
|
7348
|
+
summary: s.summary,
|
|
7349
|
+
background: s.background ? { transcript: s.background.transcript, marked: s.background.marked } : null
|
|
7350
|
+
};
|
|
7351
|
+
}
|
|
7352
|
+
function modelTurn(turn) {
|
|
7353
|
+
if (turn.contextExcluded) return {
|
|
7354
|
+
role: turn.role === "assistant" ? "assistant" : "user",
|
|
7355
|
+
text: turn.role === "assistant" ? "\uFF08\u8BE5\u8F6E\u672A\u751F\u6210\u56DE\u7B54\u3002\uFF09" : "\uFF08\u8BE5\u63D0\u95EE\u672A\u63D0\u4EA4\u7ED9\u6A21\u578B\uFF0C\u539F\u59CB\u5185\u5BB9\u4ECD\u4FDD\u5B58\u5728\u754C\u9762\u5386\u53F2\u4E2D\uFF1B\u9700\u8981\u65F6\u8BF7\u91CD\u65B0\u5F15\u7528\u5FC5\u8981\u90E8\u5206\u3002\uFF09"
|
|
7356
|
+
};
|
|
7357
|
+
return {
|
|
7358
|
+
role: turn.role === "assistant" ? "assistant" : "user",
|
|
7359
|
+
text: turn.sent || turn.text || "",
|
|
7360
|
+
...turn.attachments?.length ? { attachments: turn.attachments } : {}
|
|
7361
|
+
};
|
|
7362
|
+
}
|
|
7363
|
+
function historyDigest(turns, initial = EMPTY_DIGEST) {
|
|
7364
|
+
let hash = Number.parseInt(initial, 16) >>> 0;
|
|
7365
|
+
for (const raw of turns) {
|
|
7366
|
+
const t = modelTurn(raw);
|
|
7367
|
+
const text = JSON.stringify([t.role, t.text, (t.attachments || []).map((a) => [a.kind, a.ref.attachmentId])]) + "\n";
|
|
7368
|
+
for (let i = 0; i < text.length; i++) hash = Math.imul(hash ^ text.charCodeAt(i), 16777619) >>> 0;
|
|
7369
|
+
}
|
|
7370
|
+
return hash.toString(16).padStart(8, "0");
|
|
7371
|
+
}
|
|
7372
|
+
function contextSource(session, text, context, initial) {
|
|
7373
|
+
return historyDigest([{ role: "user", text: JSON.stringify([session, text, context, initial]) }]);
|
|
7374
|
+
}
|
|
7375
|
+
function prepareChatHistory(turns, rawState) {
|
|
7376
|
+
const visible = turns.filter((t) => !t.hidden);
|
|
7377
|
+
let state = readContextState(rawState);
|
|
7378
|
+
if (state && (state.through > visible.length || historyDigest(visible.slice(0, state.through)) !== state.digest)) state = null;
|
|
7379
|
+
const start = state?.through || 0;
|
|
7380
|
+
const materials = [];
|
|
7381
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7382
|
+
visible.forEach((turn, index) => {
|
|
7383
|
+
for (const item of turn.attachments || []) {
|
|
7384
|
+
const key = item.kind + ":" + item.ref.attachmentId;
|
|
7385
|
+
if (!seen.has(key)) {
|
|
7386
|
+
seen.add(key);
|
|
7387
|
+
materials.push({ kind: item.kind, ref: item.ref, turn: index });
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
7390
|
+
});
|
|
7391
|
+
return { history: visible.slice(start).map(modelTurn), historyStart: start, contextState: state, materials };
|
|
7392
|
+
}
|
|
7393
|
+
|
|
6146
7394
|
// src/client/history.js
|
|
6147
7395
|
function createHistoryController(env) {
|
|
6148
|
-
var FOLD_HISTORY_KEEP_LATEST = true;
|
|
6149
7396
|
var foldCount = 0;
|
|
7397
|
+
var shotTitlePendingKey = "";
|
|
6150
7398
|
var HTML_VOID_TAGS = {
|
|
6151
7399
|
area: 1,
|
|
6152
7400
|
base: 1,
|
|
@@ -6426,16 +7674,6 @@ function createHistoryController(env) {
|
|
|
6426
7674
|
src = src.replace(/```html[ \t]*\n([\s\S]*)$/, one);
|
|
6427
7675
|
return src;
|
|
6428
7676
|
}
|
|
6429
|
-
function historyTextOf(turn, keepRaw) {
|
|
6430
|
-
if (!turn) return "";
|
|
6431
|
-
if (turn.role === "user") return env.sentTextOf(turn);
|
|
6432
|
-
if (keepRaw) return turn.text;
|
|
6433
|
-
if (typeof turn.foldedCache === "string") return turn.foldedCache;
|
|
6434
|
-
if (turn.streaming === true) return turn.text;
|
|
6435
|
-
var folded = foldForHistory(turn.text);
|
|
6436
|
-
turn.foldedCache = folded;
|
|
6437
|
-
return folded;
|
|
6438
|
-
}
|
|
6439
7677
|
function currentSurfaceTheme() {
|
|
6440
7678
|
try {
|
|
6441
7679
|
var bodyEl = document.body;
|
|
@@ -6464,6 +7702,7 @@ function createHistoryController(env) {
|
|
|
6464
7702
|
function visibleRaw() {
|
|
6465
7703
|
return env.state.raw;
|
|
6466
7704
|
}
|
|
7705
|
+
var historySaveSequence = 0;
|
|
6467
7706
|
function saveHistory(options) {
|
|
6468
7707
|
var payload = env.state.payload;
|
|
6469
7708
|
if (!payload || !env.state.cacheKey) return;
|
|
@@ -6474,28 +7713,155 @@ function createHistoryController(env) {
|
|
|
6474
7713
|
if (turn.hidden === true) continue;
|
|
6475
7714
|
turns.push(exported[i]);
|
|
6476
7715
|
}
|
|
7716
|
+
var owner = env.state.turns;
|
|
7717
|
+
var ownerKey = env.state.cacheKey;
|
|
7718
|
+
var sequence = ++historySaveSequence;
|
|
7719
|
+
function noteSaved(error) {
|
|
7720
|
+
if (sequence !== historySaveSequence || env.state.turns !== owner || env.state.cacheKey !== ownerKey) return;
|
|
7721
|
+
var last = owner[owner.length - 1];
|
|
7722
|
+
if (!last || last.role !== "assistant") return;
|
|
7723
|
+
if (error) {
|
|
7724
|
+
last.notice = "\u5386\u53F2\u8BB0\u5F55\u4FDD\u5B58\u5931\u8D25\uFF0C\u5F53\u524D\u539F\u6587\u4ECD\u5728\u5C0F\u7A97\u4E2D\uFF1B\u5237\u65B0\u6216\u9000\u51FA\u524D\u8BF7\u5148\u590D\u5236\u91CD\u8981\u5185\u5BB9\u3002";
|
|
7725
|
+
last.noticeCode = "history-save-failed";
|
|
7726
|
+
} else if (last.noticeCode === "history-save-failed") {
|
|
7727
|
+
last.notice = "";
|
|
7728
|
+
last.noticeCode = "";
|
|
7729
|
+
} else return;
|
|
7730
|
+
renderTurns();
|
|
7731
|
+
}
|
|
7732
|
+
var savedBody = {
|
|
7733
|
+
key: env.state.cacheKey,
|
|
7734
|
+
text: payload.text,
|
|
7735
|
+
context: payload.context,
|
|
7736
|
+
// 截图有标题时写进 label(旧列表/旧客户端也能显示);标题本体另存 title
|
|
7737
|
+
label: payload.kind === "shot" && String(env.state.shotTitle || "").trim() ? "\u622A\u56FE \xB7 " + String(env.state.shotTitle).trim() : payload.label,
|
|
7738
|
+
...payload.kind === "shot" && String(env.state.shotTitle || "").trim() ? { title: String(env.state.shotTitle).trim() } : {},
|
|
7739
|
+
parts: env.state.parts,
|
|
7740
|
+
turns,
|
|
7741
|
+
toolDigest: env.state.toolDigest,
|
|
7742
|
+
contextState: env.state.contextState || null,
|
|
7743
|
+
pinned: !!(options && options.pinned),
|
|
7744
|
+
// 这段对话有没有解读,必须跟着条目落盘:回放时靠它决定画不画「翻译」一节。
|
|
7745
|
+
// 不落盘的话,语音卡那条"跳过翻译与详解、直接追问"的对话下次开出来,
|
|
7746
|
+
// 会顶着一张空翻译卡片("这次没有返回内容")——用户实测。
|
|
7747
|
+
skipped: env.state.skipInterpretation === true,
|
|
7748
|
+
// 截图取景范围:标题那次请求要带上同样的 scope,才能得到同一个 SHOT_PROMPT 口径
|
|
7749
|
+
...payload.kind === "shot" && env.state.shotScope ? { shotScope: env.state.shotScope, shotFrame: env.state.shotFrame !== false } : {}
|
|
7750
|
+
};
|
|
6477
7751
|
try {
|
|
6478
7752
|
fetch(HISTORY, {
|
|
6479
7753
|
method: "POST",
|
|
6480
7754
|
headers: { "content-type": "application/json" },
|
|
6481
|
-
body: JSON.stringify(
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
// 会顶着一张空翻译卡片("这次没有返回内容")——用户实测。
|
|
6493
|
-
skipped: env.state.skipInterpretation === true
|
|
6494
|
-
})
|
|
7755
|
+
body: JSON.stringify(savedBody)
|
|
7756
|
+
}).then(function(response) {
|
|
7757
|
+
if (!response.ok) throw new Error("HTTP " + response.status);
|
|
7758
|
+
return response.json();
|
|
7759
|
+
}).then(function(result) {
|
|
7760
|
+
var failed = !result || result.ok !== true;
|
|
7761
|
+
noteSaved(failed);
|
|
7762
|
+
if (!failed && payload.kind === "shot" && !String(env.state.shotTitle || "").trim() && shotTitlePendingKey !== savedBody.key) {
|
|
7763
|
+
shotTitlePendingKey = savedBody.key;
|
|
7764
|
+
requestShotTitle(savedBody, payload.sessionId);
|
|
7765
|
+
}
|
|
6495
7766
|
}).catch(function() {
|
|
7767
|
+
noteSaved(true);
|
|
6496
7768
|
});
|
|
6497
7769
|
} catch (error) {
|
|
7770
|
+
noteSaved(true);
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
7773
|
+
function requestShotTitle(saved, sessionId, attempt) {
|
|
7774
|
+
var tries = attempt || 0;
|
|
7775
|
+
var userTurn = null;
|
|
7776
|
+
var list = saved.turns || [];
|
|
7777
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
7778
|
+
if (list[i] && list[i].role === "user" && list[i].attachments && list[i].attachments.length > 0) {
|
|
7779
|
+
userTurn = list[i];
|
|
7780
|
+
break;
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
if (!userTurn) return;
|
|
7784
|
+
var savedKey = saved.key;
|
|
7785
|
+
fetch(API, {
|
|
7786
|
+
method: "POST",
|
|
7787
|
+
headers: { "content-type": "application/json" },
|
|
7788
|
+
body: JSON.stringify({
|
|
7789
|
+
text: saved.text,
|
|
7790
|
+
kind: "shot",
|
|
7791
|
+
titleOnly: true,
|
|
7792
|
+
label: "\u622A\u56FE",
|
|
7793
|
+
attachments: userTurn.attachments,
|
|
7794
|
+
// 与回答那次同一个取景口径:标题是最好的"红框/仅选区"措辞,换口径会问错东西
|
|
7795
|
+
...saved.shotScope ? { shotScope: saved.shotScope, shotFrame: saved.shotFrame !== false } : {},
|
|
7796
|
+
// ⚠️ 必须带 sessionId:不带会被上游 400 拒绝(MissingSessionID),
|
|
7797
|
+
// 而 `env.panelSessionId` 在历史模块里并不存在 —— 用的是保存时那份 payload 里的。
|
|
7798
|
+
...sessionId ? { sessionId } : {},
|
|
7799
|
+
// ⚠️ 必须带上面板当前用的模型:不带的话 host 会退回"会话默认模型",
|
|
7800
|
+
// 那个路由可能不支持图片 → 上游报错 → 正文为空 → 标题永远写不回去(实测踩到)。
|
|
7801
|
+
...env.state.modelChoice ? { provider: env.state.modelChoice.provider, model: env.state.modelChoice.model } : {}
|
|
7802
|
+
})
|
|
7803
|
+
}).then(function(response) {
|
|
7804
|
+
return response.ok ? response.text() : "";
|
|
7805
|
+
}).then(function(raw) {
|
|
7806
|
+
var title = cleanShotTitle(readSseText(raw));
|
|
7807
|
+
if (!title) throw new Error("empty-title");
|
|
7808
|
+
if (shotTitlePendingKey === savedKey) shotTitlePendingKey = "";
|
|
7809
|
+
if (env.state.cacheKey === savedKey && env.applyShotTitle) env.applyShotTitle(title);
|
|
7810
|
+
writeShotTitle(savedKey, title);
|
|
7811
|
+
}).catch(function() {
|
|
7812
|
+
if (tries >= 1) {
|
|
7813
|
+
if (shotTitlePendingKey === savedKey) shotTitlePendingKey = "";
|
|
7814
|
+
return;
|
|
7815
|
+
}
|
|
7816
|
+
setTimeout(function() {
|
|
7817
|
+
requestShotTitle(saved, sessionId, tries + 1);
|
|
7818
|
+
}, 2e3);
|
|
7819
|
+
});
|
|
7820
|
+
}
|
|
7821
|
+
function writeShotTitle(key, title) {
|
|
7822
|
+
fetch(HISTORY + "?key=" + encodeURIComponent(key)).then(function(response) {
|
|
7823
|
+
return response.ok ? response.json() : null;
|
|
7824
|
+
}).then(function(data) {
|
|
7825
|
+
var entry = data && data.entry;
|
|
7826
|
+
if (!entry) return;
|
|
7827
|
+
return fetch(HISTORY, {
|
|
7828
|
+
method: "POST",
|
|
7829
|
+
headers: { "content-type": "application/json" },
|
|
7830
|
+
// title 是权威字段;label 继续写成「截图 · 标题」兼容旧列表/旧版本
|
|
7831
|
+
body: JSON.stringify(Object.assign({}, entry, { title, label: "\u622A\u56FE \xB7 " + title }))
|
|
7832
|
+
});
|
|
7833
|
+
}).then(function(response) {
|
|
7834
|
+
if (!response || !response.ok) return;
|
|
7835
|
+
if (env.historyList && env.historyList.style.display === "flex") openHistoryList();
|
|
7836
|
+
}).catch(function() {
|
|
7837
|
+
});
|
|
7838
|
+
}
|
|
7839
|
+
function readSseText(raw) {
|
|
7840
|
+
var out = [];
|
|
7841
|
+
var lines = String(raw || "").split("\n");
|
|
7842
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
7843
|
+
var line = lines[i].trim();
|
|
7844
|
+
if (line.indexOf("data:") !== 0) continue;
|
|
7845
|
+
var chunk = line.slice(5).trim();
|
|
7846
|
+
if (!chunk || chunk === "[DONE]") continue;
|
|
7847
|
+
try {
|
|
7848
|
+
var obj = JSON.parse(chunk);
|
|
7849
|
+
if (obj.type === "thought") continue;
|
|
7850
|
+
if (typeof obj.delta === "string") out.push(obj.delta);
|
|
7851
|
+
else if (obj.type === "delta" && typeof obj.text === "string") out.push(obj.text);
|
|
7852
|
+
} catch (error) {
|
|
7853
|
+
}
|
|
6498
7854
|
}
|
|
7855
|
+
return out.join("");
|
|
7856
|
+
}
|
|
7857
|
+
function cleanShotTitle(raw) {
|
|
7858
|
+
var title = String(raw || "").trim();
|
|
7859
|
+
title = title.replace(/^标题[::]\s*/, "");
|
|
7860
|
+
title = title.replace(/^["'「『【]+|["'」』】]+$/g, "");
|
|
7861
|
+
title = title.replace(/[。!?.!?,,、::;;…]+$/g, "");
|
|
7862
|
+
title = title.replace(/\s+/g, " ").trim();
|
|
7863
|
+
if (!title) return "";
|
|
7864
|
+
return title.length > 24 ? title.slice(0, 24) : title;
|
|
6499
7865
|
}
|
|
6500
7866
|
function applyHistoryEntry(entry, source) {
|
|
6501
7867
|
var parts = entry.parts || {};
|
|
@@ -6510,6 +7876,8 @@ function createHistoryController(env) {
|
|
|
6510
7876
|
}
|
|
6511
7877
|
var noInterpretation = !String(parts.translation || "").trim() && !String(parts.detail || "").trim();
|
|
6512
7878
|
env.state.skipInterpretation = entry.skipped === true || entry.skipped === void 0 && noInterpretation && hasAskTurn;
|
|
7879
|
+
env.state.contextState = readContextState(entry.contextState);
|
|
7880
|
+
if (env.state.contextState && typeof env.state.contextState.sessionId === "string") env.panelSessionId = env.state.contextState.sessionId;
|
|
6513
7881
|
env.state.turns = Array.isArray(entry.turns) ? entry.turns.slice() : [];
|
|
6514
7882
|
env.clearQuotes();
|
|
6515
7883
|
env.state.toolDigest = typeof entry.toolDigest === "string" ? entry.toolDigest : "";
|
|
@@ -6528,10 +7896,26 @@ function createHistoryController(env) {
|
|
|
6528
7896
|
env.state.toolBusy = false;
|
|
6529
7897
|
env.state.cacheState = source === "local" ? "local" : "history";
|
|
6530
7898
|
env.state.chars = (env.state.parts.translation || "").length + (env.state.parts.detail || "").length;
|
|
6531
|
-
|
|
7899
|
+
var replayTitle = typeof entry.title === "string" ? entry.title.trim() : "";
|
|
7900
|
+
if (!replayTitle) {
|
|
7901
|
+
var titleMatch = /^截图 · (.+)$/.exec(String(entry.label || ""));
|
|
7902
|
+
if (titleMatch) replayTitle = titleMatch[1];
|
|
7903
|
+
}
|
|
7904
|
+
env.state.shotTitle = replayTitle;
|
|
7905
|
+
if (replayTitle) {
|
|
7906
|
+
if (env.applyShotTitle) env.applyShotTitle(replayTitle);
|
|
7907
|
+
else {
|
|
7908
|
+
env.quote.textContent = replayTitle;
|
|
7909
|
+
env.quote.setAttribute("data-title", "1");
|
|
7910
|
+
}
|
|
7911
|
+
} else {
|
|
7912
|
+
env.quote.textContent = entry.text.length > 240 ? entry.text.slice(0, 240) + " \u2026" : entry.text;
|
|
7913
|
+
env.quote.removeAttribute("data-title");
|
|
7914
|
+
}
|
|
6532
7915
|
env.applySectionTitle(env.state.kind, entry.text);
|
|
6533
7916
|
hideHistoryList();
|
|
6534
7917
|
env.paint();
|
|
7918
|
+
if (env.paintPill) env.paintPill();
|
|
6535
7919
|
renderTurns();
|
|
6536
7920
|
}
|
|
6537
7921
|
function restoreFromHistory(cacheKey, onMiss) {
|
|
@@ -6541,8 +7925,7 @@ function createHistoryController(env) {
|
|
|
6541
7925
|
settled = true;
|
|
6542
7926
|
if (env.state.cacheKey === cacheKey && onMiss) onMiss();
|
|
6543
7927
|
}
|
|
6544
|
-
var timer =
|
|
6545
|
-
env.timers.push(timer);
|
|
7928
|
+
var timer = env.timerScope.later(miss, 1500);
|
|
6546
7929
|
try {
|
|
6547
7930
|
fetch(HISTORY + "?key=" + encodeURIComponent(cacheKey)).then(function(response) {
|
|
6548
7931
|
return response.json();
|
|
@@ -6554,7 +7937,7 @@ function createHistoryController(env) {
|
|
|
6554
7937
|
}
|
|
6555
7938
|
if (data && data.ok === true && data.entry) {
|
|
6556
7939
|
settled = true;
|
|
6557
|
-
|
|
7940
|
+
env.timerScope.clear(timer);
|
|
6558
7941
|
applyHistoryEntry(data.entry, "history");
|
|
6559
7942
|
return;
|
|
6560
7943
|
}
|
|
@@ -6639,8 +8022,11 @@ function createHistoryController(env) {
|
|
|
6639
8022
|
}
|
|
6640
8023
|
function historyRow(item) {
|
|
6641
8024
|
var row = el("div", "dsh-sel-historyrow");
|
|
6642
|
-
var
|
|
6643
|
-
|
|
8025
|
+
var shotTitle = typeof item.title === "string" ? item.title.trim() : "";
|
|
8026
|
+
var shotMatch = /^截图 · (.+)$/.exec(String(item.label || ""));
|
|
8027
|
+
var isShot = !!shotTitle || !!shotMatch;
|
|
8028
|
+
var main = shotTitle || (shotMatch ? shotMatch[1] : String(item.text || "").trim());
|
|
8029
|
+
row.appendChild(el("div", "dsh-sel-historytext", main || "\uFF08\u8FD9\u6B21\u6CA1\u9009\u4E2D\u6587\u5B57\uFF09"));
|
|
6644
8030
|
var del = el("button", "dsh-sel-historydel", "\u2715");
|
|
6645
8031
|
del.type = "button";
|
|
6646
8032
|
del.title = "\u5220\u9664\u8FD9\u6761\u8BB0\u5F55";
|
|
@@ -6651,7 +8037,8 @@ function createHistoryController(env) {
|
|
|
6651
8037
|
deleteHistoryEntry(item, row);
|
|
6652
8038
|
});
|
|
6653
8039
|
row.appendChild(del);
|
|
6654
|
-
var
|
|
8040
|
+
var metaLabel = isShot ? "\u622A\u56FE" : shortLabel(item.label);
|
|
8041
|
+
var meta = (metaLabel ? metaLabel + " \xB7 " : "") + (item.turns > 0 ? item.turns + " \u8F6E \xB7 " : "") + formatWhen(item.at);
|
|
6655
8042
|
var metaNode = el("div", "dsh-sel-historymeta");
|
|
6656
8043
|
metaNode.appendChild(el("span", null, meta));
|
|
6657
8044
|
if (item.pinned) metaNode.appendChild(el("span", "dsh-sel-historypin", "\u5DF2\u5347\u683C"));
|
|
@@ -6709,7 +8096,7 @@ function createHistoryController(env) {
|
|
|
6709
8096
|
});
|
|
6710
8097
|
bar.appendChild(undo);
|
|
6711
8098
|
env.historyList.appendChild(bar);
|
|
6712
|
-
|
|
8099
|
+
env.timerScope.later(function() {
|
|
6713
8100
|
if (bar.parentNode) bar.parentNode.removeChild(bar);
|
|
6714
8101
|
}, 5e3);
|
|
6715
8102
|
}
|
|
@@ -6841,24 +8228,81 @@ function createHistoryController(env) {
|
|
|
6841
8228
|
if (Date.now() - stickAt < 150) return;
|
|
6842
8229
|
env.state.follow = nearBottom();
|
|
6843
8230
|
}
|
|
8231
|
+
var turnWindowSize = 40;
|
|
8232
|
+
var turnWindowKey = null;
|
|
8233
|
+
var renderedTurns = [];
|
|
8234
|
+
function clearRenderedTurns() {
|
|
8235
|
+
env.clearWaiting(env.chatLog);
|
|
8236
|
+
releasePreviewFrames(env.chatLog);
|
|
8237
|
+
env.chatLog.textContent = "";
|
|
8238
|
+
renderedTurns = [];
|
|
8239
|
+
turnWindowSize = 40;
|
|
8240
|
+
turnWindowKey = null;
|
|
8241
|
+
}
|
|
6844
8242
|
function renderTurns(options) {
|
|
8243
|
+
if (!env.panelOpen) {
|
|
8244
|
+
clearRenderedTurns();
|
|
8245
|
+
return;
|
|
8246
|
+
}
|
|
6845
8247
|
var force = !!(options && options.stick === true);
|
|
6846
|
-
var stick = force || nearBottom();
|
|
8248
|
+
var stick = !(options && options.preservePosition) && (force || nearBottom());
|
|
6847
8249
|
var visible = [];
|
|
6848
8250
|
for (var i = 0; i < env.state.turns.length; i++) {
|
|
6849
8251
|
if (env.state.turns[i].hidden !== true) visible.push(env.state.turns[i]);
|
|
6850
8252
|
}
|
|
6851
|
-
if (
|
|
8253
|
+
if (turnWindowKey !== env.state.cacheKey) {
|
|
8254
|
+
turnWindowKey = env.state.cacheKey;
|
|
8255
|
+
turnWindowSize = 40;
|
|
8256
|
+
}
|
|
8257
|
+
var earlier = Math.max(0, visible.length - turnWindowSize);
|
|
8258
|
+
visible = visible.slice(earlier);
|
|
8259
|
+
var sameWindow = renderedTurns.length === visible.length && visible.every(function(turn2, index) {
|
|
8260
|
+
return renderedTurns[index] === turn2;
|
|
8261
|
+
});
|
|
8262
|
+
var prependCount = visible.length - renderedTurns.length;
|
|
8263
|
+
var canPrepend = options && options.prepend && renderedTurns.length > 0 && prependCount > 0 && renderedTurns.every(function(turn2, index) {
|
|
8264
|
+
return visible[prependCount + index] === turn2;
|
|
8265
|
+
});
|
|
8266
|
+
if (canPrepend) {
|
|
8267
|
+
var moreButton = env.chatLog.children[0];
|
|
8268
|
+
var firstBubble = env.chatLog.children[1];
|
|
8269
|
+
if (earlier > 0) moreButton.textContent = "\u663E\u793A\u66F4\u65E9\u7684\u6D88\u606F\uFF08\u8FD8\u6709 " + earlier + " \u6761\uFF09";
|
|
8270
|
+
else env.chatLog.removeChild(moreButton);
|
|
8271
|
+
for (var p = 0; p < prependCount; p++) {
|
|
8272
|
+
var prepended = bubbleFor(visible[p]);
|
|
8273
|
+
prepended.__turn = visible[p];
|
|
8274
|
+
env.chatLog.insertBefore(prepended, firstBubble);
|
|
8275
|
+
}
|
|
8276
|
+
renderedTurns = visible.slice();
|
|
8277
|
+
} else if (!sameWindow || env.chatLog.children.length !== visible.length + (earlier > 0 ? 1 : 0)) {
|
|
6852
8278
|
env.clearWaiting(env.chatLog);
|
|
8279
|
+
releasePreviewFrames(env.chatLog);
|
|
6853
8280
|
env.chatLog.textContent = "";
|
|
8281
|
+
if (earlier > 0) {
|
|
8282
|
+
var more = el("button", "dsh-sel-expand", "\u663E\u793A\u66F4\u65E9\u7684\u6D88\u606F\uFF08\u8FD8\u6709 " + earlier + " \u6761\uFF09");
|
|
8283
|
+
more.type = "button";
|
|
8284
|
+
more.setAttribute("data-history-more", "1");
|
|
8285
|
+
listen(more, "click", function(event) {
|
|
8286
|
+
event.preventDefault();
|
|
8287
|
+
event.stopPropagation();
|
|
8288
|
+
var top = env.body.scrollTop;
|
|
8289
|
+
var height = env.body.scrollHeight;
|
|
8290
|
+
turnWindowSize += 40;
|
|
8291
|
+
renderTurns({ preservePosition: true, prepend: true });
|
|
8292
|
+
env.body.scrollTop = top + Math.max(0, env.body.scrollHeight - height);
|
|
8293
|
+
env.state.follow = false;
|
|
8294
|
+
});
|
|
8295
|
+
env.chatLog.appendChild(more);
|
|
8296
|
+
}
|
|
6854
8297
|
for (var j = 0; j < visible.length; j++) {
|
|
6855
8298
|
var bubble = bubbleFor(visible[j]);
|
|
6856
8299
|
bubble.__turn = visible[j];
|
|
6857
8300
|
env.chatLog.appendChild(bubble);
|
|
6858
8301
|
}
|
|
8302
|
+
renderedTurns = visible.slice();
|
|
6859
8303
|
var fresh = visible[visible.length - 1];
|
|
6860
8304
|
if (fresh && fresh.role === "assistant" && fresh.asking === true && !fresh.text) {
|
|
6861
|
-
env.showWaiting(env.chatLog.children[
|
|
8305
|
+
env.showWaiting(env.chatLog.children[env.chatLog.children.length - 1], "\u6B63\u5728\u56DE\u7B54\u2026", {
|
|
6862
8306
|
startedAt: fresh.startedAt,
|
|
6863
8307
|
effort: fresh.effort,
|
|
6864
8308
|
effortOf: function() {
|
|
@@ -6870,7 +8314,7 @@ function createHistoryController(env) {
|
|
|
6870
8314
|
});
|
|
6871
8315
|
}
|
|
6872
8316
|
} else if (visible.length > 0) {
|
|
6873
|
-
var node = env.chatLog.children[
|
|
8317
|
+
var node = env.chatLog.children[env.chatLog.children.length - 1];
|
|
6874
8318
|
var turn = visible[visible.length - 1];
|
|
6875
8319
|
var painted = false;
|
|
6876
8320
|
node.className = "dsh-sel-bubble " + (turn.role === "user" ? "dsh-sel-bubble-user" : "dsh-sel-bubble-bot") + (turn.error ? " dsh-sel-bubble-err" : "");
|
|
@@ -6964,13 +8408,35 @@ function createHistoryController(env) {
|
|
|
6964
8408
|
}
|
|
6965
8409
|
return strip;
|
|
6966
8410
|
}
|
|
8411
|
+
function bubbleGallery(anchor) {
|
|
8412
|
+
var log = env.chatLog;
|
|
8413
|
+
var links = log && typeof log.querySelectorAll === "function" ? log.querySelectorAll(".dsh-sel-bqimgwrap") : [];
|
|
8414
|
+
var images = [];
|
|
8415
|
+
var index = 0;
|
|
8416
|
+
for (var i = 0; i < links.length; i += 1) {
|
|
8417
|
+
var wrap = links[i];
|
|
8418
|
+
var thumb = wrap.querySelector ? wrap.querySelector("img") : null;
|
|
8419
|
+
var src = String(wrap.getAttribute("href") || thumb && thumb.getAttribute("src") || "");
|
|
8420
|
+
if (!src) continue;
|
|
8421
|
+
if (wrap === anchor) index = images.length;
|
|
8422
|
+
images.push({ src, name: thumb && thumb.alt || "\u56FE\u7247" });
|
|
8423
|
+
}
|
|
8424
|
+
if (images.length === 0 && anchor) images.push({ src: anchor.href, name: "\u56FE\u7247" });
|
|
8425
|
+
return { images, index };
|
|
8426
|
+
}
|
|
6967
8427
|
function attachmentImage(ref) {
|
|
6968
8428
|
var link = document.createElement("a");
|
|
6969
8429
|
link.className = "dsh-sel-bqimgwrap";
|
|
6970
8430
|
link.href = attachmentUrl("image", ref);
|
|
6971
8431
|
link.target = "_blank";
|
|
6972
8432
|
link.rel = "noreferrer noopener";
|
|
6973
|
-
link.title = "\u70B9\u51FB\
|
|
8433
|
+
link.title = "\u70B9\u51FB\u9884\u89C8" + (ref.name ? "\uFF1A" + ref.name : "");
|
|
8434
|
+
listen(link, "click", function(event) {
|
|
8435
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
8436
|
+
if (typeof env.previewImage !== "function") return;
|
|
8437
|
+
event.preventDefault();
|
|
8438
|
+
env.previewImage(bubbleGallery(link));
|
|
8439
|
+
});
|
|
6974
8440
|
var img = document.createElement("img");
|
|
6975
8441
|
img.className = "dsh-sel-bqimg";
|
|
6976
8442
|
img.src = link.href;
|
|
@@ -7053,6 +8519,7 @@ function createHistoryController(env) {
|
|
|
7053
8519
|
return seed.length > 1500 ? seed.slice(0, 1500) + "\u2026" : seed;
|
|
7054
8520
|
}
|
|
7055
8521
|
return {
|
|
8522
|
+
clearRenderedTurns,
|
|
7056
8523
|
hideHistoryList,
|
|
7057
8524
|
placeHistoryList,
|
|
7058
8525
|
toggleHistoryList,
|
|
@@ -7085,13 +8552,6 @@ function createHistoryController(env) {
|
|
|
7085
8552
|
loadHistoryEntry,
|
|
7086
8553
|
seedHistoryFromExplanation,
|
|
7087
8554
|
ensureChatFollow,
|
|
7088
|
-
get FOLD_HISTORY_KEEP_LATEST() {
|
|
7089
|
-
return FOLD_HISTORY_KEEP_LATEST;
|
|
7090
|
-
},
|
|
7091
|
-
set FOLD_HISTORY_KEEP_LATEST(value) {
|
|
7092
|
-
FOLD_HISTORY_KEEP_LATEST = value;
|
|
7093
|
-
},
|
|
7094
|
-
historyTextOf,
|
|
7095
8555
|
clearTransientNotice,
|
|
7096
8556
|
saveHistory,
|
|
7097
8557
|
applyToolEvent,
|
|
@@ -7158,7 +8618,7 @@ function createConversationController(env) {
|
|
|
7158
8618
|
}
|
|
7159
8619
|
return false;
|
|
7160
8620
|
}
|
|
7161
|
-
function ask(question) {
|
|
8621
|
+
async function ask(question) {
|
|
7162
8622
|
var generating = env.state.asking || env.state.phase === "loading" || env.state.phase === "streaming";
|
|
7163
8623
|
if (generating) {
|
|
7164
8624
|
env.setStatus("\u8FD8\u5728\u751F\u6210\uFF0C\u8BF7\u7A0D\u5019\u2026\uFF08\u751F\u6210\u5B8C\u5C31\u80FD\u8FFD\u95EE\uFF09");
|
|
@@ -7168,6 +8628,17 @@ function createConversationController(env) {
|
|
|
7168
8628
|
env.cancelVoice({ note: "\u5DF2\u505C\u6B62\u5F55\u97F3\uFF08\u8FD9\u6761\u5148\u53D1\u51FA\u53BB\uFF09" });
|
|
7169
8629
|
}
|
|
7170
8630
|
var asked = String(question || "").trim();
|
|
8631
|
+
if (env.attachEnsureUploaded && env.state.uploadingAttachments) return;
|
|
8632
|
+
if (env.attachEnsureUploaded) {
|
|
8633
|
+
env.state.uploadingAttachments = true;
|
|
8634
|
+
env.setStatus("\u6B63\u5728\u4E0A\u4F20\u9644\u4EF6\u2026");
|
|
8635
|
+
try {
|
|
8636
|
+
await env.attachEnsureUploaded();
|
|
8637
|
+
} finally {
|
|
8638
|
+
env.state.uploadingAttachments = false;
|
|
8639
|
+
}
|
|
8640
|
+
if (env.state.asking || env.state.phase === "loading" || env.state.phase === "streaming") return;
|
|
8641
|
+
}
|
|
7171
8642
|
var quotes = env.state.quotes.slice();
|
|
7172
8643
|
var attachments = env.attachmentsPayload();
|
|
7173
8644
|
if (!asked && quotes.length === 0 && attachments.length === 0 || !env.state.payload) return;
|
|
@@ -7206,13 +8677,15 @@ function createConversationController(env) {
|
|
|
7206
8677
|
function sendAsk(asked, quotes, attachments, attachmentsNote) {
|
|
7207
8678
|
var payload = env.state.payload;
|
|
7208
8679
|
if (!payload) return;
|
|
8680
|
+
var firstShot = payload.kind === "shot" && env.state.turns.length === 0;
|
|
7209
8681
|
var withAttachments = attachments || [];
|
|
7210
8682
|
var sent = env.composeQuestion(quotes, asked);
|
|
7211
8683
|
if (attachmentsNote) sent = attachmentsNote + "\n" + sent;
|
|
7212
8684
|
if (env.state.turns.length === 0 && (env.state.raw.trim() || env.state.parts.translation || env.state.parts.detail)) {
|
|
7213
8685
|
env.state.turns.push({ role: "assistant", text: env.seedHistoryFromExplanation(), seed: true, hidden: true });
|
|
7214
8686
|
}
|
|
7215
|
-
|
|
8687
|
+
var questionTurn = { role: "user", text: asked || QUOTE_ONLY_QUESTION, quotes, sent, attachments: withAttachments };
|
|
8688
|
+
env.state.turns.push(questionTurn);
|
|
7216
8689
|
var answerTurn = {
|
|
7217
8690
|
role: "assistant",
|
|
7218
8691
|
text: "",
|
|
@@ -7234,23 +8707,22 @@ function createConversationController(env) {
|
|
|
7234
8707
|
if (env.state.sessionOpen !== void 0) {
|
|
7235
8708
|
}
|
|
7236
8709
|
var startedAt = Date.now();
|
|
7237
|
-
var timer =
|
|
8710
|
+
var timer = env.timerScope.interval(function() {
|
|
7238
8711
|
env.paintPill();
|
|
7239
8712
|
env.setStatus(
|
|
7240
8713
|
(env.state.toolBusy ? "\u6B63\u5728\u68C0\u7D22\u8D44\u6599\u2026 " : "\u8FFD\u95EE\u4E2D\u2026 ") + ((Date.now() - startedAt) / 1e3).toFixed(1) + "s"
|
|
7241
8714
|
);
|
|
7242
8715
|
updateWaitClocks();
|
|
7243
8716
|
}, 200);
|
|
7244
|
-
env.timers.push(timer);
|
|
7245
8717
|
var historyTurns = env.state.turns.slice(0, env.state.turns.length - 2);
|
|
7246
|
-
var
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
8718
|
+
var initialExplanation = env.state.skipInterpretation ? "" : env.seedHistoryFromExplanation();
|
|
8719
|
+
var source = contextSource(env.panelOrCurrentSessionId(), payload.text.trim(), payload.context, initialExplanation);
|
|
8720
|
+
if (env.state.contextState && env.state.contextState.source !== source) env.state.contextState = null;
|
|
8721
|
+
var prepared = prepareChatHistory(historyTurns, env.state.contextState);
|
|
8722
|
+
env.state.contextState = prepared.contextState;
|
|
8723
|
+
var history = prepared.history;
|
|
8724
|
+
var requestTurns = env.state.turns;
|
|
8725
|
+
var requestKey = env.state.cacheKey;
|
|
7254
8726
|
env.state.stopped = false;
|
|
7255
8727
|
var askController = new AbortController();
|
|
7256
8728
|
env.state.askRequest = {
|
|
@@ -7262,14 +8734,14 @@ function createConversationController(env) {
|
|
|
7262
8734
|
}
|
|
7263
8735
|
};
|
|
7264
8736
|
function finish(error) {
|
|
7265
|
-
|
|
8737
|
+
env.timerScope.clear(timer);
|
|
7266
8738
|
env.state.asking = false;
|
|
7267
8739
|
env.state.toolBusy = false;
|
|
7268
8740
|
env.clearTransientNotice(answerTurn);
|
|
7269
8741
|
answerTurn.asking = false;
|
|
7270
8742
|
answerTurn.streaming = false;
|
|
7271
8743
|
clearWaiting(env.chatLog);
|
|
7272
|
-
env.saveHistory();
|
|
8744
|
+
if (env.state.turns === requestTurns && env.state.cacheKey === requestKey) env.saveHistory();
|
|
7273
8745
|
env.syncPanelStage();
|
|
7274
8746
|
refreshAskState();
|
|
7275
8747
|
env.state.askRequest = null;
|
|
@@ -7286,24 +8758,42 @@ function createConversationController(env) {
|
|
|
7286
8758
|
);
|
|
7287
8759
|
env.askFocusAfterTurn();
|
|
7288
8760
|
}
|
|
8761
|
+
var requestBody = JSON.stringify({
|
|
8762
|
+
text: payload.text,
|
|
8763
|
+
context: payload.context,
|
|
8764
|
+
label: payload.label,
|
|
8765
|
+
// 截图首轮:host 靠它走独立的 SHOT_PROMPT(不是"选中文字"模板),
|
|
8766
|
+
// 并据此描述图里到底是整窗、全屏、留白还是仅选区。
|
|
8767
|
+
...firstShot ? {
|
|
8768
|
+
kind: "shot",
|
|
8769
|
+
...env.state.shotScope ? { shotScope: env.state.shotScope, shotFrame: env.state.shotFrame !== false } : {}
|
|
8770
|
+
} : {},
|
|
8771
|
+
sessionId: env.panelOrCurrentSessionId(),
|
|
8772
|
+
question: sent,
|
|
8773
|
+
history,
|
|
8774
|
+
key: requestKey,
|
|
8775
|
+
historyStart: prepared.historyStart,
|
|
8776
|
+
contextState: prepared.contextState,
|
|
8777
|
+
materials: prepared.materials,
|
|
8778
|
+
initialExplanation,
|
|
8779
|
+
// 附件只带**引用**(图片/文件都已经在 host 的附件服务里),body 不会因为一张截图胖几 MB
|
|
8780
|
+
...withAttachments.length > 0 ? { attachments: withAttachments } : {},
|
|
8781
|
+
// 网页输出要跟着当前深浅走:深色 Harness 里给一张白底页面会很刺眼
|
|
8782
|
+
...env.state.webAnswer ? { webAnswer: true, theme: env.currentSurfaceTheme() } : {},
|
|
8783
|
+
...env.state.modelChoice ? { provider: env.state.modelChoice.provider, model: env.state.modelChoice.model } : {},
|
|
8784
|
+
...env.state.effort ? { effort: env.state.effort } : {},
|
|
8785
|
+
...env.state.toolDigest ? { toolDigest: env.state.toolDigest } : {}
|
|
8786
|
+
});
|
|
8787
|
+
if (new TextEncoder().encode(requestBody).byteLength > CHAT_REQUEST_BYTES) {
|
|
8788
|
+
questionTurn.contextExcluded = true;
|
|
8789
|
+
answerTurn.contextExcluded = true;
|
|
8790
|
+
finish("\u672C\u8F6E\u6750\u6599\u8D85\u8FC7\u8BF7\u6C42\u5BB9\u91CF\uFF0C\u8BF7\u7F29\u5C0F\u63D0\u95EE\u6216\u5F15\u7528\u8303\u56F4\uFF1B\u539F\u6587\u4ECD\u4FDD\u7559\u5728\u5F53\u524D\u5386\u53F2\u4E2D");
|
|
8791
|
+
return;
|
|
8792
|
+
}
|
|
7289
8793
|
fetch(API, {
|
|
7290
8794
|
method: "POST",
|
|
7291
8795
|
headers: { "content-type": "application/json" },
|
|
7292
|
-
body:
|
|
7293
|
-
text: payload.text,
|
|
7294
|
-
context: payload.context,
|
|
7295
|
-
label: payload.label,
|
|
7296
|
-
sessionId: env.panelOrCurrentSessionId(),
|
|
7297
|
-
question: sent,
|
|
7298
|
-
history,
|
|
7299
|
-
// 附件只带**引用**(图片/文件都已经在 host 的附件服务里),body 不会因为一张截图胖几 MB
|
|
7300
|
-
...withAttachments.length > 0 ? { attachments: withAttachments } : {},
|
|
7301
|
-
// 网页输出要跟着当前深浅走:深色 Harness 里给一张白底页面会很刺眼
|
|
7302
|
-
...env.state.webAnswer ? { webAnswer: true, theme: env.currentSurfaceTheme() } : {},
|
|
7303
|
-
...env.state.modelChoice ? { provider: env.state.modelChoice.provider, model: env.state.modelChoice.model } : {},
|
|
7304
|
-
...env.state.effort ? { effort: env.state.effort } : {},
|
|
7305
|
-
...env.state.toolDigest ? { toolDigest: env.state.toolDigest } : {}
|
|
7306
|
-
}),
|
|
8796
|
+
body: requestBody,
|
|
7307
8797
|
signal: askController.signal
|
|
7308
8798
|
}).then(function(response) {
|
|
7309
8799
|
if (!response.ok) {
|
|
@@ -7329,7 +8819,18 @@ function createConversationController(env) {
|
|
|
7329
8819
|
} catch (error) {
|
|
7330
8820
|
continue;
|
|
7331
8821
|
}
|
|
7332
|
-
if (message.type === "
|
|
8822
|
+
if (message.type === "context-state") {
|
|
8823
|
+
if (!askController.signal.aborted && env.state.turns === requestTurns && env.state.cacheKey === requestKey) {
|
|
8824
|
+
env.state.contextState = readContextState(message.state);
|
|
8825
|
+
}
|
|
8826
|
+
} else if (message.type === "material-restored") {
|
|
8827
|
+
var material = message.attachment;
|
|
8828
|
+
if (!askController.signal.aborted && material && (material.kind === "image" || material.kind === "file") && material.ref && material.ref.attachmentId) {
|
|
8829
|
+
if (!questionTurn.attachments.some(function(a) {
|
|
8830
|
+
return a.kind === material.kind && a.ref.attachmentId === material.ref.attachmentId;
|
|
8831
|
+
})) questionTurn.attachments.push(material);
|
|
8832
|
+
}
|
|
8833
|
+
} else if (message.type === "delta") {
|
|
7333
8834
|
env.clearTransientNotice(answerTurn);
|
|
7334
8835
|
if (!answerTurn.text.trim()) answerTurn.asking = false;
|
|
7335
8836
|
answerTurn.text += message.text;
|
|
@@ -7368,6 +8869,10 @@ function createConversationController(env) {
|
|
|
7368
8869
|
}
|
|
7369
8870
|
scheduleTurnsRender();
|
|
7370
8871
|
} else if (message.type === "error") {
|
|
8872
|
+
if (message.code === "context-rejected") {
|
|
8873
|
+
questionTurn.contextExcluded = true;
|
|
8874
|
+
answerTurn.contextExcluded = true;
|
|
8875
|
+
}
|
|
7371
8876
|
finish(message.message || "\u6A21\u578B\u8FD4\u56DE\u9519\u8BEF");
|
|
7372
8877
|
}
|
|
7373
8878
|
}
|
|
@@ -7499,6 +9004,8 @@ function createConversationController(env) {
|
|
|
7499
9004
|
}
|
|
7500
9005
|
}
|
|
7501
9006
|
function runRequest(payload, cacheKey, options) {
|
|
9007
|
+
var requestTurns = env.state.turns;
|
|
9008
|
+
var requestKey = cacheKey;
|
|
7502
9009
|
env.state.cacheState = "miss";
|
|
7503
9010
|
if (env.state.request) env.state.request.abort();
|
|
7504
9011
|
var controller = new AbortController();
|
|
@@ -7525,16 +9032,15 @@ function createConversationController(env) {
|
|
|
7525
9032
|
env.state.startedAt = Date.now();
|
|
7526
9033
|
env.state.elapsed = 0;
|
|
7527
9034
|
env.paint();
|
|
7528
|
-
var ticker =
|
|
9035
|
+
var ticker = env.timerScope.interval(function() {
|
|
7529
9036
|
env.paintPill();
|
|
7530
9037
|
if (env.state.phase === "loading" || env.state.phase === "streaming") {
|
|
7531
9038
|
env.setStatus(env.progressText());
|
|
7532
9039
|
updateWaitClocks();
|
|
7533
9040
|
}
|
|
7534
9041
|
}, 200);
|
|
7535
|
-
env.timers.push(ticker);
|
|
7536
9042
|
function stopTicker() {
|
|
7537
|
-
|
|
9043
|
+
env.timerScope.clear(ticker);
|
|
7538
9044
|
}
|
|
7539
9045
|
function fail(message) {
|
|
7540
9046
|
if (env.state.phase === "done") return;
|
|
@@ -7568,7 +9074,7 @@ function createConversationController(env) {
|
|
|
7568
9074
|
env.state.request = null;
|
|
7569
9075
|
clearWaiting();
|
|
7570
9076
|
if (cacheKey) env.cache.set(cacheKey, { parts: { ...env.state.parts }, turns: env.state.turns.slice() });
|
|
7571
|
-
env.saveHistory();
|
|
9077
|
+
if (env.state.turns === requestTurns && env.state.cacheKey === requestKey) env.saveHistory();
|
|
7572
9078
|
env.paint();
|
|
7573
9079
|
}
|
|
7574
9080
|
var dirty = false;
|
|
@@ -9123,6 +10629,101 @@ function createVoiceController(env) {
|
|
|
9123
10629
|
};
|
|
9124
10630
|
}
|
|
9125
10631
|
|
|
10632
|
+
// src/client/memory.js
|
|
10633
|
+
function createResultCache(maxEntries = 20, maxBytes = 8 * 1024 * 1024) {
|
|
10634
|
+
const entries = /* @__PURE__ */ new Map();
|
|
10635
|
+
let textBytes = 0;
|
|
10636
|
+
let disposed = false;
|
|
10637
|
+
function remove(key) {
|
|
10638
|
+
const entry = entries.get(key);
|
|
10639
|
+
if (!entry) return;
|
|
10640
|
+
textBytes -= entry.bytes;
|
|
10641
|
+
entries.delete(key);
|
|
10642
|
+
}
|
|
10643
|
+
const cache = {
|
|
10644
|
+
get size() {
|
|
10645
|
+
return entries.size;
|
|
10646
|
+
},
|
|
10647
|
+
// Conservative UTF-16 size of serialized text, not a measurement of JS heap size.
|
|
10648
|
+
get textBytes() {
|
|
10649
|
+
return textBytes;
|
|
10650
|
+
},
|
|
10651
|
+
keys() {
|
|
10652
|
+
return entries.keys();
|
|
10653
|
+
},
|
|
10654
|
+
get(key) {
|
|
10655
|
+
const entry = entries.get(key);
|
|
10656
|
+
if (!entry) return void 0;
|
|
10657
|
+
entries.delete(key);
|
|
10658
|
+
entries.set(key, entry);
|
|
10659
|
+
return JSON.parse(entry.json);
|
|
10660
|
+
},
|
|
10661
|
+
set(key, value) {
|
|
10662
|
+
if (disposed) return cache;
|
|
10663
|
+
remove(key);
|
|
10664
|
+
let json;
|
|
10665
|
+
try {
|
|
10666
|
+
json = JSON.stringify(value);
|
|
10667
|
+
} catch {
|
|
10668
|
+
return cache;
|
|
10669
|
+
}
|
|
10670
|
+
if (typeof json !== "string") return cache;
|
|
10671
|
+
const bytes = (String(key).length + json.length) * 2;
|
|
10672
|
+
if (bytes > maxBytes || maxEntries <= 0) return cache;
|
|
10673
|
+
entries.set(key, { json, bytes });
|
|
10674
|
+
textBytes += bytes;
|
|
10675
|
+
while (entries.size > maxEntries || textBytes > maxBytes) remove(entries.keys().next().value);
|
|
10676
|
+
return cache;
|
|
10677
|
+
},
|
|
10678
|
+
clear() {
|
|
10679
|
+
entries.clear();
|
|
10680
|
+
textBytes = 0;
|
|
10681
|
+
},
|
|
10682
|
+
dispose() {
|
|
10683
|
+
disposed = true;
|
|
10684
|
+
cache.clear();
|
|
10685
|
+
}
|
|
10686
|
+
};
|
|
10687
|
+
return cache;
|
|
10688
|
+
}
|
|
10689
|
+
function createTimerScope() {
|
|
10690
|
+
const active = /* @__PURE__ */ new Set();
|
|
10691
|
+
let disposed = false;
|
|
10692
|
+
return {
|
|
10693
|
+
get size() {
|
|
10694
|
+
return active.size;
|
|
10695
|
+
},
|
|
10696
|
+
later(fn, ms) {
|
|
10697
|
+
if (disposed) return null;
|
|
10698
|
+
const id = setTimeout(() => {
|
|
10699
|
+
active.delete(id);
|
|
10700
|
+
if (!disposed) fn();
|
|
10701
|
+
}, ms);
|
|
10702
|
+
active.add(id);
|
|
10703
|
+
return id;
|
|
10704
|
+
},
|
|
10705
|
+
interval(fn, ms) {
|
|
10706
|
+
if (disposed) return null;
|
|
10707
|
+
const id = setInterval(fn, ms);
|
|
10708
|
+
active.add(id);
|
|
10709
|
+
return id;
|
|
10710
|
+
},
|
|
10711
|
+
clear(id) {
|
|
10712
|
+
clearTimeout(id);
|
|
10713
|
+
clearInterval(id);
|
|
10714
|
+
active.delete(id);
|
|
10715
|
+
},
|
|
10716
|
+
dispose() {
|
|
10717
|
+
disposed = true;
|
|
10718
|
+
active.forEach((id) => {
|
|
10719
|
+
clearTimeout(id);
|
|
10720
|
+
clearInterval(id);
|
|
10721
|
+
});
|
|
10722
|
+
active.clear();
|
|
10723
|
+
}
|
|
10724
|
+
};
|
|
10725
|
+
}
|
|
10726
|
+
|
|
9126
10727
|
// src/client/index.js
|
|
9127
10728
|
function apply(ctx) {
|
|
9128
10729
|
var state = {
|
|
@@ -9235,7 +10836,8 @@ function apply(ctx) {
|
|
|
9235
10836
|
/** 升格请求进行中。 */
|
|
9236
10837
|
promoting: false
|
|
9237
10838
|
};
|
|
9238
|
-
var cache =
|
|
10839
|
+
var cache = createResultCache();
|
|
10840
|
+
var timerScope = createTimerScope();
|
|
9239
10841
|
var styleEl = el("style");
|
|
9240
10842
|
styleEl.setAttribute("data-plugin", "@yfwu2020/dsh-selection-explain");
|
|
9241
10843
|
styleEl.textContent = CSS;
|
|
@@ -9634,6 +11236,18 @@ function apply(ctx) {
|
|
|
9634
11236
|
}
|
|
9635
11237
|
});
|
|
9636
11238
|
var voiceCardController = createVoiceCardController({
|
|
11239
|
+
get shotRefAsync() {
|
|
11240
|
+
return shotController.takeRefAsync;
|
|
11241
|
+
},
|
|
11242
|
+
get shotFailed() {
|
|
11243
|
+
return shotController.failed;
|
|
11244
|
+
},
|
|
11245
|
+
get shotNotice() {
|
|
11246
|
+
return shotController.notice;
|
|
11247
|
+
},
|
|
11248
|
+
get clearShotMarkers() {
|
|
11249
|
+
return shotController.clearMarkers;
|
|
11250
|
+
},
|
|
9637
11251
|
get layer() {
|
|
9638
11252
|
return layer;
|
|
9639
11253
|
},
|
|
@@ -9670,8 +11284,8 @@ function apply(ctx) {
|
|
|
9670
11284
|
get frameTouchedAt() {
|
|
9671
11285
|
return previewController.frameTouchedAt;
|
|
9672
11286
|
},
|
|
9673
|
-
get
|
|
9674
|
-
return
|
|
11287
|
+
get timerScope() {
|
|
11288
|
+
return timerScope;
|
|
9675
11289
|
},
|
|
9676
11290
|
get voice() {
|
|
9677
11291
|
return voiceController.voice;
|
|
@@ -9696,6 +11310,9 @@ function apply(ctx) {
|
|
|
9696
11310
|
}
|
|
9697
11311
|
});
|
|
9698
11312
|
var quotesController = createQuotesController({
|
|
11313
|
+
get foldForHistory() {
|
|
11314
|
+
return historyController.foldForHistory;
|
|
11315
|
+
},
|
|
9699
11316
|
get state() {
|
|
9700
11317
|
return state;
|
|
9701
11318
|
},
|
|
@@ -9729,8 +11346,8 @@ function apply(ctx) {
|
|
|
9729
11346
|
get currentSessionId() {
|
|
9730
11347
|
return panelController.currentSessionId;
|
|
9731
11348
|
},
|
|
9732
|
-
get
|
|
9733
|
-
return
|
|
11349
|
+
get timerScope() {
|
|
11350
|
+
return timerScope;
|
|
9734
11351
|
}
|
|
9735
11352
|
});
|
|
9736
11353
|
var previewController = createPreviewController({
|
|
@@ -9750,7 +11367,15 @@ function apply(ctx) {
|
|
|
9750
11367
|
return later;
|
|
9751
11368
|
}
|
|
9752
11369
|
});
|
|
11370
|
+
var lightboxController = createLightboxController({
|
|
11371
|
+
get layer() {
|
|
11372
|
+
return layer;
|
|
11373
|
+
}
|
|
11374
|
+
});
|
|
9753
11375
|
var panelController = createPanelController({
|
|
11376
|
+
get timerScope() {
|
|
11377
|
+
return timerScope;
|
|
11378
|
+
},
|
|
9754
11379
|
get panelSessionId() {
|
|
9755
11380
|
return panelSessionId;
|
|
9756
11381
|
},
|
|
@@ -9787,6 +11412,12 @@ function apply(ctx) {
|
|
|
9787
11412
|
get renderTurns() {
|
|
9788
11413
|
return historyController.renderTurns;
|
|
9789
11414
|
},
|
|
11415
|
+
get clearRenderedTurns() {
|
|
11416
|
+
return historyController.clearRenderedTurns;
|
|
11417
|
+
},
|
|
11418
|
+
get clearWaiting() {
|
|
11419
|
+
return conversationController.clearWaiting;
|
|
11420
|
+
},
|
|
9790
11421
|
get sendAsk() {
|
|
9791
11422
|
return conversationController.sendAsk;
|
|
9792
11423
|
},
|
|
@@ -9865,6 +11496,9 @@ function apply(ctx) {
|
|
|
9865
11496
|
get closeSettings() {
|
|
9866
11497
|
return settingsController.closeSettings;
|
|
9867
11498
|
},
|
|
11499
|
+
get closeLightbox() {
|
|
11500
|
+
return lightboxController.close;
|
|
11501
|
+
},
|
|
9868
11502
|
get hideHistoryList() {
|
|
9869
11503
|
return historyController.hideHistoryList;
|
|
9870
11504
|
},
|
|
@@ -10046,12 +11680,21 @@ function apply(ctx) {
|
|
|
10046
11680
|
}
|
|
10047
11681
|
});
|
|
10048
11682
|
var historyController = createHistoryController({
|
|
11683
|
+
set panelSessionId(value) {
|
|
11684
|
+
panelSessionId = value;
|
|
11685
|
+
},
|
|
11686
|
+
get applyShotTitle() {
|
|
11687
|
+
return panelController.applyShotTitle;
|
|
11688
|
+
},
|
|
10049
11689
|
get sentTextOf() {
|
|
10050
11690
|
return quotesController.sentTextOf;
|
|
10051
11691
|
},
|
|
10052
11692
|
get panel() {
|
|
10053
11693
|
return panel;
|
|
10054
11694
|
},
|
|
11695
|
+
get previewImage() {
|
|
11696
|
+
return lightboxController.open;
|
|
11697
|
+
},
|
|
10055
11698
|
get state() {
|
|
10056
11699
|
return state;
|
|
10057
11700
|
},
|
|
@@ -10076,8 +11719,11 @@ function apply(ctx) {
|
|
|
10076
11719
|
get paint() {
|
|
10077
11720
|
return panelController.paint;
|
|
10078
11721
|
},
|
|
10079
|
-
get
|
|
10080
|
-
return
|
|
11722
|
+
get paintPill() {
|
|
11723
|
+
return pillController.paintPill;
|
|
11724
|
+
},
|
|
11725
|
+
get timerScope() {
|
|
11726
|
+
return timerScope;
|
|
10081
11727
|
},
|
|
10082
11728
|
get panelOpen() {
|
|
10083
11729
|
return panelOpen;
|
|
@@ -10177,6 +11823,9 @@ function apply(ctx) {
|
|
|
10177
11823
|
get composeQuestion() {
|
|
10178
11824
|
return quotesController.composeQuestion;
|
|
10179
11825
|
},
|
|
11826
|
+
get attachEnsureUploaded() {
|
|
11827
|
+
return attachController.ensureUploaded;
|
|
11828
|
+
},
|
|
10180
11829
|
get attachmentsPayload() {
|
|
10181
11830
|
return attachController.payload;
|
|
10182
11831
|
},
|
|
@@ -10207,14 +11856,8 @@ function apply(ctx) {
|
|
|
10207
11856
|
get paintPill() {
|
|
10208
11857
|
return pillController.paintPill;
|
|
10209
11858
|
},
|
|
10210
|
-
get
|
|
10211
|
-
return
|
|
10212
|
-
},
|
|
10213
|
-
get FOLD_HISTORY_KEEP_LATEST() {
|
|
10214
|
-
return historyController.FOLD_HISTORY_KEEP_LATEST;
|
|
10215
|
-
},
|
|
10216
|
-
get historyTextOf() {
|
|
10217
|
-
return historyController.historyTextOf;
|
|
11859
|
+
get timerScope() {
|
|
11860
|
+
return timerScope;
|
|
10218
11861
|
},
|
|
10219
11862
|
get clearTransientNotice() {
|
|
10220
11863
|
return historyController.clearTransientNotice;
|
|
@@ -10317,6 +11960,9 @@ function apply(ctx) {
|
|
|
10317
11960
|
get attachInput() {
|
|
10318
11961
|
return attachInput;
|
|
10319
11962
|
},
|
|
11963
|
+
get previewImage() {
|
|
11964
|
+
return lightboxController.open;
|
|
11965
|
+
},
|
|
10320
11966
|
get setStatus() {
|
|
10321
11967
|
return panelController.setStatus;
|
|
10322
11968
|
},
|
|
@@ -10324,6 +11970,48 @@ function apply(ctx) {
|
|
|
10324
11970
|
return conversationController.refreshAskState;
|
|
10325
11971
|
}
|
|
10326
11972
|
});
|
|
11973
|
+
var shotController = createShotController({
|
|
11974
|
+
get document() {
|
|
11975
|
+
return document;
|
|
11976
|
+
},
|
|
11977
|
+
get window() {
|
|
11978
|
+
return typeof window !== "undefined" ? window : null;
|
|
11979
|
+
},
|
|
11980
|
+
get layer() {
|
|
11981
|
+
return layer;
|
|
11982
|
+
},
|
|
11983
|
+
get state() {
|
|
11984
|
+
return state;
|
|
11985
|
+
},
|
|
11986
|
+
get panelOpen() {
|
|
11987
|
+
return panelOpen;
|
|
11988
|
+
},
|
|
11989
|
+
get addFiles() {
|
|
11990
|
+
return attachController.addFiles;
|
|
11991
|
+
},
|
|
11992
|
+
get focusAskBox() {
|
|
11993
|
+
return panelController.focusAskBox;
|
|
11994
|
+
},
|
|
11995
|
+
get openVCard() {
|
|
11996
|
+
return voiceCardController.openVCard;
|
|
11997
|
+
},
|
|
11998
|
+
get vcardOpen() {
|
|
11999
|
+
return function() {
|
|
12000
|
+
return voiceCardController.vcardState.open;
|
|
12001
|
+
};
|
|
12002
|
+
},
|
|
12003
|
+
get vcardElement() {
|
|
12004
|
+
return function() {
|
|
12005
|
+
return voiceCardController.vcard;
|
|
12006
|
+
};
|
|
12007
|
+
},
|
|
12008
|
+
get closeVCard() {
|
|
12009
|
+
return voiceCardController.closeVCard;
|
|
12010
|
+
},
|
|
12011
|
+
get ensureLayer() {
|
|
12012
|
+
return ensureLayer;
|
|
12013
|
+
}
|
|
12014
|
+
});
|
|
10327
12015
|
var modelsController = createModelsController({
|
|
10328
12016
|
get state() {
|
|
10329
12017
|
return state;
|
|
@@ -10444,6 +12132,10 @@ function apply(ctx) {
|
|
|
10444
12132
|
else layer.removeAttribute("data-full-view");
|
|
10445
12133
|
}
|
|
10446
12134
|
}
|
|
12135
|
+
function ensureLayer() {
|
|
12136
|
+
if (typeof document === "undefined" || !document.body) return;
|
|
12137
|
+
if (layer.parentNode !== document.body) document.body.appendChild(layer);
|
|
12138
|
+
}
|
|
10447
12139
|
function mountFloatingLayer(node) {
|
|
10448
12140
|
if (node) {
|
|
10449
12141
|
if (layer.parentNode !== document.body) document.body.appendChild(layer);
|
|
@@ -10488,7 +12180,6 @@ function apply(ctx) {
|
|
|
10488
12180
|
var STREAM_RENDER_MS = 90;
|
|
10489
12181
|
var turnsRenderPending = false;
|
|
10490
12182
|
var lastTurnsRenderAt = 0;
|
|
10491
|
-
var timers = [];
|
|
10492
12183
|
var PILL_POLL_MIN = 2e3;
|
|
10493
12184
|
var PILL_POLL_MAX = 6e4;
|
|
10494
12185
|
var pillPollDelay = PILL_POLL_MIN;
|
|
@@ -10508,9 +12199,7 @@ function apply(ctx) {
|
|
|
10508
12199
|
var pillObserver = null;
|
|
10509
12200
|
var waitList = [];
|
|
10510
12201
|
function later(fn, ms) {
|
|
10511
|
-
|
|
10512
|
-
timers.push(id);
|
|
10513
|
-
return id;
|
|
12202
|
+
return timerScope.later(fn, ms);
|
|
10514
12203
|
}
|
|
10515
12204
|
var offBridgeMessage = listen(window, "message", function(event) {
|
|
10516
12205
|
var data = event && event.data;
|
|
@@ -10942,6 +12631,15 @@ function apply(ctx) {
|
|
|
10942
12631
|
panelController.savePanelSize();
|
|
10943
12632
|
});
|
|
10944
12633
|
window.__dshSelectionExplain = {
|
|
12634
|
+
/** 截图提问的状态(调试/自动化)。 */
|
|
12635
|
+
shot: function() {
|
|
12636
|
+
return shotController.state();
|
|
12637
|
+
},
|
|
12638
|
+
/** 手动起一次截图(等同按 ⌘⇧X)。 */
|
|
12639
|
+
shotStart: function() {
|
|
12640
|
+
shotController.capture();
|
|
12641
|
+
return true;
|
|
12642
|
+
},
|
|
10945
12643
|
/**
|
|
10946
12644
|
* 语音卡(选中文字 + 输入法正在语音输入 → 就地追问)的状态。
|
|
10947
12645
|
* 给自动化验证用:能不能弹、有没有焦点、几何对不对,全在这里读。
|
|
@@ -11080,6 +12778,14 @@ function apply(ctx) {
|
|
|
11080
12778
|
turns: function() {
|
|
11081
12779
|
return state.turns.slice();
|
|
11082
12780
|
},
|
|
12781
|
+
memory: function() {
|
|
12782
|
+
return {
|
|
12783
|
+
cacheEntries: cache.size,
|
|
12784
|
+
cacheTextBytes: cache.textBytes,
|
|
12785
|
+
timers: timerScope.size,
|
|
12786
|
+
previewFrames: Object.keys(previewFrames).length
|
|
12787
|
+
};
|
|
12788
|
+
},
|
|
11083
12789
|
/** 自检用:当前待发送的引用(引用区里那几张卡片)。 */
|
|
11084
12790
|
quotes: function() {
|
|
11085
12791
|
return state.quotes.map(function(quote2) {
|
|
@@ -11424,6 +13130,7 @@ function apply(ctx) {
|
|
|
11424
13130
|
state.selection = null;
|
|
11425
13131
|
state.bridgeActive = false;
|
|
11426
13132
|
state.parts = { translation: "", detail: "" };
|
|
13133
|
+
state.contextState = null;
|
|
11427
13134
|
state.turns = [];
|
|
11428
13135
|
quotesController.clearQuotes();
|
|
11429
13136
|
state.raw = "";
|
|
@@ -11446,11 +13153,110 @@ function apply(ctx) {
|
|
|
11446
13153
|
});
|
|
11447
13154
|
}
|
|
11448
13155
|
};
|
|
13156
|
+
ctx.effect(function() {
|
|
13157
|
+
if (typeof window === "undefined" || !window.addEventListener) return function() {
|
|
13158
|
+
};
|
|
13159
|
+
function send(payload) {
|
|
13160
|
+
try {
|
|
13161
|
+
fetch("/selection-explain/api/shot/hello", {
|
|
13162
|
+
method: "POST",
|
|
13163
|
+
body: JSON.stringify(Object.assign({ at: Date.now() }, payload))
|
|
13164
|
+
}).catch(function() {
|
|
13165
|
+
});
|
|
13166
|
+
} catch (error) {
|
|
13167
|
+
}
|
|
13168
|
+
}
|
|
13169
|
+
function onError(event) {
|
|
13170
|
+
var err = event && (event.error || event);
|
|
13171
|
+
send({
|
|
13172
|
+
stage: "window-error",
|
|
13173
|
+
message: String(err && err.message || event.message || ""),
|
|
13174
|
+
stack: String(err && err.stack || "").slice(0, 2e3),
|
|
13175
|
+
source: String(event && event.filename || ""),
|
|
13176
|
+
line: event && event.lineno
|
|
13177
|
+
});
|
|
13178
|
+
}
|
|
13179
|
+
function onRejection(event) {
|
|
13180
|
+
var reason = event && event.reason;
|
|
13181
|
+
send({
|
|
13182
|
+
stage: "unhandled-rejection",
|
|
13183
|
+
message: String(reason && reason.message || reason || ""),
|
|
13184
|
+
stack: String(reason && reason.stack || "").slice(0, 2e3)
|
|
13185
|
+
});
|
|
13186
|
+
}
|
|
13187
|
+
window.addEventListener("error", onError);
|
|
13188
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
13189
|
+
return function() {
|
|
13190
|
+
window.removeEventListener("error", onError);
|
|
13191
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
13192
|
+
};
|
|
13193
|
+
}, "selection-explain: error reporter");
|
|
13194
|
+
var shortcutsService = ctx.shortcuts;
|
|
13195
|
+
var shortcutReport = { loaded: true, shortcuts: !!shortcutsService, registered: false, error: "" };
|
|
13196
|
+
function reportShortcut() {
|
|
13197
|
+
try {
|
|
13198
|
+
fetch("/selection-explain/api/shot/hello", { method: "POST", body: JSON.stringify(shortcutReport) }).catch(function() {
|
|
13199
|
+
});
|
|
13200
|
+
} catch (error) {
|
|
13201
|
+
}
|
|
13202
|
+
}
|
|
13203
|
+
reportShortcut();
|
|
13204
|
+
if (shortcutsService && typeof shortcutsService.register === "function") {
|
|
13205
|
+
ctx.effect(function() {
|
|
13206
|
+
try {
|
|
13207
|
+
var off = shortcutsService.register({
|
|
13208
|
+
id: "selection-explain.screenshot",
|
|
13209
|
+
label: function() {
|
|
13210
|
+
return "\u622A\u56FE\u63D0\u95EE";
|
|
13211
|
+
},
|
|
13212
|
+
// ⚠️ 必须给数组:设置页渲染列表时会 `[...row.aliases]`,
|
|
13213
|
+
// 留成 undefined 会直接抛 TypeError,整个快捷键弹窗打不开(实测踩到)。
|
|
13214
|
+
aliases: [],
|
|
13215
|
+
// 键位是**物理键 + 修饰键**的结构(不是 "Mod+Shift+X" 这种字符串):
|
|
13216
|
+
// code 必须是 KeyA-Z / Digit0-9 / F1-24 这类物理 code,modifiers 是 meta/control/alt/shift/primary
|
|
13217
|
+
// (primary 在 macOS 上是 ⌘、Windows 上是 Ctrl)。
|
|
13218
|
+
defaults: {
|
|
13219
|
+
"desktop:macos": { code: "KeyX", modifiers: ["meta", "shift"] },
|
|
13220
|
+
"web:macos": { code: "KeyX", modifiers: ["meta", "shift"] },
|
|
13221
|
+
"desktop:windows": { code: "KeyX", modifiers: ["primary", "shift"] },
|
|
13222
|
+
"web:windows": { code: "KeyX", modifiers: ["primary", "shift"] }
|
|
13223
|
+
},
|
|
13224
|
+
resolve: function() {
|
|
13225
|
+
return {
|
|
13226
|
+
status: "handled",
|
|
13227
|
+
run: function() {
|
|
13228
|
+
shotController.capture();
|
|
13229
|
+
}
|
|
13230
|
+
};
|
|
13231
|
+
}
|
|
13232
|
+
});
|
|
13233
|
+
shortcutReport.registered = true;
|
|
13234
|
+
reportShortcut();
|
|
13235
|
+
return off;
|
|
13236
|
+
} catch (error) {
|
|
13237
|
+
shortcutReport.error = String(error && error.message || error);
|
|
13238
|
+
reportShortcut();
|
|
13239
|
+
return function() {
|
|
13240
|
+
};
|
|
13241
|
+
}
|
|
13242
|
+
}, "selection-explain: screenshot shortcut");
|
|
13243
|
+
shotController.start();
|
|
13244
|
+
}
|
|
11449
13245
|
ctx.effect(function() {
|
|
11450
13246
|
if (typeof console !== "undefined" && console.log) {
|
|
11451
13247
|
console.log("[dsh-selection-explain] \u5212\u8BCD\u89E3\u8BFB\u5DF2\u5C31\u7EEA\uFF08\u9009\u4E2D\u6587\u5B57\u8BD5\u8BD5\uFF09");
|
|
11452
13248
|
}
|
|
11453
13249
|
return function() {
|
|
13250
|
+
panelOpen = false;
|
|
13251
|
+
timerScope.dispose();
|
|
13252
|
+
cache.dispose();
|
|
13253
|
+
voiceCardController.closeVCard(false);
|
|
13254
|
+
lightboxController.dispose();
|
|
13255
|
+
attachController.clear();
|
|
13256
|
+
shotController.stop();
|
|
13257
|
+
shotController.clearMarkers();
|
|
13258
|
+
panelController.releaseRenderedContent();
|
|
13259
|
+
if (state.askRequest) state.askRequest.abort();
|
|
11454
13260
|
offPopEnd();
|
|
11455
13261
|
offQuotePopEnd();
|
|
11456
13262
|
offMouseUp();
|
|
@@ -11538,13 +13344,17 @@ function apply(ctx) {
|
|
|
11538
13344
|
}
|
|
11539
13345
|
});
|
|
11540
13346
|
previewController.bridged.clear();
|
|
13347
|
+
previewController.frameTouchedAt.clear();
|
|
11541
13348
|
state.turns = [];
|
|
11542
13349
|
state.quotes = [];
|
|
13350
|
+
state.selection = null;
|
|
13351
|
+
state.quoteSelection = null;
|
|
13352
|
+
state.payload = null;
|
|
13353
|
+
state.parts = { translation: "", detail: "" };
|
|
13354
|
+
state.raw = "";
|
|
11543
13355
|
if (state.request) state.request.abort();
|
|
11544
|
-
for (var i = 0; i < timers.length; i++) clearTimeout(timers[i]);
|
|
11545
13356
|
if (pillIdleId) clearTimeout(pillIdleId);
|
|
11546
13357
|
if (pillController.pillStarTimer) clearTimeout(pillController.pillStarTimer);
|
|
11547
|
-
cache.clear();
|
|
11548
13358
|
if (layer.parentNode) layer.parentNode.removeChild(layer);
|
|
11549
13359
|
if (button.parentNode) button.parentNode.removeChild(button);
|
|
11550
13360
|
if (quoteButton.parentNode) quoteButton.parentNode.removeChild(quoteButton);
|
|
@@ -11559,5 +13369,5 @@ function apply(ctx) {
|
|
|
11559
13369
|
};
|
|
11560
13370
|
}, "@yfwu2020/dsh-selection-explain: \u5212\u8BCD\u89E3\u8BFB UI");
|
|
11561
13371
|
}
|
|
11562
|
-
var inject = ["slots"];
|
|
13372
|
+
var inject = ["slots", "shortcuts"];
|
|
11563
13373
|
return module.exports;}})
|