@lynn123411/dsh-chat-translate 3.0.0 → 3.1.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 +7 -7
- package/docs//351/205/215/347/275/256.md +5 -5
- package/lib/client.js +274 -174
- package/lib/client.js.map +3 -3
- package/lib/index.js +369 -335
- package/lib/index.js.map +4 -4
- package/lib/types/client/chat/assistant-step.d.ts +4 -3
- package/lib/types/client/chat/row-plan.d.ts +25 -8
- package/lib/types/client/chat/styles.d.ts +15 -6
- package/lib/types/client/chat/translate-store.d.ts +16 -16
- package/lib/types/client/locales.d.ts +12 -2
- package/lib/types/client/translate/api.d.ts +1 -8
- package/lib/types/server/cache.d.ts +1 -1
- package/lib/types/server/dispatcher.d.ts +30 -26
- package/lib/types/server/pipeline/blocks.d.ts +7 -11
- package/lib/types/server/pipeline/segments.d.ts +68 -0
- package/lib/types/server/prompt-revision.d.ts +1 -1
- package/lib/types/server/types.d.ts +15 -13
- package/package.json +10 -10
- package/lib/types/server/pipeline/mask-tokens.d.ts +0 -64
- package/lib/types/server/pipeline/masking.d.ts +0 -45
package/lib/client.js
CHANGED
|
@@ -85,8 +85,7 @@ async function requestTranslateReply(blocks) {
|
|
|
85
85
|
original: block,
|
|
86
86
|
translated: block,
|
|
87
87
|
ok: false,
|
|
88
|
-
cached: false
|
|
89
|
-
channel: "fallback-client"
|
|
88
|
+
cached: false
|
|
90
89
|
}));
|
|
91
90
|
if (!Array.isArray(blocks) || blocks.length === 0) return [];
|
|
92
91
|
try {
|
|
@@ -545,7 +544,7 @@ var zh = {
|
|
|
545
544
|
title: "\u6B63\u6587\u7FFB\u8BD1",
|
|
546
545
|
masterTitle: "\u7FFB\u8BD1\u603B\u5F00\u5173",
|
|
547
546
|
enableTranslation: "\u542F\u7528\u7FFB\u8BD1",
|
|
548
|
-
masterDesc: "\u81EA\u52A8\u628A\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u7FFB\u6210\u4E2D\u6587\uFF08\u5982 {example}\uFF09\uFF1A\u6298\u53E0\u5757\u91CC\u7684\u8FC7\u7A0B\u6B63\u6587\u4E0E\u6700\u7EC8\u6C47\u603B\u4E00\u89C6\u540C\u4EC1\uFF0C\u6574\u6BB5\u4E2D\u6587\u7684\u5757\u4E5F\u7167\u9001\uFF0C\u7531\u6A21\u578B\u4E00\u5E76\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\u3001\u987A\u6389\u673A\u5668\u8154\u3002Think \u5361\u4E0E\u5DE5\u5177\u8C03\u7528\u884C\u4E0D\u7FFB\uFF0C\u4EE3\u7801\u5757\u539F\u6837\u4FDD\u7559\u3002\
|
|
547
|
+
masterDesc: "\u81EA\u52A8\u628A\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u7FFB\u6210\u4E2D\u6587\uFF08\u5982 {example}\uFF09\uFF1A\u6298\u53E0\u5757\u91CC\u7684\u8FC7\u7A0B\u6B63\u6587\u4E0E\u6700\u7EC8\u6C47\u603B\u4E00\u89C6\u540C\u4EC1\uFF0C\u6574\u6BB5\u4E2D\u6587\u7684\u5757\u4E5F\u7167\u9001\uFF0C\u7531\u6A21\u578B\u4E00\u5E76\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\u3001\u987A\u6389\u673A\u5668\u8154\u3002Think \u5361\u4E0E\u5DE5\u5177\u8C03\u7528\u884C\u4E0D\u7FFB\uFF0C\u4EE3\u7801\u5757\u539F\u6837\u4FDD\u7559\u3002\u6B63\u6587\u5757\u7684\u5DE6\u7F18\u4E00\u6761\u7AD6\u7EBF\u62A5\u51FA\u72B6\u6001\uFF1A\u6B63\u5728\u8BFB\u8BD1\u6587\u662F\u84DD\u7EBF\uFF0C\u5907\u7740\u8BD1\u6587\u6B63\u8BFB\u539F\u6587\u662F\u7070\u7EC6\u7EBF\uFF0C\u6CA1\u8BD1\u6210\u662F\u7EA2\u5B9E\u7EBF\uFF08\u9F20\u6807\u60AC\u505C\u62A5\u51FA\u901A\u9053\u7EC6\u8282\u2014\u2014\u8D85\u65F6\u3001\u65AD\u6D41\u6216\u7A7A\u8FD4\u56DE\uFF1B\u8BD1\u6587\u7684\u7ED3\u6784\u6F02\u79FB\u7531\u5BBF\u4E3B\u81EA\u52A8\u4FEE\u56DE\u6216\u91CD\u63B7\uFF0C\u4E0D\u518D\u56E0\u683C\u5F0F\u95EE\u9898\u6302\u7EA2\u7EBF\uFF09\uFF0C\u6B63\u5728\u8BF7\u6C42\u662F\u7070\u8109\u52A8\u7EBF\uFF0C\u6CA1\u9001\u8FC7\u6A21\u578B\u6216\u5F00\u5173\u5173\u95ED\u6CA1\u6709\u7EBF\u3002\u70B9\u51FB\u5DF2\u7FFB\u8BD1\u7684\u5757\u5728\u8BD1\u6587\u4E0E\u539F\u6587\u95F4\u5207\u6362\u2014\u2014\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u3001\u8FD4\u56DE\u4E00\u6A21\u4E00\u6837\u7684\u6587\u5B57\u65F6\u540C\u6837\u7B97\u7FFB\u8BD1\u8FC7\u3001\u540C\u6837\u53EF\u5207\uFF1B\u70B9\u51FB\u6302\u7EA2\u7EBF\u7684\u5757\u5219\u6574\u884C\u91CD\u65B0\u7FFB\u8BD1\uFF0C\u5DF2\u6210\u529F\u7684\u5757\u4FDD\u6301\u663E\u793A\u4E0D\u6253\u6270\u3002\u901A\u9053\u5931\u8D25\u4E0D\u81EA\u52A8\u91CD\u8BD5\uFF0C\u6062\u590D\u540E\u7531\u4F60\u7684\u70B9\u51FB\u6551\u6D3B\u3002\u8BD1\u6587\u6309\u539F markdown \u683C\u5F0F\u91CD\u65B0\u6E32\u67D3\uFF0C\u8868\u683C\u3001\u5217\u8868\u3001\u52A0\u7C97\u4E0E\u884C\u5185\u4EE3\u7801\u4FDD\u6301\u7ED3\u6784\u3002\u56DE\u7B54\u8FD8\u5728\u6D41\u5F0F\u8F93\u51FA\u65F6\u4E0D\u7FFB\uFF0C\u843D\u5B9A\u540E\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u6BB5\u51FA\u4E2D\u6587\u3002\u53EA\u4F5C\u7528\u4E8E\u5F53\u524D\u67E5\u770B\u7684\u4F1A\u8BDD\uFF0C\u8BD1\u6587\u4E0D\u5199\u56DE\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\u6709\u540C\u6B3E\u5F00\u5173\uFF0C\u4E24\u5904\u72B6\u6001\u540C\u6B65\u3002",
|
|
549
548
|
aiTitle: "AI \u7FFB\u8BD1\uFF08OpenAI \u517C\u5BB9\u534F\u8BAE\uFF09",
|
|
550
549
|
aiRule: "\u53EA\u6709\u8FD9\u4E00\u6761\u901A\u9053\uFF1AKey / Base URL / \u6A21\u578B\u9F50\u5907\u624D\u4F1A\u7FFB\u8BD1\uFF0C\u7F3A\u4EFB\u4F55\u4E00\u9879\u90FD\u4FDD\u6301\u539F\u6587\u3002",
|
|
551
550
|
badgeConfigured: "\u5DF2\u914D\u7F6E",
|
|
@@ -570,17 +569,19 @@ var zh = {
|
|
|
570
569
|
testOk: "\u8FDE\u63A5\u6B63\u5E38\uFF0C\u5EF6\u8FDF {latency}ms",
|
|
571
570
|
testFail: "\u5931\u8D25\uFF1A{error}",
|
|
572
571
|
behaviorTitle: "\u7FFB\u8BD1\u8303\u56F4",
|
|
573
|
-
behaviorRule: "\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u4E00\u5F8B\u7FFB\u8BD1\uFF0C\u5305\u62EC\u6700\u7EC8\u6C47\u603B\u4E0E\u6574\u6BB5\u4E2D\u6587\u7684\u5757\uFF1A\u4E2D\u6587\u5757\u540C\u6837\u4EA4\u7ED9\u6A21\u578B\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\uFF0C\u610F\u4E49\u3001\u672F\u8BED\u4E0E\u6570\u5B57\u4E0D\u53D8\u3002\u601D\u8003\u94FE\u6B63\u6587\u3001\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E0E\u6298\u53E0\u6458\u8981\u4E0D\u7FFB\u8BD1\uFF0C\u4EE3\u7801\u5757\u53CA\u5176\u5185\u90E8\u539F\u6837\u4FDD\u7559\u3002",
|
|
572
|
+
behaviorRule: "\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u4E00\u5F8B\u7FFB\u8BD1\uFF0C\u5305\u62EC\u6700\u7EC8\u6C47\u603B\u4E0E\u6574\u6BB5\u4E2D\u6587\u7684\u5757\uFF1A\u4E2D\u6587\u5757\u540C\u6837\u4EA4\u7ED9\u6A21\u578B\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\uFF0C\u610F\u4E49\u3001\u672F\u8BED\u4E0E\u6570\u5B57\u4E0D\u53D8\uFF1B\u7A7A\u767D\u5757\u4E0D\u9001\u8BD1\u3001\u4E0D\u6302\u7EBF\u3002\u601D\u8003\u94FE\u6B63\u6587\u3001\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E0E\u6298\u53E0\u6458\u8981\u4E0D\u7FFB\u8BD1\uFF0C\u4EE3\u7801\u5757\u53CA\u5176\u5185\u90E8\u539F\u6837\u4FDD\u7559\u3002",
|
|
574
573
|
dockLabel: "\u8BD1",
|
|
575
574
|
dockOn: "\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5F00\u542F\uFF0C\u70B9\u51FB\u5173\u95ED",
|
|
576
|
-
dockOff: "\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F"
|
|
575
|
+
dockOff: "\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F",
|
|
576
|
+
failTitle: "\u7FFB\u8BD1\u5931\u8D25",
|
|
577
|
+
retryAria: "\u7FFB\u8BD1\u5931\u8D25\uFF0C\u6309 Enter \u91CD\u8BD5"
|
|
577
578
|
};
|
|
578
579
|
var en = {
|
|
579
580
|
pageNav: "Reading",
|
|
580
581
|
title: "Reply translate",
|
|
581
582
|
masterTitle: "Translation master switch",
|
|
582
583
|
enableTranslation: "Enable translation",
|
|
583
|
-
masterDesc: "
|
|
584
|
+
masterDesc: "Automatically translates assistant reply prose (e.g. {example}) into Chinese: text inside disclosure blocks and the final summary are treated alike, and blocks written entirely in Chinese go through the model too, which rewrites them into natural Chinese and smooths out machine-flavored phrasing. Think cards and tool-call rows are never translated, and code blocks stay untouched. Each prose block reports its state with a left-edge line: blue while its translation reads, thin grey while the original reads with a translation waiting one click away, solid red when translation failed (hover the block to read the channel detail \u2014 a timeout, a broken stream or an empty return; the answer's Markdown shape drift is repaired or re-rolled by the host, so formatting no longer draws a red line), a grey pulse while a request is in flight, and nothing at all when the block was never sent or the switch is off. Click a translated block to switch between translation and original \u2014 even when the model returns the wording unchanged it counts as translated and stays switchable; click a red-lined block to re-run the whole row, with already-successful blocks kept on screen undisturbed. Channel failures are never retried automatically: once the channel recovers, your click revives the row. Translations are re-rendered as Markdown, so tables, lists, bold runs and inline code keep their structure. A reply that is still streaming is left alone until it settles, then prose turns to Chinese paragraph by paragraph in reading order. Applies to the session you are viewing only; translations never enter the conversation context. The same switch sits on the row below the composer, sharing its state.",
|
|
584
585
|
aiTitle: "AI translation (OpenAI-compatible)",
|
|
585
586
|
aiRule: "This is the only channel: translation runs when Key, Base URL and model are all present; missing any one leaves the text as it is.",
|
|
586
587
|
badgeConfigured: "Configured",
|
|
@@ -605,11 +606,29 @@ var en = {
|
|
|
605
606
|
testOk: "Connected, {latency}ms latency",
|
|
606
607
|
testFail: "Failed: {error}",
|
|
607
608
|
behaviorTitle: "What gets translated",
|
|
608
|
-
behaviorRule: "All assistant reply prose, the final summary and Chinese-only blocks included: Chinese blocks are also handed to the model, which rewrites them into natural Chinese while keeping meaning, terms and numbers unchanged. Think-chain text, tool-call titles and folded summaries are never translated; code blocks and their contents stay untouched.",
|
|
609
|
+
behaviorRule: "All assistant reply prose, the final summary and Chinese-only blocks included: Chinese blocks are also handed to the model, which rewrites them into natural Chinese while keeping meaning, terms and numbers unchanged; blank blocks are never sent and never marked. Think-chain text, tool-call titles and folded summaries are never translated; code blocks and their contents stay untouched.",
|
|
609
610
|
dockLabel: "Translate",
|
|
610
611
|
dockOn: "Translation is on; click to turn it off",
|
|
611
|
-
dockOff: "Translation is off; click to turn it on"
|
|
612
|
+
dockOff: "Translation is off; click to turn it on",
|
|
613
|
+
failTitle: "Translation failed",
|
|
614
|
+
retryAria: "Translation failed \u2014 press Enter to retry"
|
|
612
615
|
};
|
|
616
|
+
var rowT = (key) => zh[key] ?? key;
|
|
617
|
+
function bindRowCopy(locale) {
|
|
618
|
+
if (locale && typeof locale.bind === "function") {
|
|
619
|
+
const bound = locale.bind(NS);
|
|
620
|
+
if (typeof bound === "function") rowT = bound;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function rowCopy() {
|
|
624
|
+
return {
|
|
625
|
+
retryAria: rowT("retryAria"),
|
|
626
|
+
failTitle: (detail) => {
|
|
627
|
+
const label = rowT("failTitle");
|
|
628
|
+
return detail === void 0 || detail === "" || detail === null ? label : `${label} \u2014 ${detail}`;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
613
632
|
|
|
614
633
|
// src/client/settings/ui.tsx
|
|
615
634
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -906,6 +925,185 @@ var React = __toESM(require("react"), 1);
|
|
|
906
925
|
var import_react4 = require("react");
|
|
907
926
|
var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
908
927
|
|
|
928
|
+
// src/client/chat/row-plan.ts
|
|
929
|
+
function isBlankText(value) {
|
|
930
|
+
return (value ?? "").trim() === "";
|
|
931
|
+
}
|
|
932
|
+
function planAssistantRow(input) {
|
|
933
|
+
const { blocks, streaming, interrupted, groupPart, canTranslate, outcomes, rowStatus, originalKeys } = input;
|
|
934
|
+
const hasVisible = streaming || interrupted || blocks.some((block) => block.kind !== "tool-call");
|
|
935
|
+
if (!hasVisible) return { entries: null, texts: [] };
|
|
936
|
+
const texts = [];
|
|
937
|
+
if (groupPart !== "reasoning") {
|
|
938
|
+
for (const block of blocks) {
|
|
939
|
+
if (block.kind === "text" && !isBlankText(block.text)) texts.push(block.text ?? "");
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const entries = [];
|
|
943
|
+
let textIndex = 0;
|
|
944
|
+
let imageRun = null;
|
|
945
|
+
const flushImages = () => {
|
|
946
|
+
if (imageRun !== null) {
|
|
947
|
+
entries.push({ type: "images", key: entries.length, attachments: imageRun });
|
|
948
|
+
imageRun = null;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
952
|
+
const block = blocks[i];
|
|
953
|
+
if (block === void 0) continue;
|
|
954
|
+
if (groupPart === "reasoning" && block.kind !== "reasoning") continue;
|
|
955
|
+
if (groupPart === "response" && block.kind === "reasoning") continue;
|
|
956
|
+
switch (block.kind) {
|
|
957
|
+
case "text": {
|
|
958
|
+
flushImages();
|
|
959
|
+
const blank = isBlankText(block.text);
|
|
960
|
+
const index = textIndex;
|
|
961
|
+
if (!blank) textIndex += 1;
|
|
962
|
+
const key = entries.length;
|
|
963
|
+
const attemptable = !blank && canTranslate && !streaming;
|
|
964
|
+
const outcome = attemptable && outcomes !== void 0 ? outcomes[index] ?? null : null;
|
|
965
|
+
let mark = null;
|
|
966
|
+
let fail = null;
|
|
967
|
+
if (outcome !== null && outcome.ok) {
|
|
968
|
+
mark = originalKeys?.has(key) ? "original-view" : "translated";
|
|
969
|
+
} else if (outcome !== null) {
|
|
970
|
+
mark = "failed";
|
|
971
|
+
fail = outcome.detail ?? null;
|
|
972
|
+
} else if (attemptable && rowStatus === "pending") {
|
|
973
|
+
mark = "inflight";
|
|
974
|
+
}
|
|
975
|
+
entries.push({
|
|
976
|
+
type: "prose",
|
|
977
|
+
key,
|
|
978
|
+
text: block.text ?? "",
|
|
979
|
+
translated: outcome !== null && outcome.ok ? outcome.translated : null,
|
|
980
|
+
mark,
|
|
981
|
+
fail
|
|
982
|
+
});
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
case "reasoning":
|
|
986
|
+
flushImages();
|
|
987
|
+
entries.push({ type: "reasoning", key: entries.length, blockIndex: i, text: block.text ?? "" });
|
|
988
|
+
break;
|
|
989
|
+
case "image":
|
|
990
|
+
if (imageRun === null) imageRun = [];
|
|
991
|
+
imageRun.push(block.attachment);
|
|
992
|
+
break;
|
|
993
|
+
case "tool-call":
|
|
994
|
+
flushImages();
|
|
995
|
+
break;
|
|
996
|
+
default:
|
|
997
|
+
flushImages();
|
|
998
|
+
entries.push({ type: "unknown", key: entries.length, block: block.block ?? null });
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
flushImages();
|
|
1002
|
+
const showStopped = interrupted && (groupPart === void 0 || groupPart === "response" || !blocks.some((block) => block.kind !== "reasoning" && block.kind !== "tool-call"));
|
|
1003
|
+
if (showStopped) entries.push({ type: "stopped", key: entries.length });
|
|
1004
|
+
return { entries, texts };
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/client/chat/click-guard.ts
|
|
1008
|
+
function isBareBlockClick(event, view) {
|
|
1009
|
+
const target = event.target;
|
|
1010
|
+
const interactive = typeof target?.closest === "function" ? target.closest('a,button,[role="button"]') : null;
|
|
1011
|
+
if (interactive !== null && interactive !== void 0 && interactive !== event.currentTarget) return false;
|
|
1012
|
+
const selection = typeof view?.getSelection === "function" ? view.getSelection() : null;
|
|
1013
|
+
if (selection !== null && selection !== void 0) {
|
|
1014
|
+
if (selection.isCollapsed === false && String(selection.toString?.() ?? "") !== "") return false;
|
|
1015
|
+
}
|
|
1016
|
+
return true;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/client/chat/styles.ts
|
|
1020
|
+
var STYLE_TAG_ID = "@lynn123411/dsh-chat-translate/chat-assistant.css";
|
|
1021
|
+
var ASSISTANT_CSS = [
|
|
1022
|
+
// ---- 行骨架(宿主 AssistantMarkdown.root/body 等价) ----
|
|
1023
|
+
".dsh-ct-root{display:flex;flex-direction:column;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary)}",
|
|
1024
|
+
".dsh-ct-body{display:flex;flex-direction:column;gap:16px}",
|
|
1025
|
+
".dsh-ct-body>[data-turn-process-inline][hidden]{margin-bottom:-16px}",
|
|
1026
|
+
// 宽表格溢出内容列(宿主等价规则,类名 md-table-wide 由 MarkdownText 自己产出)
|
|
1027
|
+
".dsh-ct-body .md-table-wide{--dsh-table-spare:max(0px, calc((100cqw - var(--dsh-chat-content-width)) / 2));--dsh-table-lead:calc(var(--dsh-table-spare) + min(var(--dsh-chat-content-width), 100cqw) - 100%);box-sizing:border-box;width:calc(100% + var(--dsh-table-lead) + var(--dsh-table-spare));max-width:none;margin-left:calc(-1 * var(--dsh-table-lead));padding-left:var(--dsh-table-lead)}",
|
|
1028
|
+
".dsh-ct-body .md-table-wide>table{z-index:1;position:relative}",
|
|
1029
|
+
// ---- 已停止标记(宿主 .hWmORq_stopped 等价) ----
|
|
1030
|
+
".dsh-ct-stopped{border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}",
|
|
1031
|
+
// ---- 「已翻译」交互与标记:可点态常驻(含左缘内缩)——蓝粗线=读译文,
|
|
1032
|
+
// 灰细线=读原文备着译文;区分走色相+粗细 ----
|
|
1033
|
+
".dsh-ct-prose-clickable{padding-left:12px;cursor:pointer}",
|
|
1034
|
+
".dsh-ct-prose-clickable:focus-visible{outline:1.5px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px;border-radius:var(--dsw-radius-sm)}",
|
|
1035
|
+
".dsh-ct-prose-translated{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent)}",
|
|
1036
|
+
".dsh-ct-prose-original{border-left:0.5px solid var(--dsw-alias-border-l2)}",
|
|
1037
|
+
// ---- 失败标记:1px error 色红实线(只剩通道伤一种,悬停报细节);
|
|
1038
|
+
// 可点,点=整行手动补跑 ----
|
|
1039
|
+
".dsh-ct-prose-failed{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 65%, transparent)}",
|
|
1040
|
+
// ---- 在途:灰 1px 线脉动(与成功态同 padding,转蓝不跳字);
|
|
1041
|
+
// prefers-reduced-motion 降级为静态灰线 ----
|
|
1042
|
+
".dsh-ct-prose-inflight{padding-left:12px;border-left:1px solid var(--dsw-alias-border-l2);animation:1.4s ease-in-out infinite alternate dsh-ct-prose-inflight-pulse}",
|
|
1043
|
+
"@keyframes dsh-ct-prose-inflight-pulse{from{border-left-color:var(--dsw-alias-border-l2)}to{border-left-color:color-mix(in srgb, var(--dsw-alias-border-l2) 20%, transparent)}}",
|
|
1044
|
+
"@media (prefers-reduced-motion:reduce){.dsh-ct-prose-inflight{animation:none}}",
|
|
1045
|
+
// ---- 失败块的重试指引:悬停/键盘聚焦时在左侧线槽内露出小 ↻(官方图标,
|
|
1046
|
+
// 文案由 Tooltip 挂在该锚点上);静态不扰动阅读 ----
|
|
1047
|
+
".dsh-ct-prose-retryable{position:relative}",
|
|
1048
|
+
".dsh-ct-retry{position:absolute;left:2px;top:6px;display:flex;color:var(--dsw-alias-state-error-primary);opacity:0;visibility:hidden}",
|
|
1049
|
+
".dsh-ct-prose-retryable:hover .dsh-ct-retry,.dsh-ct-prose-retryable:focus-visible .dsh-ct-retry{opacity:1;visibility:visible}",
|
|
1050
|
+
// ---- ReasoningRow(宿主 lcKema_* 等价,前缀换成 dsh-ct-think) ----
|
|
1051
|
+
".dsh-ct-think-root{flex-direction:column;display:flex}",
|
|
1052
|
+
".dsh-ct-think-root:not([data-expanded]){contain:size layout;height:calc(24px + var(--dsh-content-font-delta,0px))}",
|
|
1053
|
+
".dsh-ct-think-row{position:relative;overflow:hidden}",
|
|
1054
|
+
".dsh-ct-think-root[data-expanded] [data-open] [data-disclosure-row]{z-index:1;background:var(--dsw-alias-bg-base);position:sticky;top:0}",
|
|
1055
|
+
'.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{content:"";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite dsh-ct-reasoning-sweep;position:absolute;left:0}',
|
|
1056
|
+
"@keyframes dsh-ct-reasoning-sweep{0%{left:-300px}90%,to{left:100%}}",
|
|
1057
|
+
"@media (prefers-reduced-motion:reduce){.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{animation:none}}",
|
|
1058
|
+
".dsh-ct-think-leading{flex-shrink:0}",
|
|
1059
|
+
".dsh-ct-think-chevron{color:var(--dsw-alias-label-secondary)}",
|
|
1060
|
+
".dsh-ct-think-title{font-weight:400}",
|
|
1061
|
+
".dsh-ct-think-separator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}",
|
|
1062
|
+
".dsh-ct-think-summary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));white-space:nowrap;flex:auto;overflow:hidden}",
|
|
1063
|
+
".dsh-ct-think-summary-text{text-overflow:ellipsis;display:block;overflow:hidden}",
|
|
1064
|
+
".dsh-ct-think-summary[data-streaming]{mask-image:linear-gradient(90deg,currentcolor calc(100% - 48px),transparent)}",
|
|
1065
|
+
".dsh-ct-think-summary[data-streaming] .dsh-ct-think-summary-text{text-overflow:clip;overflow:visible}",
|
|
1066
|
+
".dsh-ct-think-root:not([data-preview]) .dsh-ct-think-separator,.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-summary{display:none}",
|
|
1067
|
+
".dsh-ct-think-body{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));min-width:0}",
|
|
1068
|
+
// ---- 无障碍:屏幕阅读器专用文本 ----
|
|
1069
|
+
".dsh-ct-visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}"
|
|
1070
|
+
].join("");
|
|
1071
|
+
function proseAction(mark) {
|
|
1072
|
+
switch (mark) {
|
|
1073
|
+
case "translated":
|
|
1074
|
+
case "original-view":
|
|
1075
|
+
return "toggle";
|
|
1076
|
+
case "failed":
|
|
1077
|
+
return "retry";
|
|
1078
|
+
default:
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function proseClassNames(mark) {
|
|
1083
|
+
switch (mark) {
|
|
1084
|
+
case "translated":
|
|
1085
|
+
return "dsh-ct-prose-clickable dsh-ct-prose-translated";
|
|
1086
|
+
case "original-view":
|
|
1087
|
+
return "dsh-ct-prose-clickable dsh-ct-prose-original";
|
|
1088
|
+
case "failed":
|
|
1089
|
+
return "dsh-ct-prose-clickable dsh-ct-prose-failed dsh-ct-prose-retryable";
|
|
1090
|
+
case "inflight":
|
|
1091
|
+
return "dsh-ct-prose-inflight";
|
|
1092
|
+
default:
|
|
1093
|
+
return void 0;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function ensureAssistantStyles() {
|
|
1097
|
+
if (typeof document === "undefined") return;
|
|
1098
|
+
const selector = `style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`;
|
|
1099
|
+
if (document.querySelector(selector) !== null) return;
|
|
1100
|
+
const tag = document.createElement("style");
|
|
1101
|
+
tag.dataset.plugin = "@lynn123411/dsh-chat-translate";
|
|
1102
|
+
tag.dataset.pluginCss = STYLE_TAG_ID;
|
|
1103
|
+
tag.textContent = ASSISTANT_CSS;
|
|
1104
|
+
document.head.appendChild(tag);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
909
1107
|
// src/server/pipeline/blocks.ts
|
|
910
1108
|
var REPLY_MAX_INPUT_TOKENS = 4096;
|
|
911
1109
|
var CJK_CHAR = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\u3000-\u303f\uff00-\uffef]/;
|
|
@@ -919,7 +1117,6 @@ function estimateTokens(text) {
|
|
|
919
1117
|
|
|
920
1118
|
// src/client/chat/translate-store.ts
|
|
921
1119
|
var MAX_TRANSLATED_ROWS = 200;
|
|
922
|
-
var MAX_ROW_ATTEMPTS = 3;
|
|
923
1120
|
var REQUEST_TOKEN_BUDGET = REPLY_MAX_INPUT_TOKENS;
|
|
924
1121
|
function chunkTexts(texts) {
|
|
925
1122
|
const batches = [];
|
|
@@ -966,32 +1163,33 @@ var ChatTranslateStore = class {
|
|
|
966
1163
|
}
|
|
967
1164
|
/**
|
|
968
1165
|
* 确保该行按当前文本被翻译。
|
|
969
|
-
* @
|
|
1166
|
+
* @param manual - 用户点击失败块发起的补跑:已落定的同代行只有这条路会重发。
|
|
1167
|
+
* @returns 是否发出了新的请求。
|
|
970
1168
|
*/
|
|
971
|
-
ensure(rowKey, texts) {
|
|
1169
|
+
ensure(rowKey, texts, manual = false) {
|
|
972
1170
|
const current = this.rows.get(rowKey);
|
|
973
|
-
let
|
|
1171
|
+
let seed = null;
|
|
974
1172
|
if (current !== void 0 && sameTexts(current.texts, texts)) {
|
|
975
|
-
if (current.status
|
|
976
|
-
if (
|
|
977
|
-
|
|
1173
|
+
if (current.status === "pending") return false;
|
|
1174
|
+
if (!manual) return false;
|
|
1175
|
+
seed = current.outcomes.map((outcome) => outcome !== null && outcome.ok ? { ...outcome } : null);
|
|
978
1176
|
}
|
|
979
1177
|
const snapshot = [...texts];
|
|
1178
|
+
const seedOutcomes = seed ?? snapshot.map(() => null);
|
|
980
1179
|
const next = {
|
|
981
1180
|
status: "pending",
|
|
982
1181
|
texts: snapshot,
|
|
983
|
-
outcomes:
|
|
984
|
-
attempts
|
|
1182
|
+
outcomes: seedOutcomes
|
|
985
1183
|
};
|
|
986
1184
|
this.rows.delete(rowKey);
|
|
987
1185
|
this.rows.set(rowKey, next);
|
|
988
1186
|
this.prune();
|
|
989
1187
|
this.bump();
|
|
990
|
-
if (snapshot.length > 0) void this.run(rowKey, snapshot);
|
|
1188
|
+
if (snapshot.length > 0) void this.run(rowKey, snapshot, seedOutcomes);
|
|
991
1189
|
return true;
|
|
992
1190
|
}
|
|
993
|
-
async run(rowKey, texts) {
|
|
994
|
-
const outcomes =
|
|
1191
|
+
async run(rowKey, texts, initial) {
|
|
1192
|
+
const outcomes = initial.map((o) => o);
|
|
995
1193
|
let offset = 0;
|
|
996
1194
|
for (const batch of chunkTexts(texts)) {
|
|
997
1195
|
let results = [];
|
|
@@ -1005,7 +1203,12 @@ var ChatTranslateStore = class {
|
|
|
1005
1203
|
if (result !== void 0 && result.ok && result.translated && result.translated.trim()) {
|
|
1006
1204
|
outcomes[offset + i] = { translated: result.translated, ok: true };
|
|
1007
1205
|
} else {
|
|
1008
|
-
|
|
1206
|
+
const detail = typeof result?.detail === "string" && result.detail !== "" ? result.detail : void 0;
|
|
1207
|
+
outcomes[offset + i] = {
|
|
1208
|
+
translated: batch[i] ?? texts[offset + i] ?? "",
|
|
1209
|
+
ok: false,
|
|
1210
|
+
...detail === void 0 ? {} : { detail }
|
|
1211
|
+
};
|
|
1009
1212
|
}
|
|
1010
1213
|
}
|
|
1011
1214
|
offset += batch.length;
|
|
@@ -1014,19 +1217,16 @@ var ChatTranslateStore = class {
|
|
|
1014
1217
|
this.rows.set(rowKey, {
|
|
1015
1218
|
status: "pending",
|
|
1016
1219
|
texts: live.texts,
|
|
1017
|
-
outcomes: [...outcomes]
|
|
1018
|
-
attempts: live.attempts
|
|
1220
|
+
outcomes: [...outcomes]
|
|
1019
1221
|
});
|
|
1020
1222
|
this.bump();
|
|
1021
1223
|
}
|
|
1022
1224
|
const state = this.rows.get(rowKey);
|
|
1023
1225
|
if (state?.status === "pending" && sameTexts(state.texts, texts)) {
|
|
1024
|
-
const anyFailed = outcomes.some((outcome) => outcome === null || !outcome.ok);
|
|
1025
1226
|
this.rows.set(rowKey, {
|
|
1026
|
-
status:
|
|
1227
|
+
status: "settled",
|
|
1027
1228
|
texts: state.texts,
|
|
1028
|
-
outcomes
|
|
1029
|
-
attempts: state.attempts
|
|
1229
|
+
outcomes: [...outcomes]
|
|
1030
1230
|
});
|
|
1031
1231
|
this.bump();
|
|
1032
1232
|
}
|
|
@@ -1061,133 +1261,6 @@ function createTranslateStore(fetcher) {
|
|
|
1061
1261
|
}
|
|
1062
1262
|
var chatTranslate = createTranslateStore(requestTranslateReply);
|
|
1063
1263
|
|
|
1064
|
-
// src/client/chat/row-plan.ts
|
|
1065
|
-
function planAssistantRow(input) {
|
|
1066
|
-
const { blocks, streaming, interrupted, groupPart, canTranslate, outcomes } = input;
|
|
1067
|
-
const hasVisible = streaming || interrupted || blocks.some((block) => block.kind !== "tool-call");
|
|
1068
|
-
if (!hasVisible) return { entries: null, texts: [] };
|
|
1069
|
-
const texts = [];
|
|
1070
|
-
if (groupPart !== "reasoning") {
|
|
1071
|
-
for (const block of blocks) {
|
|
1072
|
-
if (block.kind === "text") texts.push(block.text ?? "");
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
const entries = [];
|
|
1076
|
-
let textIndex = 0;
|
|
1077
|
-
let imageRun = null;
|
|
1078
|
-
const flushImages = () => {
|
|
1079
|
-
if (imageRun !== null) {
|
|
1080
|
-
entries.push({ type: "images", key: entries.length, attachments: imageRun });
|
|
1081
|
-
imageRun = null;
|
|
1082
|
-
}
|
|
1083
|
-
};
|
|
1084
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
1085
|
-
const block = blocks[i];
|
|
1086
|
-
if (block === void 0) continue;
|
|
1087
|
-
if (groupPart === "reasoning" && block.kind !== "reasoning") continue;
|
|
1088
|
-
if (groupPart === "response" && block.kind === "reasoning") continue;
|
|
1089
|
-
switch (block.kind) {
|
|
1090
|
-
case "text": {
|
|
1091
|
-
flushImages();
|
|
1092
|
-
const index = textIndex;
|
|
1093
|
-
textIndex += 1;
|
|
1094
|
-
const outcome = canTranslate && !streaming && outcomes !== void 0 ? outcomes[index] : null;
|
|
1095
|
-
entries.push({
|
|
1096
|
-
type: "prose",
|
|
1097
|
-
key: entries.length,
|
|
1098
|
-
text: block.text ?? "",
|
|
1099
|
-
translated: outcome !== null && outcome !== void 0 && outcome.ok ? outcome.translated : null
|
|
1100
|
-
});
|
|
1101
|
-
break;
|
|
1102
|
-
}
|
|
1103
|
-
case "reasoning":
|
|
1104
|
-
flushImages();
|
|
1105
|
-
entries.push({ type: "reasoning", key: entries.length, blockIndex: i, text: block.text ?? "" });
|
|
1106
|
-
break;
|
|
1107
|
-
case "image":
|
|
1108
|
-
if (imageRun === null) imageRun = [];
|
|
1109
|
-
imageRun.push(block.attachment);
|
|
1110
|
-
break;
|
|
1111
|
-
case "tool-call":
|
|
1112
|
-
flushImages();
|
|
1113
|
-
break;
|
|
1114
|
-
default:
|
|
1115
|
-
flushImages();
|
|
1116
|
-
entries.push({ type: "unknown", key: entries.length, block: block.block ?? null });
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
flushImages();
|
|
1120
|
-
const showStopped = interrupted && (groupPart === void 0 || groupPart === "response" || !blocks.some((block) => block.kind !== "reasoning" && block.kind !== "tool-call"));
|
|
1121
|
-
if (showStopped) entries.push({ type: "stopped", key: entries.length });
|
|
1122
|
-
return { entries, texts };
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// src/client/chat/click-guard.ts
|
|
1126
|
-
function isBareBlockClick(event, view) {
|
|
1127
|
-
const target = event.target;
|
|
1128
|
-
const interactive = typeof target?.closest === "function" ? target.closest('a,button,[role="button"]') : null;
|
|
1129
|
-
if (interactive !== null && interactive !== void 0 && interactive !== event.currentTarget) return false;
|
|
1130
|
-
const selection = typeof view?.getSelection === "function" ? view.getSelection() : null;
|
|
1131
|
-
if (selection !== null && selection !== void 0) {
|
|
1132
|
-
if (selection.isCollapsed === false && String(selection.toString?.() ?? "") !== "") return false;
|
|
1133
|
-
}
|
|
1134
|
-
return true;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// src/client/chat/styles.ts
|
|
1138
|
-
var STYLE_TAG_ID = "@lynn123411/dsh-chat-translate/chat-assistant.css";
|
|
1139
|
-
var ASSISTANT_CSS = [
|
|
1140
|
-
// ---- 行骨架(宿主 AssistantMarkdown.root/body 等价) ----
|
|
1141
|
-
".dsh-ct-root{display:flex;flex-direction:column;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary)}",
|
|
1142
|
-
".dsh-ct-body{display:flex;flex-direction:column;gap:16px}",
|
|
1143
|
-
".dsh-ct-body>[data-turn-process-inline][hidden]{margin-bottom:-16px}",
|
|
1144
|
-
// 宽表格溢出内容列(宿主等价规则,类名 md-table-wide 由 MarkdownText 自己产出)
|
|
1145
|
-
".dsh-ct-body .md-table-wide{--dsh-table-spare:max(0px, calc((100cqw - var(--dsh-chat-content-width)) / 2));--dsh-table-lead:calc(var(--dsh-table-spare) + min(var(--dsh-chat-content-width), 100cqw) - 100%);box-sizing:border-box;width:calc(100% + var(--dsh-table-lead) + var(--dsh-table-spare));max-width:none;margin-left:calc(-1 * var(--dsh-table-lead));padding-left:var(--dsh-table-lead)}",
|
|
1146
|
-
".dsh-ct-body .md-table-wide>table{z-index:1;position:relative}",
|
|
1147
|
-
// ---- 已停止标记(宿主 .hWmORq_stopped 等价) ----
|
|
1148
|
-
".dsh-ct-stopped{border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}",
|
|
1149
|
-
// ---- 「已翻译」交互与标记:可点态常驻(含左缘内缩);区分走色相+粗细
|
|
1150
|
-
// (0.5px 虚线会被抗锯齿糊成实线)——蓝粗线=读译文,灰细线=读原文备着译文 ----
|
|
1151
|
-
".dsh-ct-prose-clickable{padding-left:12px;cursor:pointer}",
|
|
1152
|
-
".dsh-ct-prose-clickable:focus-visible{outline:1.5px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px;border-radius:var(--dsw-radius-sm)}",
|
|
1153
|
-
".dsh-ct-prose-translated{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent)}",
|
|
1154
|
-
".dsh-ct-prose-original{border-left:0.5px solid var(--dsw-alias-border-l2)}",
|
|
1155
|
-
// ---- ReasoningRow(宿主 lcKema_* 等价,前缀换成 dsh-ct-think) ----
|
|
1156
|
-
".dsh-ct-think-root{flex-direction:column;display:flex}",
|
|
1157
|
-
".dsh-ct-think-root:not([data-expanded]){contain:size layout;height:calc(24px + var(--dsh-content-font-delta,0px))}",
|
|
1158
|
-
".dsh-ct-think-row{position:relative;overflow:hidden}",
|
|
1159
|
-
".dsh-ct-think-root[data-expanded] [data-open] [data-disclosure-row]{z-index:1;background:var(--dsw-alias-bg-base);position:sticky;top:0}",
|
|
1160
|
-
'.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{content:"";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite dsh-ct-reasoning-sweep;position:absolute;left:0}',
|
|
1161
|
-
"@keyframes dsh-ct-reasoning-sweep{0%{left:-300px}90%,to{left:100%}}",
|
|
1162
|
-
"@media (prefers-reduced-motion:reduce){.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{animation:none}}",
|
|
1163
|
-
".dsh-ct-think-leading{flex-shrink:0}",
|
|
1164
|
-
".dsh-ct-think-chevron{color:var(--dsw-alias-label-secondary)}",
|
|
1165
|
-
".dsh-ct-think-title{font-weight:400}",
|
|
1166
|
-
".dsh-ct-think-separator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}",
|
|
1167
|
-
".dsh-ct-think-summary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));white-space:nowrap;flex:auto;overflow:hidden}",
|
|
1168
|
-
".dsh-ct-think-summary-text{text-overflow:ellipsis;display:block;overflow:hidden}",
|
|
1169
|
-
".dsh-ct-think-summary[data-streaming]{mask-image:linear-gradient(90deg,currentcolor calc(100% - 48px),transparent)}",
|
|
1170
|
-
".dsh-ct-think-summary[data-streaming] .dsh-ct-think-summary-text{text-overflow:clip;overflow:visible}",
|
|
1171
|
-
".dsh-ct-think-root:not([data-preview]) .dsh-ct-think-separator,.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-summary{display:none}",
|
|
1172
|
-
".dsh-ct-think-body{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));min-width:0}",
|
|
1173
|
-
// ---- 无障碍:屏幕阅读器专用文本 ----
|
|
1174
|
-
".dsh-ct-visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}"
|
|
1175
|
-
].join("");
|
|
1176
|
-
function proseClassNames(processed, showOriginal) {
|
|
1177
|
-
if (!processed) return void 0;
|
|
1178
|
-
return showOriginal ? "dsh-ct-prose-clickable dsh-ct-prose-original" : "dsh-ct-prose-clickable dsh-ct-prose-translated";
|
|
1179
|
-
}
|
|
1180
|
-
function ensureAssistantStyles() {
|
|
1181
|
-
if (typeof document === "undefined") return;
|
|
1182
|
-
const selector = `style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`;
|
|
1183
|
-
if (document.querySelector(selector) !== null) return;
|
|
1184
|
-
const tag = document.createElement("style");
|
|
1185
|
-
tag.dataset.plugin = "@lynn123411/dsh-chat-translate";
|
|
1186
|
-
tag.dataset.pluginCss = STYLE_TAG_ID;
|
|
1187
|
-
tag.textContent = ASSISTANT_CSS;
|
|
1188
|
-
document.head.appendChild(tag);
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
1264
|
// src/client/chat/assistant-step.tsx
|
|
1192
1265
|
var thinkIcon = () => React.createElement(import_dsh_client_ui_primitives3.IconThinkOutlineRegular, { size: 14 });
|
|
1193
1266
|
function markdownLabels(t) {
|
|
@@ -1352,44 +1425,55 @@ var ReasoningRow = (0, import_react4.memo)(function ReasoningRow2({
|
|
|
1352
1425
|
)
|
|
1353
1426
|
);
|
|
1354
1427
|
});
|
|
1355
|
-
function blockClick(event,
|
|
1428
|
+
function blockClick(event, action) {
|
|
1356
1429
|
const guardEvent = {
|
|
1357
1430
|
target: event.target,
|
|
1358
1431
|
currentTarget: event.currentTarget
|
|
1359
1432
|
};
|
|
1360
|
-
if (isBareBlockClick(guardEvent, typeof window === "undefined" ? void 0 : window))
|
|
1433
|
+
if (isBareBlockClick(guardEvent, typeof window === "undefined" ? void 0 : window)) action();
|
|
1361
1434
|
}
|
|
1362
1435
|
function ProseBlock({
|
|
1363
1436
|
text,
|
|
1364
1437
|
translated,
|
|
1365
|
-
|
|
1438
|
+
mark,
|
|
1439
|
+
fail,
|
|
1366
1440
|
onToggle,
|
|
1441
|
+
onRetry,
|
|
1367
1442
|
streaming,
|
|
1368
1443
|
labels,
|
|
1369
1444
|
mentions,
|
|
1370
1445
|
pathImages
|
|
1371
1446
|
}) {
|
|
1372
|
-
const
|
|
1373
|
-
const
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1447
|
+
const action = proseAction(mark);
|
|
1448
|
+
const clickable = action !== null;
|
|
1449
|
+
const retrying = action === "retry";
|
|
1450
|
+
const handler = retrying ? onRetry : onToggle;
|
|
1451
|
+
const source = mark === "translated" ? translated : text;
|
|
1452
|
+
const copy = rowCopy();
|
|
1453
|
+
const block = React.createElement(
|
|
1376
1454
|
"div",
|
|
1377
1455
|
{
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
"data-translated": showTranslation ? "true" : void 0,
|
|
1456
|
+
className: proseClassNames(mark),
|
|
1457
|
+
"data-translated": mark === "translated" ? "true" : void 0,
|
|
1381
1458
|
// 容器内含链接等交互内容,不套 role=button(非法嵌套);可聚焦 + 键盘
|
|
1382
|
-
// Enter/Space
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1459
|
+
// Enter/Space 即切换/重试,满足官方「键盘可达」门。在途块也保持可聚焦
|
|
1460
|
+
// (Enter 空操作)——补跑把红线折成脉动时,键盘焦点不致从行上掉回 body。
|
|
1461
|
+
tabIndex: clickable || mark === "inflight" ? 0 : void 0,
|
|
1462
|
+
onClick: clickable ? (event) => blockClick(event, handler) : void 0,
|
|
1463
|
+
onKeyDown: clickable ? (event) => {
|
|
1386
1464
|
if (event.target !== event.currentTarget) return;
|
|
1387
1465
|
if (event.key === "Enter" || event.key === " ") {
|
|
1388
1466
|
event.preventDefault();
|
|
1389
|
-
|
|
1467
|
+
handler();
|
|
1390
1468
|
}
|
|
1391
1469
|
} : void 0
|
|
1392
1470
|
},
|
|
1471
|
+
retrying && React.createElement(
|
|
1472
|
+
"span",
|
|
1473
|
+
{ className: "dsh-ct-retry", "aria-hidden": true },
|
|
1474
|
+
React.createElement(import_dsh_client_ui_primitives3.IconRefreshOutlineRegular, { size: 10 })
|
|
1475
|
+
),
|
|
1476
|
+
retrying && React.createElement("span", { className: "dsh-ct-visually-hidden" }, copy.retryAria),
|
|
1393
1477
|
React.createElement(import_dsh_client_ui_primitives3.MarkdownText, {
|
|
1394
1478
|
text: source,
|
|
1395
1479
|
streaming,
|
|
@@ -1398,6 +1482,13 @@ function ProseBlock({
|
|
|
1398
1482
|
pathImages
|
|
1399
1483
|
})
|
|
1400
1484
|
);
|
|
1485
|
+
if (!retrying) return block;
|
|
1486
|
+
return React.createElement(import_dsh_client_ui_primitives3.Tooltip, {
|
|
1487
|
+
label: () => copy.failTitle(fail),
|
|
1488
|
+
side: "right",
|
|
1489
|
+
portal: true,
|
|
1490
|
+
children: block
|
|
1491
|
+
});
|
|
1401
1492
|
}
|
|
1402
1493
|
var AssistantStepView = (0, import_react4.memo)(function AssistantStepView2(props) {
|
|
1403
1494
|
ensureAssistantStyles();
|
|
@@ -1441,7 +1532,9 @@ var AssistantStepView = (0, import_react4.memo)(function AssistantStepView2(prop
|
|
|
1441
1532
|
if (streaming || !canTranslate || !inView || plan.entries === null || plan.texts.length === 0) return;
|
|
1442
1533
|
chatTranslate.ensure(rowKey, plan.texts);
|
|
1443
1534
|
}, [streaming, canTranslate, inView, rowKey, plan]);
|
|
1444
|
-
const
|
|
1535
|
+
const [originalKeys, setOriginalKeys] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set());
|
|
1536
|
+
const registered = chatTranslate.getState(rowKey);
|
|
1537
|
+
const rowState = registered !== void 0 && sameTexts(registered.texts, plan.texts) ? registered : void 0;
|
|
1445
1538
|
const displayed = (0, import_react4.useMemo)(
|
|
1446
1539
|
() => planAssistantRow({
|
|
1447
1540
|
blocks,
|
|
@@ -1449,15 +1542,16 @@ var AssistantStepView = (0, import_react4.memo)(function AssistantStepView2(prop
|
|
|
1449
1542
|
interrupted,
|
|
1450
1543
|
groupPart,
|
|
1451
1544
|
canTranslate,
|
|
1452
|
-
outcomes: rowState?.outcomes
|
|
1545
|
+
outcomes: rowState?.outcomes,
|
|
1546
|
+
rowStatus: rowState?.status,
|
|
1547
|
+
originalKeys
|
|
1453
1548
|
}),
|
|
1454
|
-
[blocks, streaming, interrupted, groupPart, canTranslate, rowState]
|
|
1549
|
+
[blocks, streaming, interrupted, groupPart, canTranslate, rowState, originalKeys]
|
|
1455
1550
|
);
|
|
1456
1551
|
const reasoningHidden = turnProcess !== void 0 && turnProcess.foldable && turnProcess.spec.answerStep === data.step && turnProcess.spec.inlineReasoning && !turnProcess.open;
|
|
1457
1552
|
const revealProcess = (0, import_react4.useCallback)(() => {
|
|
1458
1553
|
turnProcess?.setOpen(true);
|
|
1459
1554
|
}, [turnProcess]);
|
|
1460
|
-
const [originalKeys, setOriginalKeys] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set());
|
|
1461
1555
|
const toggleBlock = (0, import_react4.useCallback)((key) => {
|
|
1462
1556
|
setOriginalKeys((current) => {
|
|
1463
1557
|
const next = new Set(current);
|
|
@@ -1466,6 +1560,9 @@ var AssistantStepView = (0, import_react4.memo)(function AssistantStepView2(prop
|
|
|
1466
1560
|
return next;
|
|
1467
1561
|
});
|
|
1468
1562
|
}, []);
|
|
1563
|
+
const retryRow = (0, import_react4.useCallback)(() => {
|
|
1564
|
+
chatTranslate.ensure(rowKey, plan.texts, true);
|
|
1565
|
+
}, [rowKey, plan]);
|
|
1469
1566
|
if (displayed.entries === null) return null;
|
|
1470
1567
|
const last = blocks.length - 1;
|
|
1471
1568
|
const rendered = [];
|
|
@@ -1477,8 +1574,10 @@ var AssistantStepView = (0, import_react4.memo)(function AssistantStepView2(prop
|
|
|
1477
1574
|
key: entry.key,
|
|
1478
1575
|
text: entry.text,
|
|
1479
1576
|
translated: entry.translated,
|
|
1480
|
-
|
|
1577
|
+
mark: entry.mark,
|
|
1578
|
+
fail: entry.fail,
|
|
1481
1579
|
onToggle: () => toggleBlock(entry.key),
|
|
1580
|
+
onRetry: retryRow,
|
|
1482
1581
|
streaming,
|
|
1483
1582
|
labels,
|
|
1484
1583
|
mentions,
|
|
@@ -1648,6 +1747,7 @@ function apply(ctx) {
|
|
|
1648
1747
|
}, "dsh-chat-translate: chat presentation source");
|
|
1649
1748
|
ctx.effect(() => setupSettingsUi(ctx), "dsh-chat-translate: settings section");
|
|
1650
1749
|
ctx.effect(() => setupComposerToggle(ctx), "dsh-chat-translate: composer dock toggle");
|
|
1750
|
+
bindRowCopy(ctx.locale);
|
|
1651
1751
|
}
|
|
1652
1752
|
return module.exports; } });
|
|
1653
1753
|
//# sourceMappingURL=client.js.map
|