@yfwu2020/dsh-selection-explain 0.10.6 → 0.11.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 +13 -3
- package/lib/client.js +692 -14
- package/lib/client.js.map +3 -3
- package/lib/host/analyze.js +130 -15
- package/lib/host/analyze.js.map +1 -1
- package/lib/host/attach.js +254 -0
- package/lib/host/attach.js.map +1 -0
- package/lib/host/history-attachments.js +184 -0
- package/lib/host/history-attachments.js.map +1 -0
- package/lib/host/history-store.js +25 -13
- package/lib/host/history-store.js.map +1 -1
- package/lib/host/http.js +7 -0
- package/lib/host/http.js.map +1 -1
- package/lib/host/plugin.js +5 -0
- package/lib/host/plugin.js.map +1 -1
- package/lib/host/promote.js +65 -13
- package/lib/host/promote.js.map +1 -1
- package/lib/host/tools.js +28 -0
- package/lib/host/tools.js.map +1 -1
- package/lib/types/host/attach.d.ts +77 -0
- package/lib/types/host/history-attachments.d.ts +108 -0
- package/lib/types/host/http.d.ts +7 -0
- package/lib/types/host/tools.d.ts +21 -0
- package/lib/types/host/types.d.ts +5 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -117,6 +117,10 @@ var PILL_POS = "/selection-explain/api/pill";
|
|
|
117
117
|
var PANEL_SIZE = "/selection-explain/api/panel";
|
|
118
118
|
var HISTORY = "/selection-explain/api/history";
|
|
119
119
|
var MODELS = "/selection-explain/api/models";
|
|
120
|
+
var ATTACH = "/selection-explain/api/attach";
|
|
121
|
+
var ATTACHMENT = "/selection-explain/api/attachment";
|
|
122
|
+
var ATTACH_MAX_ITEMS = 10;
|
|
123
|
+
var ATTACH_MAX_ITEM_BYTES = 20 * 1024 * 1024;
|
|
120
124
|
var SETTINGS = "/selection-explain/api/settings";
|
|
121
125
|
var ROUTE = "/selection-explain/api/route";
|
|
122
126
|
var SPEECH_API = "/selection-explain/api/speech";
|
|
@@ -441,12 +445,97 @@ var CSS = [
|
|
|
441
445
|
"background:transparent;color:var(--dsw-alias-label-secondary,inherit);opacity:.5;font:inherit;font-size:11px;",
|
|
442
446
|
"line-height:1;display:inline-flex;align-items:center;justify-content:center}",
|
|
443
447
|
".dsh-sel-quotechip-x:hover{opacity:1;background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.16))}",
|
|
448
|
+
// ── 附件(composer 第一行,和引用同栏)──
|
|
449
|
+
// 引用是竖着排的卡片(有出处 + 一段文字),附件是横着一排的缩略图 —— 两种形状,
|
|
450
|
+
// 所以是**两条独立的条**、共用 ask 的第一行区域,而不是把附件塞进引用卡片里。
|
|
451
|
+
// 横向溢出就滚动(附件最多 10 件,小窗宽度装不下是常态),滚动条藏起来不占高度。
|
|
452
|
+
".dsh-sel-attach{display:none;flex-direction:row;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;",
|
|
453
|
+
"scrollbar-width:none;padding:1px 0 0}",
|
|
454
|
+
'.dsh-sel-attach[data-show="1"]{display:flex}',
|
|
455
|
+
".dsh-sel-attach::-webkit-scrollbar{width:0;height:0;display:none}",
|
|
456
|
+
// 40px 见方:比主会话的 64px 小一圈(小窗的 composer 整体是收着的:按钮 26、输入 13px)
|
|
457
|
+
".dsh-sel-thumb{position:relative;flex:0 0 auto;width:40px;height:40px;border-radius:9px;overflow:hidden;",
|
|
458
|
+
"border:1px solid var(--dsw-alias-border-l3,rgba(140,140,140,.35));background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.14));",
|
|
459
|
+
"display:grid;place-items:center;color:var(--dsw-alias-label-secondary,inherit)}",
|
|
460
|
+
".dsh-sel-thumb img{width:100%;height:100%;object-fit:cover;display:block}",
|
|
461
|
+
".dsh-sel-thumb svg{width:15px;height:15px;opacity:.55}",
|
|
462
|
+
'.dsh-sel-thumb[data-state="error"]{border-color:color-mix(in srgb,var(--sel-bad,#e5534b) 70%,transparent)}',
|
|
463
|
+
'.dsh-sel-thumb[data-state="error"] svg{opacity:.9;color:var(--sel-bad,#e5534b)}',
|
|
464
|
+
// 转圈 + 不定态进度条:fetch 拿不到上传进度,所以**不假装知道百分比**
|
|
465
|
+
// (主会话在"首次报告字节之前"用的也是不定态)
|
|
466
|
+
".dsh-sel-attachspin{position:absolute;inset:0;display:grid;place-items:center;background:rgba(0,0,0,.42)}",
|
|
467
|
+
".dsh-sel-attachspin svg{width:16px;height:16px;opacity:1;color:var(--sel-a1,#0d9488);animation:dsh-sel-spin .9s linear infinite}",
|
|
468
|
+
".dsh-sel-attachbar{position:absolute;left:0;right:0;bottom:0;height:3px;background:rgba(140,140,140,.35);overflow:hidden}",
|
|
469
|
+
".dsh-sel-attachbar i{display:block;height:100%;width:38%;background:var(--sel-a1,#0d9488);animation:dsh-sel-indeterminate 1.1s ease-in-out infinite}",
|
|
470
|
+
"@keyframes dsh-sel-indeterminate{0%{transform:translateX(-100%)}100%{transform:translateX(280%)}}",
|
|
471
|
+
// 移除:缩略图/文件卡右上角。**常驻可见**(触摸设备上没有 hover,藏起来就删不掉了)
|
|
472
|
+
// 兜底色必须是**中性灰 + 白字**:主题变量缺席时(非 DSH 宿主 / 测试页)
|
|
473
|
+
// 原来的 `bg-layer-2 → #3a3f46` 配 `label-primary → CanvasText`(浅色方案下是黑)
|
|
474
|
+
// 会画成"深底黑叉",实拍就是一坨看不出是什么的黑点。
|
|
475
|
+
".dsh-sel-attachx{position:absolute;top:-5px;right:-5px;width:15px;height:15px;padding:0;border-radius:50%;",
|
|
476
|
+
"border:1px solid var(--dsw-alias-bg-layer-1,Canvas);background:var(--dsw-alias-bg-layer-2,rgba(120,120,120,.92));",
|
|
477
|
+
"color:var(--dsw-alias-label-primary,#fff);font:inherit;font-size:9px;line-height:1;cursor:pointer;",
|
|
478
|
+
"display:grid;place-items:center;opacity:.85}",
|
|
479
|
+
".dsh-sel-attachx:hover{opacity:1}",
|
|
480
|
+
// 文件卡:和缩略图同高(40px),名字一行、扩展名 + 字节数一行小字
|
|
481
|
+
".dsh-sel-fcard{position:relative;display:inline-flex;align-items:center;gap:7px;flex:0 0 auto;height:40px;",
|
|
482
|
+
"padding:0 7px 0 6px;border-radius:9px;border:1px solid var(--dsw-alias-border-l3,rgba(140,140,140,.35));",
|
|
483
|
+
"background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.1));font-size:11.5px;line-height:1.35}",
|
|
484
|
+
'.dsh-sel-fcard[data-state="error"]{border-color:color-mix(in srgb,var(--sel-bad,#e5534b) 70%,transparent)}',
|
|
485
|
+
".dsh-sel-fico{flex:0 0 auto;width:22px;height:22px;border-radius:6px;display:grid;place-items:center;",
|
|
486
|
+
"background:linear-gradient(160deg,#3f6ee0,#7aa2ff);color:#fff}",
|
|
487
|
+
".dsh-sel-fico svg{width:13px;height:13px;opacity:1}",
|
|
488
|
+
".dsh-sel-fname{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
489
|
+
".dsh-sel-fmeta{font-size:10.5px;opacity:.6;letter-spacing:.3px;white-space:nowrap}",
|
|
490
|
+
".dsh-sel-fretry{flex:0 0 auto;height:16px;padding:0 6px;border-radius:8px;cursor:pointer;font:inherit;font-size:10.5px;",
|
|
491
|
+
"line-height:14px;color:var(--sel-bad,#e5534b);border:1px solid color-mix(in srgb,var(--sel-bad,#e5534b) 50%,transparent);",
|
|
492
|
+
"background:transparent}",
|
|
493
|
+
".dsh-sel-fretry:hover{background:color-mix(in srgb,var(--sel-bad,#e5534b) 12%,transparent)}",
|
|
494
|
+
// 附件出错时的说明行(贴在附件条下面,一行小字)
|
|
495
|
+
".dsh-sel-attachnote{display:none;margin:0;padding:0 2px;font-size:11px;line-height:1.6;color:var(--sel-warn-fg,#b45309)}",
|
|
496
|
+
'.dsh-sel-attachnote[data-show="1"]{display:block}',
|
|
497
|
+
// + 入口:圆形浅底(和主会话 composer 的 + 同款),放在工具行**最左**
|
|
498
|
+
".dsh-sel-add{background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.14));color:var(--dsw-alias-label-primary,CanvasText)}",
|
|
499
|
+
".dsh-sel-add:hover{background:var(--dsw-alias-interactive-bg-active,rgba(140,140,140,.22))}",
|
|
500
|
+
'.dsh-sel-add[data-busy="1"]{opacity:.6}',
|
|
501
|
+
// 拖放遮罩:铺满面板(面板本身 overflow:hidden,所以内缩 6px 的圆角块正好贴住内容区)
|
|
502
|
+
".dsh-sel-drop{position:absolute;inset:6px;z-index:9;display:none;flex-direction:column;align-items:center;",
|
|
503
|
+
"justify-content:center;gap:9px;border:2px dashed var(--sel-a1,#0d9488);border-radius:13px;",
|
|
504
|
+
"background:color-mix(in srgb,var(--dsw-alias-bg-layer-1,Canvas) 88%,transparent);pointer-events:none}",
|
|
505
|
+
'.dsh-sel-panel[data-dropping="1"] .dsh-sel-drop{display:flex}',
|
|
506
|
+
".dsh-sel-drop-art{width:52px;height:52px;border-radius:14px;display:grid;place-items:center;",
|
|
507
|
+
"background:var(--sel-a1-soft);color:var(--sel-a1,#0d9488)}",
|
|
508
|
+
".dsh-sel-drop-art svg{width:25px;height:25px}",
|
|
509
|
+
".dsh-sel-drop-title{font-size:14px;font-weight:600}",
|
|
510
|
+
".dsh-sel-drop-hint{font-size:11.5px;opacity:.7}",
|
|
444
511
|
// 气泡里的引用块(用户消息):引用在上、问题在下 —— 和发出去给模型的那份顺序一致
|
|
445
512
|
".dsh-sel-bq{display:flex;flex-direction:column;gap:3px;margin-bottom:6px}",
|
|
446
513
|
".dsh-sel-bqitem{padding:3px 8px;border-radius:8px;border-left:2px solid color-mix(in srgb,var(--sel-a1,#0d9488) 45%,transparent);",
|
|
447
514
|
"background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.14));font-size:11.5px;line-height:1.55;",
|
|
448
515
|
"opacity:.82;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
449
516
|
".dsh-sel-bqitem b{font-weight:600;opacity:.75;margin-right:5px}",
|
|
517
|
+
// 气泡里的附件:图片是缩略图(点开原图),文件是一张卡(点一下下载)。
|
|
518
|
+
// 和输入框上面那条 rail 是**同一个东西的两个阶段**(发送前 / 发送后),
|
|
519
|
+
// 所以尺寸、圆角、配色都对齐:缩略图 40px、文件卡同高。
|
|
520
|
+
".dsh-sel-bqattach{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:6px}",
|
|
521
|
+
// ⚠️ flex:0 0 auto 会让这个盒子的宽度由**图片自己**撑出来,于是图片上的 max-width:100%
|
|
522
|
+
// 变成"相对于自己"(循环引用)→ 一张 810px 宽的图会照原样渲染并溢出气泡(实测量到 810x180)。
|
|
523
|
+
// 必须允许它收缩(flex:0 1 auto + min-width:0),百分比才有一个确定的参照。
|
|
524
|
+
".dsh-sel-bqimgwrap{flex:0 1 auto;min-width:0;max-width:100%;display:block;border-radius:9px;overflow:hidden;line-height:0;",
|
|
525
|
+
"border:1px solid var(--dsw-alias-border-l3,rgba(140,140,140,.35))}",
|
|
526
|
+
// 上限 180px 高、最宽占满气泡:小窗本来就窄,缩略图不能把气泡撑爆
|
|
527
|
+
".dsh-sel-bqimg{display:block;max-width:100%;max-height:180px;width:auto;height:auto;border-radius:8px;cursor:zoom-in}",
|
|
528
|
+
".dsh-sel-bqimgfail{display:inline-block;padding:5px 9px;border-radius:8px;font-size:11.5px;opacity:.7;",
|
|
529
|
+
"background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.14))}",
|
|
530
|
+
".dsh-sel-bqfile{display:inline-flex;align-items:center;gap:7px;max-width:100%;padding:5px 9px;border-radius:9px;",
|
|
531
|
+
"text-decoration:none;color:inherit;cursor:pointer;font-size:11.5px;line-height:1.35;",
|
|
532
|
+
"border:1px solid var(--dsw-alias-border-l3,rgba(140,140,140,.35));",
|
|
533
|
+
"background:var(--dsw-alias-interactive-bg-hover,rgba(140,140,140,.1))}",
|
|
534
|
+
".dsh-sel-bqfile:hover{border-color:color-mix(in srgb,var(--sel-a1,#0d9488) 45%,transparent)}",
|
|
535
|
+
".dsh-sel-bqfileext{flex:0 0 auto;font-size:9.5px;font-weight:700;letter-spacing:.4px;padding:0 5px;",
|
|
536
|
+
"border-radius:5px;line-height:15px;background:var(--sel-fill,#0f766e);color:var(--sel-fill-fg,#fff)}",
|
|
537
|
+
".dsh-sel-bqfilename{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:190px}",
|
|
538
|
+
".dsh-sel-bqfilemeta{flex:0 0 auto;font-size:10.5px;opacity:.6}",
|
|
450
539
|
// 「引用整条」:贴在助手气泡末尾,**平时不出现**(正文优先),鼠标移到这条消息上才浮出来。
|
|
451
540
|
// 键盘 Tab 也能到(:focus-visible 同样点亮),触屏上另有浮标那条路。
|
|
452
541
|
".dsh-sel-bubquote{margin-top:7px;display:inline-flex;align-items:center;gap:4px;padding:2px 9px;",
|
|
@@ -1128,6 +1217,21 @@ function micIcon() {
|
|
|
1128
1217
|
svg.appendChild(stem);
|
|
1129
1218
|
return svg;
|
|
1130
1219
|
}
|
|
1220
|
+
function spinnerIcon() {
|
|
1221
|
+
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1222
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
1223
|
+
svg.setAttribute("width", "14");
|
|
1224
|
+
svg.setAttribute("height", "14");
|
|
1225
|
+
svg.setAttribute("fill", "none");
|
|
1226
|
+
svg.setAttribute("stroke", "currentColor");
|
|
1227
|
+
svg.setAttribute("stroke-width", "1.7");
|
|
1228
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
1229
|
+
svg.setAttribute("aria-hidden", "true");
|
|
1230
|
+
var arc = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1231
|
+
arc.setAttribute("d", "M8 1.8a6.2 6.2 0 1 1-4.4 1.8");
|
|
1232
|
+
svg.appendChild(arc);
|
|
1233
|
+
return svg;
|
|
1234
|
+
}
|
|
1131
1235
|
function sendIcon() {
|
|
1132
1236
|
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1133
1237
|
svg.setAttribute("viewBox", "0 0 16 16");
|
|
@@ -1178,6 +1282,34 @@ function quoteIcon() {
|
|
|
1178
1282
|
}
|
|
1179
1283
|
return svg;
|
|
1180
1284
|
}
|
|
1285
|
+
function plusIcon() {
|
|
1286
|
+
var svg = iconCanvas(14);
|
|
1287
|
+
iconPath(svg, "M8 3.4v9.2M3.4 8h9.2");
|
|
1288
|
+
return svg;
|
|
1289
|
+
}
|
|
1290
|
+
function docIcon() {
|
|
1291
|
+
var svg = iconCanvas(14);
|
|
1292
|
+
iconPath(svg, "M4.3 1.9h4.4l3 3v9.2H4.3z");
|
|
1293
|
+
iconPath(svg, "M8.5 1.9v3.2h3.2");
|
|
1294
|
+
return svg;
|
|
1295
|
+
}
|
|
1296
|
+
function imageIcon() {
|
|
1297
|
+
var svg = iconCanvas(14);
|
|
1298
|
+
iconPath(svg, "M2.6 3.2h10.8v9.6H2.6z");
|
|
1299
|
+
var dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1300
|
+
dot.setAttribute("cx", "5.7");
|
|
1301
|
+
dot.setAttribute("cy", "6.3");
|
|
1302
|
+
dot.setAttribute("r", "0.9");
|
|
1303
|
+
svg.appendChild(dot);
|
|
1304
|
+
iconPath(svg, "M2.6 10.9l3.1-2.9 2.5 2.3 2.3-2.1 2.9 2.7");
|
|
1305
|
+
return svg;
|
|
1306
|
+
}
|
|
1307
|
+
function retryIcon() {
|
|
1308
|
+
var svg = iconCanvas(13);
|
|
1309
|
+
iconPath(svg, "M12.6 8a4.6 4.6 0 1 1-1.6-3.5");
|
|
1310
|
+
iconPath(svg, "M12.9 2.6v3.1h-3.1");
|
|
1311
|
+
return svg;
|
|
1312
|
+
}
|
|
1181
1313
|
|
|
1182
1314
|
// src/client/index.js
|
|
1183
1315
|
var import_react = __toESM(require("react"), 1);
|
|
@@ -3935,7 +4067,12 @@ function createQuotesController(env) {
|
|
|
3935
4067
|
function exportTurns() {
|
|
3936
4068
|
var out = [];
|
|
3937
4069
|
for (var i = 0; i < env.state.turns.length; i += 1) {
|
|
3938
|
-
|
|
4070
|
+
var turn = env.state.turns[i];
|
|
4071
|
+
out.push({
|
|
4072
|
+
role: turn.role,
|
|
4073
|
+
text: sentTextOf(turn),
|
|
4074
|
+
...turn.attachments && turn.attachments.length > 0 ? { attachments: turn.attachments } : {}
|
|
4075
|
+
});
|
|
3939
4076
|
}
|
|
3940
4077
|
return out;
|
|
3941
4078
|
}
|
|
@@ -3954,6 +4091,383 @@ function createQuotesController(env) {
|
|
|
3954
4091
|
};
|
|
3955
4092
|
}
|
|
3956
4093
|
|
|
4094
|
+
// src/client/attach.js
|
|
4095
|
+
function encodeAttachmentRef(ref) {
|
|
4096
|
+
var json = JSON.stringify(ref || {});
|
|
4097
|
+
if (typeof TextEncoder === "function") {
|
|
4098
|
+
var bytes = new TextEncoder().encode(json);
|
|
4099
|
+
var binary = "";
|
|
4100
|
+
for (var i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
|
|
4101
|
+
json = binary;
|
|
4102
|
+
}
|
|
4103
|
+
return btoa(json).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4104
|
+
}
|
|
4105
|
+
function attachmentUrl(kind, ref, download) {
|
|
4106
|
+
return ATTACHMENT + "?kind=" + (kind === "file" ? "file" : "image") + "&ref=" + encodeAttachmentRef(ref) + (download ? "&download=1" : "");
|
|
4107
|
+
}
|
|
4108
|
+
function formatBytes(bytes) {
|
|
4109
|
+
var value = typeof bytes === "number" && isFinite(bytes) ? bytes : 0;
|
|
4110
|
+
if (value < 1024) return value + " B";
|
|
4111
|
+
if (value < 1024 * 1024) return (value / 1024).toFixed(1) + " KB";
|
|
4112
|
+
return (value / 1024 / 1024).toFixed(1) + " MB";
|
|
4113
|
+
}
|
|
4114
|
+
function extensionOf(name) {
|
|
4115
|
+
var match = /\.([A-Za-z0-9]+)$/.exec(String(name || ""));
|
|
4116
|
+
return match ? match[1].toUpperCase() : "FILE";
|
|
4117
|
+
}
|
|
4118
|
+
function fileToBase64(file) {
|
|
4119
|
+
return new Promise(function(resolve, reject) {
|
|
4120
|
+
var reader = new FileReader();
|
|
4121
|
+
reader.onload = function() {
|
|
4122
|
+
var result = String(reader.result || "");
|
|
4123
|
+
var comma = result.indexOf(",");
|
|
4124
|
+
resolve(comma >= 0 ? result.slice(comma + 1) : "");
|
|
4125
|
+
};
|
|
4126
|
+
reader.onerror = function() {
|
|
4127
|
+
reject(reader.error || new Error("\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25"));
|
|
4128
|
+
};
|
|
4129
|
+
reader.readAsDataURL(file);
|
|
4130
|
+
});
|
|
4131
|
+
}
|
|
4132
|
+
function looksLikeDirectory(file) {
|
|
4133
|
+
return file && file.size === 0 && !file.type && !/\.[A-Za-z0-9]+$/.test(String(file.name || ""));
|
|
4134
|
+
}
|
|
4135
|
+
function createAttachController(env) {
|
|
4136
|
+
var seq = 0;
|
|
4137
|
+
function items() {
|
|
4138
|
+
return env.state.attachments;
|
|
4139
|
+
}
|
|
4140
|
+
function note(text) {
|
|
4141
|
+
env.state.attachNote = text || "";
|
|
4142
|
+
renderNote();
|
|
4143
|
+
}
|
|
4144
|
+
function renderNote() {
|
|
4145
|
+
if (!env.attachNote) return;
|
|
4146
|
+
env.attachNote.textContent = env.state.attachNote || "";
|
|
4147
|
+
env.attachNote.setAttribute("data-show", env.state.attachNote ? "1" : "0");
|
|
4148
|
+
}
|
|
4149
|
+
function busy() {
|
|
4150
|
+
var list = items();
|
|
4151
|
+
for (var i = 0; i < list.length; i += 1) if (list[i].status === "uploading") return true;
|
|
4152
|
+
return false;
|
|
4153
|
+
}
|
|
4154
|
+
function payload() {
|
|
4155
|
+
var out = [];
|
|
4156
|
+
var list = items();
|
|
4157
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4158
|
+
if (list[i].status === "done" && list[i].ref) out.push({ kind: list[i].kind, ref: list[i].ref });
|
|
4159
|
+
}
|
|
4160
|
+
return out;
|
|
4161
|
+
}
|
|
4162
|
+
function describe(explicit) {
|
|
4163
|
+
var list = explicit || items();
|
|
4164
|
+
var names = [];
|
|
4165
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4166
|
+
if (list[i].status !== "done" || !list[i].ref) continue;
|
|
4167
|
+
names.push(list[i].name + (list[i].kind === "image" ? "\uFF08\u56FE\u7247\uFF09" : "\uFF08\u6587\u4EF6\uFF09"));
|
|
4168
|
+
}
|
|
4169
|
+
if (names.length === 0) return "";
|
|
4170
|
+
return "\u3010\u672C\u6761\u6D88\u606F\u5E26\u4E86\u9644\u4EF6\u3011" + names.join("\u3001");
|
|
4171
|
+
}
|
|
4172
|
+
function clear() {
|
|
4173
|
+
var list = items();
|
|
4174
|
+
for (var i = 0; i < list.length; i += 1) revoke(list[i]);
|
|
4175
|
+
env.state.attachments = [];
|
|
4176
|
+
env.state.attachNote = "";
|
|
4177
|
+
render();
|
|
4178
|
+
}
|
|
4179
|
+
function revoke(item) {
|
|
4180
|
+
if (item && item.url && typeof URL !== "undefined" && URL.revokeObjectURL) {
|
|
4181
|
+
try {
|
|
4182
|
+
URL.revokeObjectURL(item.url);
|
|
4183
|
+
} catch (error) {
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
function remove(id) {
|
|
4188
|
+
var list = items();
|
|
4189
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4190
|
+
if (list[i].id === id) {
|
|
4191
|
+
revoke(list[i]);
|
|
4192
|
+
list.splice(i, 1);
|
|
4193
|
+
break;
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
env.state.attachNote = "";
|
|
4197
|
+
render();
|
|
4198
|
+
env.refreshAskState();
|
|
4199
|
+
}
|
|
4200
|
+
function addFiles(files) {
|
|
4201
|
+
var list = items();
|
|
4202
|
+
var incoming = [];
|
|
4203
|
+
for (var i = 0; i < files.length; i += 1) incoming.push(files[i]);
|
|
4204
|
+
if (incoming.length === 0) return;
|
|
4205
|
+
var rejected = [];
|
|
4206
|
+
for (var j = 0; j < incoming.length; j += 1) {
|
|
4207
|
+
var file = incoming[j];
|
|
4208
|
+
if (looksLikeDirectory(file)) {
|
|
4209
|
+
rejected.push("\u300C" + (file.name || "\u672A\u547D\u540D") + "\u300D\u662F\u6587\u4EF6\u5939\uFF0C\u4E0D\u652F\u6301");
|
|
4210
|
+
continue;
|
|
4211
|
+
}
|
|
4212
|
+
if (list.length >= ATTACH_MAX_ITEMS) {
|
|
4213
|
+
rejected.push("\u4E00\u6B21\u6700\u591A\u5E26 " + ATTACH_MAX_ITEMS + " \u4EF6\u9644\u4EF6");
|
|
4214
|
+
break;
|
|
4215
|
+
}
|
|
4216
|
+
if (file.size > ATTACH_MAX_ITEM_BYTES) {
|
|
4217
|
+
rejected.push("\u300C" + file.name + "\u300D\u8D85\u8FC7\u5355\u4EF6\u4E0A\u9650 " + formatBytes(ATTACH_MAX_ITEM_BYTES));
|
|
4218
|
+
continue;
|
|
4219
|
+
}
|
|
4220
|
+
list.push(createItem(file));
|
|
4221
|
+
}
|
|
4222
|
+
note(rejected.join("\uFF1B"));
|
|
4223
|
+
render();
|
|
4224
|
+
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
|
+
}
|
|
4235
|
+
function createItem(file) {
|
|
4236
|
+
var isImage = /^image\//.test(String(file.type || ""));
|
|
4237
|
+
var item = {
|
|
4238
|
+
id: ++seq,
|
|
4239
|
+
kind: isImage ? "image" : "file",
|
|
4240
|
+
name: file.name || (isImage ? "\u7C98\u8D34\u7684\u56FE\u7247" : "\u672A\u547D\u540D\u6587\u4EF6"),
|
|
4241
|
+
size: typeof file.size === "number" ? file.size : 0,
|
|
4242
|
+
mediaType: file.type || "application/octet-stream",
|
|
4243
|
+
file,
|
|
4244
|
+
// 缩略图先用本地 object URL 显示(不必等上传完),失败时回落到图标
|
|
4245
|
+
url: isImage && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : "",
|
|
4246
|
+
status: "uploading",
|
|
4247
|
+
started: false,
|
|
4248
|
+
ref: null,
|
|
4249
|
+
error: ""
|
|
4250
|
+
};
|
|
4251
|
+
return item;
|
|
4252
|
+
}
|
|
4253
|
+
function upload(item) {
|
|
4254
|
+
if (item.started) return Promise.resolve();
|
|
4255
|
+
item.started = true;
|
|
4256
|
+
item.status = "uploading";
|
|
4257
|
+
item.error = "";
|
|
4258
|
+
render();
|
|
4259
|
+
return fileToBase64(item.file).then(function(data) {
|
|
4260
|
+
return fetch(ATTACH, {
|
|
4261
|
+
method: "POST",
|
|
4262
|
+
headers: { "content-type": "application/json" },
|
|
4263
|
+
body: JSON.stringify({
|
|
4264
|
+
items: [{ kind: item.kind, name: item.name, mediaType: item.mediaType, data }]
|
|
4265
|
+
})
|
|
4266
|
+
});
|
|
4267
|
+
}).then(function(response) {
|
|
4268
|
+
return response.json().catch(function() {
|
|
4269
|
+
return {};
|
|
4270
|
+
}).then(function(body) {
|
|
4271
|
+
if (!response.ok || !body || body.ok !== true) {
|
|
4272
|
+
throw new Error(body && body.error || "HTTP " + response.status);
|
|
4273
|
+
}
|
|
4274
|
+
var first = body.items && body.items[0];
|
|
4275
|
+
if (!first || !first.ref) throw new Error("\u4E0A\u4F20\u540E\u6CA1\u6709\u62FF\u5230\u9644\u4EF6\u5F15\u7528");
|
|
4276
|
+
item.ref = first.ref;
|
|
4277
|
+
item.status = "done";
|
|
4278
|
+
item.file = null;
|
|
4279
|
+
if (first.ref.name) item.name = String(first.ref.name);
|
|
4280
|
+
if (typeof first.ref.bytes === "number") item.size = first.ref.bytes;
|
|
4281
|
+
if (env.state.attachNote.indexOf(item.name) >= 0) note("");
|
|
4282
|
+
});
|
|
4283
|
+
}).catch(function(error) {
|
|
4284
|
+
item.status = "error";
|
|
4285
|
+
item.error = String(error && error.message || error);
|
|
4286
|
+
note("\u300C" + item.name + "\u300D" + item.error);
|
|
4287
|
+
}).then(function() {
|
|
4288
|
+
render();
|
|
4289
|
+
env.refreshAskState();
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
function retry(id) {
|
|
4293
|
+
var list = items();
|
|
4294
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4295
|
+
if (list[i].id === id && list[i].status === "error") {
|
|
4296
|
+
if (!list[i].file) {
|
|
4297
|
+
note("\u8FD9\u4E00\u4EF6\u5DF2\u7ECF\u6CA1\u6CD5\u91CD\u8BD5\u4E86\uFF0C\u8BF7\u91CD\u65B0\u6DFB\u52A0");
|
|
4298
|
+
return;
|
|
4299
|
+
}
|
|
4300
|
+
list[i].started = false;
|
|
4301
|
+
upload(list[i]);
|
|
4302
|
+
return;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
function render() {
|
|
4307
|
+
var box = env.attachBox;
|
|
4308
|
+
if (!box) return;
|
|
4309
|
+
var list = items();
|
|
4310
|
+
box.textContent = "";
|
|
4311
|
+
box.setAttribute("data-show", list.length > 0 ? "1" : "0");
|
|
4312
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
4313
|
+
box.appendChild(list[i].kind === "image" ? thumbNode(list[i]) : fileNode(list[i]));
|
|
4314
|
+
}
|
|
4315
|
+
renderNote();
|
|
4316
|
+
}
|
|
4317
|
+
function removeButton(item) {
|
|
4318
|
+
var button = el("button", "dsh-sel-attachx", "\u2715");
|
|
4319
|
+
button.type = "button";
|
|
4320
|
+
button.title = "\u79FB\u9664\u300C" + item.name + "\u300D";
|
|
4321
|
+
button.setAttribute("aria-label", "\u79FB\u9664\u9644\u4EF6 " + item.name);
|
|
4322
|
+
listen(button, "click", function(event) {
|
|
4323
|
+
event.stopPropagation();
|
|
4324
|
+
remove(item.id);
|
|
4325
|
+
});
|
|
4326
|
+
return button;
|
|
4327
|
+
}
|
|
4328
|
+
function thumbNode(item) {
|
|
4329
|
+
var node = el("span", "dsh-sel-thumb");
|
|
4330
|
+
node.setAttribute("data-state", item.status);
|
|
4331
|
+
node.title = item.name;
|
|
4332
|
+
if (item.url) {
|
|
4333
|
+
var img = document.createElement("img");
|
|
4334
|
+
img.src = item.url;
|
|
4335
|
+
img.alt = item.name;
|
|
4336
|
+
node.appendChild(img);
|
|
4337
|
+
} else {
|
|
4338
|
+
node.appendChild(imageIcon());
|
|
4339
|
+
}
|
|
4340
|
+
if (item.status === "uploading") {
|
|
4341
|
+
var spin = el("span", "dsh-sel-attachspin");
|
|
4342
|
+
spin.appendChild(spinnerIcon());
|
|
4343
|
+
node.appendChild(spin);
|
|
4344
|
+
var bar = el("span", "dsh-sel-attachbar");
|
|
4345
|
+
bar.appendChild(el("i"));
|
|
4346
|
+
node.appendChild(bar);
|
|
4347
|
+
} else if (item.status === "error") {
|
|
4348
|
+
var retryBox = el("span", "dsh-sel-attachspin");
|
|
4349
|
+
retryBox.style.background = "transparent";
|
|
4350
|
+
retryBox.title = "\u4E0A\u4F20\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5";
|
|
4351
|
+
var retryButton = el("button", "dsh-sel-attachx", "");
|
|
4352
|
+
retryButton.type = "button";
|
|
4353
|
+
retryButton.style.position = "static";
|
|
4354
|
+
retryButton.style.width = "20px";
|
|
4355
|
+
retryButton.style.height = "20px";
|
|
4356
|
+
retryButton.setAttribute("aria-label", "\u91CD\u8BD5\u4E0A\u4F20 " + item.name);
|
|
4357
|
+
retryButton.appendChild(retryIcon());
|
|
4358
|
+
listen(retryButton, "click", function(event) {
|
|
4359
|
+
event.stopPropagation();
|
|
4360
|
+
retry(item.id);
|
|
4361
|
+
});
|
|
4362
|
+
retryBox.appendChild(retryButton);
|
|
4363
|
+
node.appendChild(retryBox);
|
|
4364
|
+
}
|
|
4365
|
+
node.appendChild(removeButton(item));
|
|
4366
|
+
return node;
|
|
4367
|
+
}
|
|
4368
|
+
function fileNode(item) {
|
|
4369
|
+
var node = el("span", "dsh-sel-fcard");
|
|
4370
|
+
node.setAttribute("data-state", item.status);
|
|
4371
|
+
var icon = el("span", "dsh-sel-fico");
|
|
4372
|
+
icon.appendChild(docIcon());
|
|
4373
|
+
node.appendChild(icon);
|
|
4374
|
+
node.appendChild(el("span", "dsh-sel-fname", item.name));
|
|
4375
|
+
var meta = extensionOf(item.name) + " \xB7 " + formatBytes(item.size);
|
|
4376
|
+
if (item.status === "uploading") meta = "\u6B63\u5728\u4E0A\u4F20\u2026";
|
|
4377
|
+
node.appendChild(el("span", "dsh-sel-fmeta", meta));
|
|
4378
|
+
if (item.status === "error") {
|
|
4379
|
+
var retryButton = el("button", "dsh-sel-fretry", "\u91CD\u8BD5");
|
|
4380
|
+
retryButton.type = "button";
|
|
4381
|
+
listen(retryButton, "click", function(event) {
|
|
4382
|
+
event.stopPropagation();
|
|
4383
|
+
retry(item.id);
|
|
4384
|
+
});
|
|
4385
|
+
node.appendChild(retryButton);
|
|
4386
|
+
}
|
|
4387
|
+
node.appendChild(removeButton(item));
|
|
4388
|
+
return node;
|
|
4389
|
+
}
|
|
4390
|
+
function pick() {
|
|
4391
|
+
if (!env.attachInput) return;
|
|
4392
|
+
env.attachInput.value = "";
|
|
4393
|
+
env.attachInput.click();
|
|
4394
|
+
}
|
|
4395
|
+
function handlePaste(event) {
|
|
4396
|
+
var files = event.clipboardData && event.clipboardData.files;
|
|
4397
|
+
if (!files || files.length === 0) return;
|
|
4398
|
+
event.preventDefault();
|
|
4399
|
+
addFiles(files);
|
|
4400
|
+
env.setStatus("\u5DF2\u4ECE\u526A\u8D34\u677F\u6DFB\u52A0 " + files.length + " \u4EF6\u9644\u4EF6");
|
|
4401
|
+
}
|
|
4402
|
+
function isFileDrag(event) {
|
|
4403
|
+
var transfer = event.dataTransfer;
|
|
4404
|
+
if (!transfer) return false;
|
|
4405
|
+
var types = transfer.types;
|
|
4406
|
+
if (!types) return false;
|
|
4407
|
+
for (var i = 0; i < types.length; i += 1) {
|
|
4408
|
+
if (types[i] === "Files") return true;
|
|
4409
|
+
}
|
|
4410
|
+
return false;
|
|
4411
|
+
}
|
|
4412
|
+
function wireDrop() {
|
|
4413
|
+
var depth = 0;
|
|
4414
|
+
var panel = env.panel;
|
|
4415
|
+
if (!panel) return;
|
|
4416
|
+
function show() {
|
|
4417
|
+
panel.setAttribute("data-dropping", "1");
|
|
4418
|
+
}
|
|
4419
|
+
function hide() {
|
|
4420
|
+
depth = 0;
|
|
4421
|
+
panel.removeAttribute("data-dropping");
|
|
4422
|
+
}
|
|
4423
|
+
listen(panel, "dragenter", function(event) {
|
|
4424
|
+
if (!isFileDrag(event)) return;
|
|
4425
|
+
event.preventDefault();
|
|
4426
|
+
event.stopPropagation();
|
|
4427
|
+
depth += 1;
|
|
4428
|
+
show();
|
|
4429
|
+
});
|
|
4430
|
+
listen(panel, "dragover", function(event) {
|
|
4431
|
+
if (!isFileDrag(event)) return;
|
|
4432
|
+
event.preventDefault();
|
|
4433
|
+
event.stopPropagation();
|
|
4434
|
+
if (event.dataTransfer.dropEffect !== void 0) event.dataTransfer.dropEffect = "copy";
|
|
4435
|
+
});
|
|
4436
|
+
listen(panel, "dragleave", function(event) {
|
|
4437
|
+
if (!isFileDrag(event)) return;
|
|
4438
|
+
event.stopPropagation();
|
|
4439
|
+
depth -= 1;
|
|
4440
|
+
if (depth <= 0) hide();
|
|
4441
|
+
});
|
|
4442
|
+
listen(panel, "drop", function(event) {
|
|
4443
|
+
if (!isFileDrag(event)) return;
|
|
4444
|
+
event.preventDefault();
|
|
4445
|
+
event.stopPropagation();
|
|
4446
|
+
hide();
|
|
4447
|
+
var files = event.dataTransfer.files;
|
|
4448
|
+
if (files && files.length > 0) {
|
|
4449
|
+
addFiles(files);
|
|
4450
|
+
env.setStatus("\u5DF2\u6DFB\u52A0 " + files.length + " \u4EF6\u9644\u4EF6");
|
|
4451
|
+
}
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
return {
|
|
4455
|
+
items,
|
|
4456
|
+
addFiles,
|
|
4457
|
+
remove,
|
|
4458
|
+
clear,
|
|
4459
|
+
retry,
|
|
4460
|
+
render,
|
|
4461
|
+
busy,
|
|
4462
|
+
payload,
|
|
4463
|
+
describe,
|
|
4464
|
+
note,
|
|
4465
|
+
pick,
|
|
4466
|
+
handlePaste,
|
|
4467
|
+
wireDrop
|
|
4468
|
+
};
|
|
4469
|
+
}
|
|
4470
|
+
|
|
3957
4471
|
// src/client/bridge.js
|
|
3958
4472
|
function bridgeBody() {
|
|
3959
4473
|
if (window.__dshSelBridge) return;
|
|
@@ -4515,6 +5029,7 @@ function createPanelController(env) {
|
|
|
4515
5029
|
env.state.turns = [];
|
|
4516
5030
|
env.state.toolDigest = "";
|
|
4517
5031
|
env.clearQuotes();
|
|
5032
|
+
env.clearAttachments();
|
|
4518
5033
|
env.state.parts = cached && cached.parts || { translation: "", detail: "" };
|
|
4519
5034
|
env.state.stage = "";
|
|
4520
5035
|
env.state.raw = "";
|
|
@@ -6339,7 +6854,9 @@ function createHistoryController(env) {
|
|
|
6339
6854
|
if (turn.role === "user") {
|
|
6340
6855
|
var usig = (turn.text || "") + "|" + (turn.quotes || []).map(function(item) {
|
|
6341
6856
|
return item.text;
|
|
6342
|
-
}).join("\0")
|
|
6857
|
+
}).join("\0") + "|" + (turn.attachments || []).map(function(item) {
|
|
6858
|
+
return (item.kind || "") + ":" + String(item.ref && item.ref.attachmentId || "");
|
|
6859
|
+
}).join(",");
|
|
6343
6860
|
if (node.__sig !== usig) {
|
|
6344
6861
|
node.__sig = usig;
|
|
6345
6862
|
fillUserBubble(node, turn);
|
|
@@ -6393,6 +6910,10 @@ function createHistoryController(env) {
|
|
|
6393
6910
|
function fillUserBubble(node, turn) {
|
|
6394
6911
|
node.textContent = "";
|
|
6395
6912
|
var quotes = turn.quotes || [];
|
|
6913
|
+
var attachments = turn.attachments || [];
|
|
6914
|
+
if (attachments.length > 0) {
|
|
6915
|
+
node.appendChild(attachmentStrip(attachments));
|
|
6916
|
+
}
|
|
6396
6917
|
if (quotes.length > 0) {
|
|
6397
6918
|
var box = el("div", "dsh-sel-bq");
|
|
6398
6919
|
for (var i = 0; i < quotes.length; i += 1) {
|
|
@@ -6404,7 +6925,57 @@ function createHistoryController(env) {
|
|
|
6404
6925
|
}
|
|
6405
6926
|
node.appendChild(box);
|
|
6406
6927
|
}
|
|
6407
|
-
|
|
6928
|
+
var display = displayTextOf(turn);
|
|
6929
|
+
if (display) node.appendChild(el("div", null, display));
|
|
6930
|
+
}
|
|
6931
|
+
function attachmentStrip(attachments) {
|
|
6932
|
+
var strip = el("div", "dsh-sel-bqattach");
|
|
6933
|
+
for (var i = 0; i < attachments.length; i += 1) {
|
|
6934
|
+
var item = attachments[i] || {};
|
|
6935
|
+
var ref = item.ref || {};
|
|
6936
|
+
if (item.kind === "image") {
|
|
6937
|
+
strip.appendChild(attachmentImage(ref));
|
|
6938
|
+
} else {
|
|
6939
|
+
strip.appendChild(attachmentFile(ref));
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
return strip;
|
|
6943
|
+
}
|
|
6944
|
+
function attachmentImage(ref) {
|
|
6945
|
+
var link = document.createElement("a");
|
|
6946
|
+
link.className = "dsh-sel-bqimgwrap";
|
|
6947
|
+
link.href = attachmentUrl("image", ref);
|
|
6948
|
+
link.target = "_blank";
|
|
6949
|
+
link.rel = "noreferrer noopener";
|
|
6950
|
+
link.title = "\u70B9\u51FB\u67E5\u770B\u539F\u56FE" + (ref.name ? "\uFF1A" + ref.name : "");
|
|
6951
|
+
var img = document.createElement("img");
|
|
6952
|
+
img.className = "dsh-sel-bqimg";
|
|
6953
|
+
img.src = link.href;
|
|
6954
|
+
img.alt = ref.name || "\u56FE\u7247";
|
|
6955
|
+
img.loading = "lazy";
|
|
6956
|
+
img.addEventListener("error", function() {
|
|
6957
|
+
var box = el("span", "dsh-sel-bqimgfail", "\u56FE\u7247\u5DF2\u4E0D\u5728\u9644\u4EF6\u4ED3\u91CC");
|
|
6958
|
+
if (link.parentNode) link.parentNode.replaceChild(box, link);
|
|
6959
|
+
});
|
|
6960
|
+
link.appendChild(img);
|
|
6961
|
+
return link;
|
|
6962
|
+
}
|
|
6963
|
+
function attachmentFile(ref) {
|
|
6964
|
+
var link = document.createElement("a");
|
|
6965
|
+
link.className = "dsh-sel-bqfile";
|
|
6966
|
+
link.href = attachmentUrl("file", ref, true);
|
|
6967
|
+
link.rel = "noreferrer noopener";
|
|
6968
|
+
link.title = "\u4E0B\u8F7D\uFF1A" + (ref.name || "\u6587\u4EF6");
|
|
6969
|
+
link.appendChild(el("span", "dsh-sel-bqfileext", ref.name ? extensionOf(ref.name) : "FILE"));
|
|
6970
|
+
link.appendChild(el("span", "dsh-sel-bqfilename", ref.name || "\u6587\u4EF6"));
|
|
6971
|
+
if (typeof ref.bytes === "number") link.appendChild(el("span", "dsh-sel-bqfilemeta", formatBytes(ref.bytes)));
|
|
6972
|
+
return link;
|
|
6973
|
+
}
|
|
6974
|
+
function displayTextOf(turn) {
|
|
6975
|
+
var text = turn.text || "";
|
|
6976
|
+
if (!text) return "";
|
|
6977
|
+
if (!turn.attachments || turn.attachments.length === 0) return text;
|
|
6978
|
+
return text.replace(/^【本条消息带了附件】[^\n]*\n?/, "");
|
|
6408
6979
|
}
|
|
6409
6980
|
function bubbleFor(turn) {
|
|
6410
6981
|
var bubble = el("div", "dsh-sel-bubble " + (turn.role === "user" ? "dsh-sel-bubble-user" : "dsh-sel-bubble-bot"));
|
|
@@ -6539,7 +7110,10 @@ function createConversationController(env) {
|
|
|
6539
7110
|
env.askSend.setAttribute("aria-label", generating ? "\u505C\u6B62" : "\u53D1\u9001");
|
|
6540
7111
|
env.askSend.title = generating ? "\u505C\u6B62\u751F\u6210\uFF08\u70B9\u4E00\u4E0B\u6253\u65AD\u8FD9\u6B21\u8F93\u51FA\uFF09" : "\u53D1\u9001\uFF08Enter\uFF09";
|
|
6541
7112
|
}
|
|
6542
|
-
|
|
7113
|
+
var waitingAttachments = env.attachmentsBusy();
|
|
7114
|
+
env.askSend.disabled = generating ? false : waitingAttachments || !String(env.askBox.value || "").trim() && env.state.quotes.length === 0 && env.state.attachments.length === 0;
|
|
7115
|
+
var placeholder = env.state.attachments.length > 0 ? "\u5C31\u8FD9\u6BB5\u6587\u5B57\u7EE7\u7EED\u8FFD\u95EE\u2026\uFF08Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\uFF09" : "\u5C31\u8FD9\u6BB5\u6587\u5B57\u7EE7\u7EED\u8FFD\u95EE\u2026\uFF08\u53EF\u62D6\u5165\u56FE\u7247 / \u6587\u4EF6\uFF0C\u6216 \u2318V \u7C98\u8D34\uFF09";
|
|
7116
|
+
if (env.askBox.placeholder !== placeholder) env.askBox.placeholder = placeholder;
|
|
6543
7117
|
}
|
|
6544
7118
|
function stopCurrent() {
|
|
6545
7119
|
if (env.state.askRequest) {
|
|
@@ -6572,7 +7146,12 @@ function createConversationController(env) {
|
|
|
6572
7146
|
}
|
|
6573
7147
|
var asked = String(question || "").trim();
|
|
6574
7148
|
var quotes = env.state.quotes.slice();
|
|
6575
|
-
|
|
7149
|
+
var attachments = env.attachmentsPayload();
|
|
7150
|
+
if (!asked && quotes.length === 0 && attachments.length === 0 || !env.state.payload) return;
|
|
7151
|
+
if (env.attachmentsBusy()) {
|
|
7152
|
+
env.setStatus("\u9644\u4EF6\u8FD8\u5728\u4E0A\u4F20\uFF0C\u7A0D\u7B49\u4E00\u4E0B\u2026");
|
|
7153
|
+
return;
|
|
7154
|
+
}
|
|
6576
7155
|
if (env.state.resolvingQuotes) {
|
|
6577
7156
|
env.setStatus("\u6B63\u5728\u53D6\u5F15\u7528\u7684\u4E0A\u4E0B\u6587\u2026");
|
|
6578
7157
|
return;
|
|
@@ -6586,24 +7165,31 @@ function createConversationController(env) {
|
|
|
6586
7165
|
env.renderQuotes();
|
|
6587
7166
|
refreshAskState();
|
|
6588
7167
|
}
|
|
7168
|
+
var attachmentsNote = env.attachmentsDescribe();
|
|
7169
|
+
if (attachments.length > 0) {
|
|
7170
|
+
env.clearAttachments();
|
|
7171
|
+
refreshAskState();
|
|
7172
|
+
}
|
|
6589
7173
|
if (quotes.length === 0) {
|
|
6590
|
-
sendAsk(asked, quotes);
|
|
7174
|
+
sendAsk(asked, quotes, attachments, attachmentsNote);
|
|
6591
7175
|
return;
|
|
6592
7176
|
}
|
|
6593
7177
|
env.state.resolvingQuotes = true;
|
|
6594
7178
|
env.resolveQuoteContexts(quotes).then(function(resolved) {
|
|
6595
7179
|
env.state.resolvingQuotes = false;
|
|
6596
|
-
sendAsk(asked, resolved);
|
|
7180
|
+
sendAsk(asked, resolved, attachments, attachmentsNote);
|
|
6597
7181
|
});
|
|
6598
7182
|
}
|
|
6599
|
-
function sendAsk(asked, quotes) {
|
|
7183
|
+
function sendAsk(asked, quotes, attachments, attachmentsNote) {
|
|
6600
7184
|
var payload = env.state.payload;
|
|
6601
7185
|
if (!payload) return;
|
|
7186
|
+
var withAttachments = attachments || [];
|
|
6602
7187
|
var sent = env.composeQuestion(quotes, asked);
|
|
7188
|
+
if (attachmentsNote) sent = attachmentsNote + "\n" + sent;
|
|
6603
7189
|
if (env.state.turns.length === 0 && (env.state.raw.trim() || env.state.parts.translation || env.state.parts.detail)) {
|
|
6604
7190
|
env.state.turns.push({ role: "assistant", text: env.seedHistoryFromExplanation(), seed: true, hidden: true });
|
|
6605
7191
|
}
|
|
6606
|
-
env.state.turns.push({ role: "user", text: asked || QUOTE_ONLY_QUESTION, quotes, sent });
|
|
7192
|
+
env.state.turns.push({ role: "user", text: asked || QUOTE_ONLY_QUESTION, quotes, sent, attachments: withAttachments });
|
|
6607
7193
|
var answerTurn = {
|
|
6608
7194
|
role: "assistant",
|
|
6609
7195
|
text: "",
|
|
@@ -6636,7 +7222,11 @@ function createConversationController(env) {
|
|
|
6636
7222
|
var historyTurns = env.state.turns.slice(0, env.state.turns.length - 2);
|
|
6637
7223
|
var history = historyTurns.map(function(turn, index) {
|
|
6638
7224
|
var keepRaw = env.FOLD_HISTORY_KEEP_LATEST && index === historyTurns.length - 1;
|
|
6639
|
-
return {
|
|
7225
|
+
return {
|
|
7226
|
+
role: turn.role,
|
|
7227
|
+
text: env.historyTextOf(turn, keepRaw),
|
|
7228
|
+
...turn.attachments && turn.attachments.length > 0 ? { attachments: turn.attachments } : {}
|
|
7229
|
+
};
|
|
6640
7230
|
});
|
|
6641
7231
|
env.state.stopped = false;
|
|
6642
7232
|
var askController = new AbortController();
|
|
@@ -6683,6 +7273,8 @@ function createConversationController(env) {
|
|
|
6683
7273
|
sessionId: env.panelOrCurrentSessionId(),
|
|
6684
7274
|
question: sent,
|
|
6685
7275
|
history,
|
|
7276
|
+
// 附件只带**引用**(图片/文件都已经在 host 的附件服务里),body 不会因为一张截图胖几 MB
|
|
7277
|
+
...withAttachments.length > 0 ? { attachments: withAttachments } : {},
|
|
6686
7278
|
// 网页输出要跟着当前深浅走:深色 Harness 里给一张白底页面会很刺眼
|
|
6687
7279
|
...env.state.webAnswer ? { webAnswer: true, theme: env.currentSurfaceTheme() } : {},
|
|
6688
7280
|
...env.state.modelChoice ? { provider: env.state.modelChoice.provider, model: env.state.modelChoice.model } : {},
|
|
@@ -8565,6 +9157,14 @@ function apply(ctx) {
|
|
|
8565
9157
|
* 只属于**下一条**提问(发送即清空);关掉面板不清 —— 回来接着写还看得见。
|
|
8566
9158
|
*/
|
|
8567
9159
|
quotes: [],
|
|
9160
|
+
/**
|
|
9161
|
+
* 待发送的附件:[{ id, kind:'image'|'file', name, size, mediaType, file, url, status, ref, error }]。
|
|
9162
|
+
* 和引用同一条规矩 —— 只属于**下一条**追问,发送即清空;
|
|
9163
|
+
* 区别是上传状态挂在每一项上(逐件上传、逐件重试,见 attach.js)。
|
|
9164
|
+
*/
|
|
9165
|
+
attachments: [],
|
|
9166
|
+
/** 附件条下面那行提示("文件夹不支持""一次最多 10 件"之类),空串就不显示。 */
|
|
9167
|
+
attachNote: "",
|
|
8568
9168
|
/** 最近一次"可以引用"的选区:[{ text, label, rect, source }](浮标据此显示)。 */
|
|
8569
9169
|
quoteSelection: null,
|
|
8570
9170
|
/** 正在取引用的会话上下文(这期间再按 Enter 不能空发一条)。 */
|
|
@@ -8685,14 +9285,25 @@ function apply(ctx) {
|
|
|
8685
9285
|
var askRow = el("div", "dsh-sel-ask");
|
|
8686
9286
|
var quotesBox = el("div", "dsh-sel-quotes");
|
|
8687
9287
|
askRow.appendChild(quotesBox);
|
|
9288
|
+
var attachBox = el("div", "dsh-sel-attach");
|
|
9289
|
+
askRow.appendChild(attachBox);
|
|
9290
|
+
var attachNote = el("p", "dsh-sel-attachnote");
|
|
9291
|
+
askRow.appendChild(attachNote);
|
|
9292
|
+
var attachInput = el("input");
|
|
9293
|
+
attachInput.type = "file";
|
|
9294
|
+
attachInput.multiple = true;
|
|
9295
|
+
attachInput.style.display = "none";
|
|
9296
|
+
attachInput.setAttribute("aria-hidden", "true");
|
|
9297
|
+
attachInput.tabIndex = -1;
|
|
9298
|
+
askRow.appendChild(attachInput);
|
|
8688
9299
|
var askBox = el("textarea", "dsh-sel-askbox");
|
|
8689
9300
|
askBox.rows = 1;
|
|
8690
9301
|
askBox.placeholder = "\u5C31\u8FD9\u6BB5\u6587\u5B57\u7EE7\u7EED\u8FFD\u95EE\u2026\uFF08Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\uFF09";
|
|
8691
9302
|
var webMode = el("span", "dsh-sel-pref");
|
|
8692
|
-
webMode.
|
|
9303
|
+
webMode.title = "\u8F93\u51FA\u504F\u597D\uFF1AMarkdown\uFF08\u9ED8\u8BA4\u6587\u5B57\u56DE\u7B54\uFF09/ \u7F51\u9875\uFF08\u590D\u6742\u95EE\u9898\u751F\u6210 HTML \u9875\u9762\uFF09";
|
|
8693
9304
|
var prefSeg = el("span", "dsh-sel-seg");
|
|
8694
9305
|
prefSeg.setAttribute("role", "radiogroup");
|
|
8695
|
-
prefSeg.setAttribute("aria-label", "\u8F93\u51FA\u504F\u597D");
|
|
9306
|
+
prefSeg.setAttribute("aria-label", "\u8F93\u51FA\u504F\u597D\uFF1AMarkdown \u6216\u7F51\u9875");
|
|
8696
9307
|
var prefPill = el("span", "dsh-sel-segpill");
|
|
8697
9308
|
var cellMd = el("button", "dsh-sel-segcell");
|
|
8698
9309
|
cellMd.type = "button";
|
|
@@ -8773,7 +9384,13 @@ function apply(ctx) {
|
|
|
8773
9384
|
captureRow.appendChild(voiceActionSlot);
|
|
8774
9385
|
captureRow.appendChild(voiceStop);
|
|
8775
9386
|
var askTools = el("div", "dsh-sel-asktools");
|
|
9387
|
+
var addButton = el("button", "dsh-sel-iconbtn dsh-sel-add");
|
|
9388
|
+
addButton.type = "button";
|
|
9389
|
+
addButton.setAttribute("aria-label", "\u6DFB\u52A0\u56FE\u7247\u6216\u6587\u4EF6");
|
|
9390
|
+
addButton.title = "\u6DFB\u52A0\u56FE\u7247\u6216\u6587\u4EF6\uFF08\u4E5F\u53EF\u4EE5\u76F4\u63A5\u7C98\u8D34\u6216\u62D6\u8FDB\u6765\uFF09";
|
|
9391
|
+
addButton.appendChild(plusIcon());
|
|
8776
9392
|
var askSpacer = el("span", "dsh-sel-askspace");
|
|
9393
|
+
askTools.appendChild(addButton);
|
|
8777
9394
|
askTools.appendChild(webMode);
|
|
8778
9395
|
askTools.appendChild(askSpacer);
|
|
8779
9396
|
askTools.appendChild(modelPill);
|
|
@@ -8837,6 +9454,14 @@ function apply(ctx) {
|
|
|
8837
9454
|
panel.appendChild(head);
|
|
8838
9455
|
panel.appendChild(body);
|
|
8839
9456
|
panel.appendChild(askRow);
|
|
9457
|
+
var dropMask = el("div", "dsh-sel-drop");
|
|
9458
|
+
dropMask.setAttribute("aria-hidden", "true");
|
|
9459
|
+
var dropArt = el("div", "dsh-sel-drop-art");
|
|
9460
|
+
dropArt.appendChild(plusIcon());
|
|
9461
|
+
dropMask.appendChild(dropArt);
|
|
9462
|
+
dropMask.appendChild(el("div", "dsh-sel-drop-title", "\u677E\u5F00\u5373\u6DFB\u52A0\u4E3A\u8FD9\u6761\u8FFD\u95EE\u7684\u9644\u4EF6"));
|
|
9463
|
+
dropMask.appendChild(el("div", "dsh-sel-drop-hint", "\u56FE\u7247\u76F4\u63A5\u7ED9\u6A21\u578B\u770B \xB7 \u6587\u4EF6\u5B58\u6210\u53EA\u8BFB\u8DEF\u5F84\u6309\u9700\u8BFB\u53D6"));
|
|
9464
|
+
panel.appendChild(dropMask);
|
|
8840
9465
|
layer.appendChild(panel);
|
|
8841
9466
|
var settingsController = createSettingsController({
|
|
8842
9467
|
get state() {
|
|
@@ -9125,6 +9750,9 @@ function apply(ctx) {
|
|
|
9125
9750
|
get clearQuotes() {
|
|
9126
9751
|
return quotesController.clearQuotes;
|
|
9127
9752
|
},
|
|
9753
|
+
get clearAttachments() {
|
|
9754
|
+
return attachController.clear;
|
|
9755
|
+
},
|
|
9128
9756
|
get renderTurns() {
|
|
9129
9757
|
return historyController.renderTurns;
|
|
9130
9758
|
},
|
|
@@ -9518,6 +10146,18 @@ function apply(ctx) {
|
|
|
9518
10146
|
get composeQuestion() {
|
|
9519
10147
|
return quotesController.composeQuestion;
|
|
9520
10148
|
},
|
|
10149
|
+
get attachmentsPayload() {
|
|
10150
|
+
return attachController.payload;
|
|
10151
|
+
},
|
|
10152
|
+
get attachmentsBusy() {
|
|
10153
|
+
return attachController.busy;
|
|
10154
|
+
},
|
|
10155
|
+
get attachmentsDescribe() {
|
|
10156
|
+
return attachController.describe;
|
|
10157
|
+
},
|
|
10158
|
+
get clearAttachments() {
|
|
10159
|
+
return attachController.clear;
|
|
10160
|
+
},
|
|
9521
10161
|
get seedHistoryFromExplanation() {
|
|
9522
10162
|
return historyController.seedHistoryFromExplanation;
|
|
9523
10163
|
},
|
|
@@ -9630,6 +10270,29 @@ function apply(ctx) {
|
|
|
9630
10270
|
return panelController.buildPayload;
|
|
9631
10271
|
}
|
|
9632
10272
|
});
|
|
10273
|
+
var attachController = createAttachController({
|
|
10274
|
+
get state() {
|
|
10275
|
+
return state;
|
|
10276
|
+
},
|
|
10277
|
+
get panel() {
|
|
10278
|
+
return panel;
|
|
10279
|
+
},
|
|
10280
|
+
get attachBox() {
|
|
10281
|
+
return attachBox;
|
|
10282
|
+
},
|
|
10283
|
+
get attachNote() {
|
|
10284
|
+
return attachNote;
|
|
10285
|
+
},
|
|
10286
|
+
get attachInput() {
|
|
10287
|
+
return attachInput;
|
|
10288
|
+
},
|
|
10289
|
+
get setStatus() {
|
|
10290
|
+
return panelController.setStatus;
|
|
10291
|
+
},
|
|
10292
|
+
get refreshAskState() {
|
|
10293
|
+
return conversationController.refreshAskState;
|
|
10294
|
+
}
|
|
10295
|
+
});
|
|
9633
10296
|
var modelsController = createModelsController({
|
|
9634
10297
|
get state() {
|
|
9635
10298
|
return state;
|
|
@@ -10162,7 +10825,7 @@ function apply(ctx) {
|
|
|
10162
10825
|
event.preventDefault();
|
|
10163
10826
|
event.stopPropagation();
|
|
10164
10827
|
var question = askBox.value;
|
|
10165
|
-
if (!question.trim() && state.quotes.length === 0) return;
|
|
10828
|
+
if (!question.trim() && state.quotes.length === 0 && state.attachments.length === 0) return;
|
|
10166
10829
|
conversationController.ask(question);
|
|
10167
10830
|
});
|
|
10168
10831
|
var offAskInput = listen(askBox, "input", function() {
|
|
@@ -10175,9 +10838,21 @@ function apply(ctx) {
|
|
|
10175
10838
|
return;
|
|
10176
10839
|
}
|
|
10177
10840
|
var question = askBox.value;
|
|
10178
|
-
if (!question.trim() && state.quotes.length === 0) return;
|
|
10841
|
+
if (!question.trim() && state.quotes.length === 0 && state.attachments.length === 0) return;
|
|
10179
10842
|
conversationController.ask(question);
|
|
10180
10843
|
});
|
|
10844
|
+
var offAttachPick = listen(attachInput, "change", function() {
|
|
10845
|
+
attachController.addFiles(attachInput.files || []);
|
|
10846
|
+
attachInput.value = "";
|
|
10847
|
+
});
|
|
10848
|
+
var offAttachButton = listen(addButton, "click", function(event) {
|
|
10849
|
+
event.stopPropagation();
|
|
10850
|
+
attachController.pick();
|
|
10851
|
+
});
|
|
10852
|
+
var offAttachPaste = listen(panel, "paste", function(event) {
|
|
10853
|
+
attachController.handlePaste(event);
|
|
10854
|
+
});
|
|
10855
|
+
attachController.wireDrop();
|
|
10181
10856
|
var offMic = listen(micButton, "click", function(event) {
|
|
10182
10857
|
event.stopPropagation();
|
|
10183
10858
|
voiceController.startVoice();
|
|
@@ -10773,6 +11448,9 @@ function apply(ctx) {
|
|
|
10773
11448
|
offAskKey();
|
|
10774
11449
|
offAskInput();
|
|
10775
11450
|
offAskSend();
|
|
11451
|
+
offAttachPick();
|
|
11452
|
+
offAttachButton();
|
|
11453
|
+
offAttachPaste();
|
|
10776
11454
|
offMic();
|
|
10777
11455
|
offVoiceCancel();
|
|
10778
11456
|
offVoiceStop();
|