@webskill/chatbot 0.9.0 → 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/dist/chatbot.css +13 -8
- package/dist/index.d.ts +733 -522
- package/dist/index.js +636 -305
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -6,11 +6,11 @@ import { t as yaml } from "./yaml-C888tCbJ.js";
|
|
|
6
6
|
import { t as json } from "./json-BOcU6nei.js";
|
|
7
7
|
import { t as typescript } from "./typescript-DAau_Qyl.js";
|
|
8
8
|
import { t as jsx$1 } from "./jsx-D2Jv0wvt.js";
|
|
9
|
-
import { BEHAVIOR_RECORDS_KEY, DEFAULT_LOOP_LIMITS, DEFAULT_USER_PROFILE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FullDisclosureRouter, SerializingMemoryStore, SkillDiscovery, USER_PROFILE_KEY, WebSkillError, WebSkillRuntime, applyUserProfileImport, assertRemoteUrlAllowed, atomicWriteText, diffUserProfile, exportUserProfile, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, summarizeRunUsage, summarizeToolCalls, textParts } from "@webskill/sdk";
|
|
9
|
+
import { BEHAVIOR_RECORDS_KEY, DEFAULT_LOOP_LIMITS, DEFAULT_USER_PROFILE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FullDisclosureRouter, SUPPORTED_DOCUMENT_MIME, SerializingMemoryStore, SkillDiscovery, USER_PROFILE_KEY, WebSkillError, WebSkillRuntime, applyUserProfileImport, assertRemoteUrlAllowed, atomicWriteText, diffUserProfile, exportUserProfile, formatAttachmentText, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, summarizeRunUsage, summarizeToolCalls, textParts } from "@webskill/sdk";
|
|
10
10
|
import { BrowserWorkerScriptExecutor, checkDictationAvailability, compressImageToBudget, createEncryptedMemoryStore, createLlmClient, deleteMemoryEncryptionKey, openMemoryEncryptionKey, probeChromeBuiltinAvailability, startDictation } from "@webskill/sdk/browser";
|
|
11
11
|
import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, SurfaceFormTextsProvider, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
|
|
12
12
|
import { A2UI_SPEC_FORM_PATH, collectFormScopes, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
|
|
13
|
-
import { TodoStore, createDelegationToolSource, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
|
|
13
|
+
import { DownloadedFilePolicy, PAGE_ACTION_KINDS, TodoStore, createDelegationToolSource, createDownloadedFileToolSource, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
|
|
14
14
|
import * as React$1 from "react";
|
|
15
15
|
import React, { Children, Component, Fragment, createContext, createElement, forwardRef, isValidElement, memo, useCallback, useContext, useDeferredValue, useEffect, useEffectEvent, useId, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
16
16
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -26,6 +26,63 @@ import { Derived, attachTransformScopes, useClientLookup, useClientResource } fr
|
|
|
26
26
|
import { resource, useMemoCache, withKey } from "@assistant-ui/tap";
|
|
27
27
|
import { POLICY_DENIAL_CODES } from "@webskill/sdk/governance";
|
|
28
28
|
|
|
29
|
+
//#region ../ui-kit/src/i18n/localizedText.ts
|
|
30
|
+
/**
|
|
31
|
+
* 键的声明顺序即回退顺序(0.13.0 分册 21 FR-21.2)。
|
|
32
|
+
* 写成 `Record<Locale, …>` 而不是 `['zh','en'] as const satisfies readonly Locale[]`:
|
|
33
|
+
* 后者只校验元素合法、不校验取值穷尽,新增语种时会安静地少一个。
|
|
34
|
+
*/
|
|
35
|
+
const FALLBACK_ORDER = {
|
|
36
|
+
zh: null,
|
|
37
|
+
en: null
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* SDK 支持的语种,声明顺序同时是缺语种时的回退顺序与设置界面里的展示顺序。
|
|
41
|
+
* @experimental
|
|
42
|
+
*/
|
|
43
|
+
const SUPPORTED_LOCALES = Object.keys(FALLBACK_ORDER);
|
|
44
|
+
const trimmed = (value) => {
|
|
45
|
+
if (typeof value !== "string") return void 0;
|
|
46
|
+
const text = value.trim();
|
|
47
|
+
return text === "" ? void 0 : text;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* 按当前语种解析多语种文案(0.13.0 分册 21 FR-21.2):
|
|
51
|
+
* 当前语种非空 → 用它;否则按 {@link SUPPORTED_LOCALES} 顺序取第一个非空的;
|
|
52
|
+
* 全空返回 `undefined`——调用方据此整条丢弃,返回空串会一路流到渲染层变成空白卡片。
|
|
53
|
+
* @experimental
|
|
54
|
+
*/
|
|
55
|
+
function resolveLocalizedText(text, locale) {
|
|
56
|
+
if (typeof text === "string") return trimmed(text);
|
|
57
|
+
if (typeof text !== "object" || text === null) return void 0;
|
|
58
|
+
const preferred = trimmed(text[locale]);
|
|
59
|
+
if (preferred !== void 0) return preferred;
|
|
60
|
+
for (const candidate of SUPPORTED_LOCALES) {
|
|
61
|
+
const value = trimmed(text[candidate]);
|
|
62
|
+
if (value !== void 0) return value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** 字符串简写填满全部语种:「这条不分语种,就这一句」。空白串得到空对象。 */
|
|
66
|
+
function normalizeLocalizedText(value) {
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
const text = trimmed(value);
|
|
69
|
+
if (text === void 0) return {};
|
|
70
|
+
return Object.fromEntries(SUPPORTED_LOCALES.map((locale) => [locale, text]));
|
|
71
|
+
}
|
|
72
|
+
if (typeof value !== "object" || value === null) return {};
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const locale of SUPPORTED_LOCALES) {
|
|
75
|
+
const text = trimmed(value[locale]);
|
|
76
|
+
if (text !== void 0) out[locale] = text;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** 是否有任何语种填了内容;全空的条目一律丢弃(FR-21.2 规则 3) */
|
|
81
|
+
function hasLocalizedText(text) {
|
|
82
|
+
return SUPPORTED_LOCALES.some((locale) => resolveLocalizedText(text, locale) !== void 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
29
86
|
//#region ../ui-kit/src/i18n/createI18n.ts
|
|
30
87
|
/** 字典工厂:各包定义自己的双语字典(恒等返回,保留字面量类型) */
|
|
31
88
|
function defineDictionary(dict) {
|
|
@@ -947,72 +1004,6 @@ const __iconNode$22 = [
|
|
|
947
1004
|
];
|
|
948
1005
|
const Mic = createLucideIcon("mic", __iconNode$22);
|
|
949
1006
|
|
|
950
|
-
//#endregion
|
|
951
|
-
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/monitor-off.mjs
|
|
952
|
-
/**
|
|
953
|
-
* @license lucide-react v1.27.0 - ISC
|
|
954
|
-
*
|
|
955
|
-
* This source code is licensed under the ISC license.
|
|
956
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
957
|
-
*/
|
|
958
|
-
const __iconNode$21 = [
|
|
959
|
-
["path", {
|
|
960
|
-
d: "M12 17v4",
|
|
961
|
-
key: "1riwvh"
|
|
962
|
-
}],
|
|
963
|
-
["path", {
|
|
964
|
-
d: "M17 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 1.184-1.826",
|
|
965
|
-
key: "cv7jms"
|
|
966
|
-
}],
|
|
967
|
-
["path", {
|
|
968
|
-
d: "m2 2 20 20",
|
|
969
|
-
key: "1ooewy"
|
|
970
|
-
}],
|
|
971
|
-
["path", {
|
|
972
|
-
d: "M8 21h8",
|
|
973
|
-
key: "1ev6f3"
|
|
974
|
-
}],
|
|
975
|
-
["path", {
|
|
976
|
-
d: "M8.656 3H20a2 2 0 0 1 2 2v10a2 2 0 0 1-.293 1.042",
|
|
977
|
-
key: "z8ni2w"
|
|
978
|
-
}]
|
|
979
|
-
];
|
|
980
|
-
const MonitorOff = createLucideIcon("monitor-off", __iconNode$21);
|
|
981
|
-
|
|
982
|
-
//#endregion
|
|
983
|
-
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/monitor.mjs
|
|
984
|
-
/**
|
|
985
|
-
* @license lucide-react v1.27.0 - ISC
|
|
986
|
-
*
|
|
987
|
-
* This source code is licensed under the ISC license.
|
|
988
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
989
|
-
*/
|
|
990
|
-
const __iconNode$20 = [
|
|
991
|
-
["rect", {
|
|
992
|
-
width: "20",
|
|
993
|
-
height: "14",
|
|
994
|
-
x: "2",
|
|
995
|
-
y: "3",
|
|
996
|
-
rx: "2",
|
|
997
|
-
key: "48i651"
|
|
998
|
-
}],
|
|
999
|
-
["line", {
|
|
1000
|
-
x1: "8",
|
|
1001
|
-
x2: "16",
|
|
1002
|
-
y1: "21",
|
|
1003
|
-
y2: "21",
|
|
1004
|
-
key: "1svkeh"
|
|
1005
|
-
}],
|
|
1006
|
-
["line", {
|
|
1007
|
-
x1: "12",
|
|
1008
|
-
x2: "12",
|
|
1009
|
-
y1: "17",
|
|
1010
|
-
y2: "21",
|
|
1011
|
-
key: "vw1qmm"
|
|
1012
|
-
}]
|
|
1013
|
-
];
|
|
1014
|
-
const Monitor = createLucideIcon("monitor", __iconNode$20);
|
|
1015
|
-
|
|
1016
1007
|
//#endregion
|
|
1017
1008
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/octagon-x.mjs
|
|
1018
1009
|
/**
|
|
@@ -1021,7 +1012,7 @@ const Monitor = createLucideIcon("monitor", __iconNode$20);
|
|
|
1021
1012
|
* This source code is licensed under the ISC license.
|
|
1022
1013
|
* See the LICENSE file in the root directory of this source tree.
|
|
1023
1014
|
*/
|
|
1024
|
-
const __iconNode$
|
|
1015
|
+
const __iconNode$21 = [
|
|
1025
1016
|
["path", {
|
|
1026
1017
|
d: "m15 9-6 6",
|
|
1027
1018
|
key: "1uzhvr"
|
|
@@ -1035,7 +1026,7 @@ const __iconNode$19 = [
|
|
|
1035
1026
|
key: "z0biqf"
|
|
1036
1027
|
}]
|
|
1037
1028
|
];
|
|
1038
|
-
const OctagonX = createLucideIcon("octagon-x", __iconNode$
|
|
1029
|
+
const OctagonX = createLucideIcon("octagon-x", __iconNode$21);
|
|
1039
1030
|
|
|
1040
1031
|
//#endregion
|
|
1041
1032
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/panel-left.mjs
|
|
@@ -1045,7 +1036,7 @@ const OctagonX = createLucideIcon("octagon-x", __iconNode$19);
|
|
|
1045
1036
|
* This source code is licensed under the ISC license.
|
|
1046
1037
|
* See the LICENSE file in the root directory of this source tree.
|
|
1047
1038
|
*/
|
|
1048
|
-
const __iconNode$
|
|
1039
|
+
const __iconNode$20 = [["rect", {
|
|
1049
1040
|
width: "18",
|
|
1050
1041
|
height: "18",
|
|
1051
1042
|
x: "3",
|
|
@@ -1056,7 +1047,7 @@ const __iconNode$18 = [["rect", {
|
|
|
1056
1047
|
d: "M9 3v18",
|
|
1057
1048
|
key: "fh3hqa"
|
|
1058
1049
|
}]];
|
|
1059
|
-
const PanelLeft = createLucideIcon("panel-left", __iconNode$
|
|
1050
|
+
const PanelLeft = createLucideIcon("panel-left", __iconNode$20);
|
|
1060
1051
|
|
|
1061
1052
|
//#endregion
|
|
1062
1053
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/paperclip.mjs
|
|
@@ -1066,11 +1057,11 @@ const PanelLeft = createLucideIcon("panel-left", __iconNode$18);
|
|
|
1066
1057
|
* This source code is licensed under the ISC license.
|
|
1067
1058
|
* See the LICENSE file in the root directory of this source tree.
|
|
1068
1059
|
*/
|
|
1069
|
-
const __iconNode$
|
|
1060
|
+
const __iconNode$19 = [["path", {
|
|
1070
1061
|
d: "m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",
|
|
1071
1062
|
key: "1miecu"
|
|
1072
1063
|
}]];
|
|
1073
|
-
const Paperclip = createLucideIcon("paperclip", __iconNode$
|
|
1064
|
+
const Paperclip = createLucideIcon("paperclip", __iconNode$19);
|
|
1074
1065
|
|
|
1075
1066
|
//#endregion
|
|
1076
1067
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/pencil.mjs
|
|
@@ -1080,14 +1071,59 @@ const Paperclip = createLucideIcon("paperclip", __iconNode$17);
|
|
|
1080
1071
|
* This source code is licensed under the ISC license.
|
|
1081
1072
|
* See the LICENSE file in the root directory of this source tree.
|
|
1082
1073
|
*/
|
|
1083
|
-
const __iconNode$
|
|
1074
|
+
const __iconNode$18 = [["path", {
|
|
1084
1075
|
d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
|
|
1085
1076
|
key: "1a8usu"
|
|
1086
1077
|
}], ["path", {
|
|
1087
1078
|
d: "m15 5 4 4",
|
|
1088
1079
|
key: "1mk7zo"
|
|
1089
1080
|
}]];
|
|
1090
|
-
const Pencil = createLucideIcon("pencil", __iconNode$
|
|
1081
|
+
const Pencil = createLucideIcon("pencil", __iconNode$18);
|
|
1082
|
+
|
|
1083
|
+
//#endregion
|
|
1084
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/pin-off.mjs
|
|
1085
|
+
/**
|
|
1086
|
+
* @license lucide-react v1.27.0 - ISC
|
|
1087
|
+
*
|
|
1088
|
+
* This source code is licensed under the ISC license.
|
|
1089
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1090
|
+
*/
|
|
1091
|
+
const __iconNode$17 = [
|
|
1092
|
+
["path", {
|
|
1093
|
+
d: "M12 17v5",
|
|
1094
|
+
key: "bb1du9"
|
|
1095
|
+
}],
|
|
1096
|
+
["path", {
|
|
1097
|
+
d: "M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89",
|
|
1098
|
+
key: "znwnzq"
|
|
1099
|
+
}],
|
|
1100
|
+
["path", {
|
|
1101
|
+
d: "m2 2 20 20",
|
|
1102
|
+
key: "1ooewy"
|
|
1103
|
+
}],
|
|
1104
|
+
["path", {
|
|
1105
|
+
d: "M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11",
|
|
1106
|
+
key: "c9qhm2"
|
|
1107
|
+
}]
|
|
1108
|
+
];
|
|
1109
|
+
const PinOff = createLucideIcon("pin-off", __iconNode$17);
|
|
1110
|
+
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/pin.mjs
|
|
1113
|
+
/**
|
|
1114
|
+
* @license lucide-react v1.27.0 - ISC
|
|
1115
|
+
*
|
|
1116
|
+
* This source code is licensed under the ISC license.
|
|
1117
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1118
|
+
*/
|
|
1119
|
+
const __iconNode$16 = [["path", {
|
|
1120
|
+
d: "M12 17v5",
|
|
1121
|
+
key: "bb1du9"
|
|
1122
|
+
}], ["path", {
|
|
1123
|
+
d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z",
|
|
1124
|
+
key: "1nkz8b"
|
|
1125
|
+
}]];
|
|
1126
|
+
const Pin = createLucideIcon("pin", __iconNode$16);
|
|
1091
1127
|
|
|
1092
1128
|
//#endregion
|
|
1093
1129
|
//#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/play.mjs
|
|
@@ -7967,7 +8003,7 @@ const DIALOG_WIDTHS = {
|
|
|
7967
8003
|
* 基类锁定 `h` `rounded` `z`(className 覆盖不生效):
|
|
7968
8004
|
* `max-h` 是视口上限,圆角与层级属 ui-kit 的整体契约。
|
|
7969
8005
|
*/
|
|
7970
|
-
function Dialog({ open, onClose, title, description, footer, hideCloseButton = false, className = "", size, children }) {
|
|
8006
|
+
function Dialog({ open, onClose, title, description, footer, hideCloseButton = false, dismissOnOutsideClick = true, className = "", size, children }) {
|
|
7971
8007
|
const restoreFocusRef = useRef(null);
|
|
7972
8008
|
const wasOpenRef = useRef(false);
|
|
7973
8009
|
if (open && !wasOpenRef.current && typeof document !== "undefined") restoreFocusRef.current = document.activeElement;
|
|
@@ -7978,6 +8014,7 @@ function Dialog({ open, onClose, title, description, footer, hideCloseButton = f
|
|
|
7978
8014
|
children: /* @__PURE__ */ jsxs(DialogPortal, {
|
|
7979
8015
|
container: usePortalContainer(),
|
|
7980
8016
|
children: [/* @__PURE__ */ jsx(DialogOverlay, { className: "fixed inset-0 z-[var(--webskill-z-scrim)] bg-scrim" }), /* @__PURE__ */ jsxs(DialogContent, {
|
|
8017
|
+
...dismissOnOutsideClick ? {} : { onInteractOutside: (event) => event.preventDefault() },
|
|
7981
8018
|
onCloseAutoFocus: (event) => {
|
|
7982
8019
|
const source = restoreFocusRef.current;
|
|
7983
8020
|
if (source instanceof HTMLElement && source.isConnected) {
|
|
@@ -12748,7 +12785,7 @@ function merge(definitions, space) {
|
|
|
12748
12785
|
* Value that can be used to look up the properly cased property on a
|
|
12749
12786
|
* `Schema`.
|
|
12750
12787
|
*/
|
|
12751
|
-
function normalize$
|
|
12788
|
+
function normalize$2(value) {
|
|
12752
12789
|
return value.toLowerCase();
|
|
12753
12790
|
}
|
|
12754
12791
|
|
|
@@ -12899,8 +12936,8 @@ function create(definition) {
|
|
|
12899
12936
|
const info = new DefinedInfo(property, definition.transform(definition.attributes || {}, property), value, definition.space);
|
|
12900
12937
|
if (definition.mustUseProperty && definition.mustUseProperty.includes(property)) info.mustUseProperty = true;
|
|
12901
12938
|
properties[property] = info;
|
|
12902
|
-
normals[normalize$
|
|
12903
|
-
normals[normalize$
|
|
12939
|
+
normals[normalize$2(property)] = property;
|
|
12940
|
+
normals[normalize$2(info.attribute)] = property;
|
|
12904
12941
|
}
|
|
12905
12942
|
return new Schema(properties, normals, definition.space);
|
|
12906
12943
|
}
|
|
@@ -13992,7 +14029,7 @@ const valid = /^data[-\w.:]+$/i;
|
|
|
13992
14029
|
* Info.
|
|
13993
14030
|
*/
|
|
13994
14031
|
function find(schema, value) {
|
|
13995
|
-
const normal = normalize$
|
|
14032
|
+
const normal = normalize$2(value);
|
|
13996
14033
|
let property = value;
|
|
13997
14034
|
let Type = Info;
|
|
13998
14035
|
if (normal in schema.normal) return schema.property[schema.normal[normal]];
|
|
@@ -18879,7 +18916,7 @@ function join(...segments) {
|
|
|
18879
18916
|
assertPath$1(segments[index]);
|
|
18880
18917
|
if (segments[index]) joined = joined === void 0 ? segments[index] : joined + "/" + segments[index];
|
|
18881
18918
|
}
|
|
18882
|
-
return joined === void 0 ? "." : normalize(joined);
|
|
18919
|
+
return joined === void 0 ? "." : normalize$1(joined);
|
|
18883
18920
|
}
|
|
18884
18921
|
/**
|
|
18885
18922
|
* Normalize a basic file path.
|
|
@@ -18889,7 +18926,7 @@ function join(...segments) {
|
|
|
18889
18926
|
* @returns {string}
|
|
18890
18927
|
* File path.
|
|
18891
18928
|
*/
|
|
18892
|
-
function normalize(path) {
|
|
18929
|
+
function normalize$1(path) {
|
|
18893
18930
|
assertPath$1(path);
|
|
18894
18931
|
const absolute = path.codePointAt(0) === 47;
|
|
18895
18932
|
let value = normalizeString(path, !absolute);
|
|
@@ -27475,7 +27512,7 @@ function addChild(nodes, value) {
|
|
|
27475
27512
|
function parsePrimitive(info, name, value) {
|
|
27476
27513
|
if (typeof value === "string") {
|
|
27477
27514
|
if (info.number && value && !Number.isNaN(Number(value))) return Number(value);
|
|
27478
|
-
if ((info.boolean || info.overloadedBoolean) && (value === "" || normalize$
|
|
27515
|
+
if ((info.boolean || info.overloadedBoolean) && (value === "" || normalize$2(value) === normalize$2(name))) return true;
|
|
27479
27516
|
}
|
|
27480
27517
|
return value;
|
|
27481
27518
|
}
|
|
@@ -30298,6 +30335,55 @@ const fadeInUp = {
|
|
|
30298
30335
|
}
|
|
30299
30336
|
};
|
|
30300
30337
|
|
|
30338
|
+
//#endregion
|
|
30339
|
+
//#region ../core/src/attachment/kind.ts
|
|
30340
|
+
/** 可作为图片分片外发的 MIME 类型 @experimental */
|
|
30341
|
+
const IMAGE_MIME_TYPES = [
|
|
30342
|
+
"image/png",
|
|
30343
|
+
"image/jpeg",
|
|
30344
|
+
"image/webp",
|
|
30345
|
+
"image/gif"
|
|
30346
|
+
];
|
|
30347
|
+
/**
|
|
30348
|
+
* 可按文本读取的扩展名。accept 白名单与判定白名单共用这一份——
|
|
30349
|
+
* 0.5.x 两处不一致,`log`/`yaml`/`yml` 能通过判定却在文件选择器里选不到。
|
|
30350
|
+
*/
|
|
30351
|
+
const TEXT_EXTENSIONS = [
|
|
30352
|
+
"txt",
|
|
30353
|
+
"md",
|
|
30354
|
+
"csv",
|
|
30355
|
+
"json",
|
|
30356
|
+
"log",
|
|
30357
|
+
"yaml",
|
|
30358
|
+
"yml"
|
|
30359
|
+
];
|
|
30360
|
+
/** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) @experimental */
|
|
30361
|
+
const FILE_MIME_TYPES = ["application/pdf"];
|
|
30362
|
+
/**
|
|
30363
|
+
* 需要**客户端先抽取成文本**才能外发的类型(FR-23.9 / 0.13.0 FR-12.5)。
|
|
30364
|
+
* 不走直通的 `file`:docx / xlsx 发给 provider 会被当成二进制垃圾。
|
|
30365
|
+
*/
|
|
30366
|
+
const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
30367
|
+
const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
30368
|
+
const TEXT_EXTENSION_SET = new Set(TEXT_EXTENSIONS);
|
|
30369
|
+
const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
|
|
30370
|
+
const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
|
|
30371
|
+
const EXTRACTED_MIME_SET = /* @__PURE__ */ new Set([DOCX_MIME, XLSX_MIME]);
|
|
30372
|
+
const EXTRACTED_EXTENSION_SET = /* @__PURE__ */ new Set(["docx", "xlsx"]);
|
|
30373
|
+
function extensionOf(fileName) {
|
|
30374
|
+
return fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
30375
|
+
}
|
|
30376
|
+
/** 分类集中一处,不散落在各 UI 分支里 @experimental */
|
|
30377
|
+
function classifyAttachment(contentType, fileName) {
|
|
30378
|
+
const type = contentType.toLowerCase();
|
|
30379
|
+
if (IMAGE_MIME_SET.has(type)) return "image";
|
|
30380
|
+
if (EXTRACTED_MIME_SET.has(type)) return "document-text";
|
|
30381
|
+
if (FILE_MIME_SET.has(type)) return "file";
|
|
30382
|
+
if (type.startsWith("text/") || type === "application/json") return "text";
|
|
30383
|
+
if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
|
|
30384
|
+
if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
|
|
30385
|
+
}
|
|
30386
|
+
|
|
30301
30387
|
//#endregion
|
|
30302
30388
|
//#region ../runtime/src/tools/types.ts
|
|
30303
30389
|
/**
|
|
@@ -30348,6 +30434,101 @@ const DEFAULT_LOOP_LIMITS$1 = {
|
|
|
30348
30434
|
maxHistoryMessages: 1e3
|
|
30349
30435
|
};
|
|
30350
30436
|
|
|
30437
|
+
//#endregion
|
|
30438
|
+
//#region ../ui-kit/src/runtime-config/quickPrompts.ts
|
|
30439
|
+
/**
|
|
30440
|
+
* 图标表。这张表不进任何包的公开面:宿主只认名字,具体用哪套图标是实现细节。
|
|
30441
|
+
*/
|
|
30442
|
+
const ICONS = {
|
|
30443
|
+
chart: ChartColumn,
|
|
30444
|
+
document: FileText,
|
|
30445
|
+
report: ClipboardList,
|
|
30446
|
+
search: Search,
|
|
30447
|
+
list: List,
|
|
30448
|
+
bug: Bug,
|
|
30449
|
+
test: FlaskConical,
|
|
30450
|
+
metric: Gauge,
|
|
30451
|
+
compare: GitCompare,
|
|
30452
|
+
page: AppWindow,
|
|
30453
|
+
run: Play,
|
|
30454
|
+
warn: TriangleAlert,
|
|
30455
|
+
sparkles: Sparkles,
|
|
30456
|
+
settings: Settings
|
|
30457
|
+
};
|
|
30458
|
+
/** 受控枚举的全部取值(console 的图标下拉数据源) */
|
|
30459
|
+
const QUICK_PROMPT_ICON_NAMES = Object.keys(ICONS);
|
|
30460
|
+
/** 空态网格是两列,8 条即四行(FR-17.4 / D-17-6);用户可在 console 里上调 */
|
|
30461
|
+
const DEFAULT_QUICK_PROMPT_LIMIT = 8;
|
|
30462
|
+
/** 可调范围的天花板:20 条已经把欢迎区撑成十行,再多就满屏都是按钮 */
|
|
30463
|
+
const MAX_QUICK_PROMPT_LIMIT = 20;
|
|
30464
|
+
/** 未知名字每个只提示一次,免得每次渲染都刷屏 */
|
|
30465
|
+
const warned$1 = /* @__PURE__ */ new Set();
|
|
30466
|
+
/**
|
|
30467
|
+
* 解析图标名;取值不在枚举内时返回 undefined(按无图标渲染),
|
|
30468
|
+
* 并在控制台提示一次可用取值——宿主拼错时不至于只看到「图标没出来」。
|
|
30469
|
+
*/
|
|
30470
|
+
function resolveQuickPromptIcon(icon) {
|
|
30471
|
+
if (icon === void 0) return void 0;
|
|
30472
|
+
const resolved = ICONS[icon];
|
|
30473
|
+
if (resolved !== void 0) return resolved;
|
|
30474
|
+
if (!warned$1.has(icon)) {
|
|
30475
|
+
warned$1.add(icon);
|
|
30476
|
+
console.debug("[webskill] unknown quick prompt icon", {
|
|
30477
|
+
icon,
|
|
30478
|
+
available: Object.keys(ICONS)
|
|
30479
|
+
});
|
|
30480
|
+
}
|
|
30481
|
+
}
|
|
30482
|
+
/** 统一成对象形态:宿主可以只给字符串,该字符串填满全部语种(FR-21.1) */
|
|
30483
|
+
const normalize = (entry) => typeof entry === "string" ? { text: normalizeLocalizedText(entry) } : entry;
|
|
30484
|
+
/**
|
|
30485
|
+
* 拼接动态与静态两批快捷指令(FR-17.4)。
|
|
30486
|
+
*
|
|
30487
|
+
* 动态在前——它是上下文相关的,更容易点到的位置留给它。
|
|
30488
|
+
* 去重**只发生在跨界处**:静态清单里与某条动态指令同文案的会让位;
|
|
30489
|
+
* 静态清单**内部**的重复原样保留(0.12.0 AC-21.6:宿主自定义时文案本就无唯一性约束,
|
|
30490
|
+
* 混排 `['A', {text:'A', icon:'bug'}]` 是合法的两张卡)。
|
|
30491
|
+
*
|
|
30492
|
+
* 0.13.0 分册 21 FR-21.4:去重口径是**按 `locale` 解析后的文案**,
|
|
30493
|
+
* 因此同一对条目在一个语种下会合并、在另一个语种下可能各自保留。
|
|
30494
|
+
* 按 `id` 去重不可行:动态清单传的是 `QuickPrompt`,本来就没有 `id`。
|
|
30495
|
+
*
|
|
30496
|
+
* 结果按 `limit` 截断(默认 {@link DEFAULT_QUICK_PROMPT_LIMIT})。
|
|
30497
|
+
*/
|
|
30498
|
+
function mergeQuickPrompts(dynamic, statics, locale, limit = 8) {
|
|
30499
|
+
const resolve = (entry) => {
|
|
30500
|
+
const prompt = normalize(entry);
|
|
30501
|
+
const text = resolveLocalizedText(prompt.text, locale);
|
|
30502
|
+
if (text === void 0) return void 0;
|
|
30503
|
+
return {
|
|
30504
|
+
text,
|
|
30505
|
+
...prompt.icon !== void 0 ? { icon: prompt.icon } : {}
|
|
30506
|
+
};
|
|
30507
|
+
};
|
|
30508
|
+
const head = [];
|
|
30509
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
30510
|
+
for (const entry of dynamic ?? []) {
|
|
30511
|
+
const prompt = resolve(entry);
|
|
30512
|
+
if (prompt === void 0 || claimed.has(prompt.text)) continue;
|
|
30513
|
+
claimed.add(prompt.text);
|
|
30514
|
+
head.push(prompt);
|
|
30515
|
+
}
|
|
30516
|
+
const tail = [];
|
|
30517
|
+
for (const entry of statics) {
|
|
30518
|
+
const prompt = resolve(entry);
|
|
30519
|
+
if (prompt === void 0 || claimed.has(prompt.text)) continue;
|
|
30520
|
+
tail.push(prompt);
|
|
30521
|
+
}
|
|
30522
|
+
return [...head, ...tail].slice(0, clampQuickPromptLimit(limit));
|
|
30523
|
+
}
|
|
30524
|
+
/** 存储里的脏值不能让空态变成空白或一堵墙;非正整数一律回默认 */
|
|
30525
|
+
function clampQuickPromptLimit(value) {
|
|
30526
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 8;
|
|
30527
|
+
const floored = Math.floor(value);
|
|
30528
|
+
if (floored < 1) return 8;
|
|
30529
|
+
return Math.min(floored, 20);
|
|
30530
|
+
}
|
|
30531
|
+
|
|
30351
30532
|
//#endregion
|
|
30352
30533
|
//#region ../ui-kit/src/runtime-config/types.ts
|
|
30353
30534
|
/**
|
|
@@ -30402,11 +30583,13 @@ function defaultRuntimeConfig() {
|
|
|
30402
30583
|
fetchData: false
|
|
30403
30584
|
},
|
|
30404
30585
|
maxDataSourceBytes: DEFAULT_MAX_DATA_SOURCE_BYTES,
|
|
30586
|
+
dataSources: [],
|
|
30405
30587
|
remoteUrl: {
|
|
30406
30588
|
allowHttp: false,
|
|
30407
30589
|
allowPrivateHosts: false
|
|
30408
30590
|
},
|
|
30409
|
-
typescript: { enabled: false }
|
|
30591
|
+
typescript: { enabled: false },
|
|
30592
|
+
downloadedFiles: false
|
|
30410
30593
|
},
|
|
30411
30594
|
llm: { entries: [] },
|
|
30412
30595
|
agentCapabilities: {
|
|
@@ -30427,7 +30610,8 @@ function defaultRuntimeConfig() {
|
|
|
30427
30610
|
imageAttachments: false,
|
|
30428
30611
|
pageImageCapture: false,
|
|
30429
30612
|
maxImageBytes: 10 * 1024 * 1024,
|
|
30430
|
-
maxImagesPerMessage:
|
|
30613
|
+
maxImagesPerMessage: 50,
|
|
30614
|
+
minImageArea: 1024
|
|
30431
30615
|
},
|
|
30432
30616
|
documentSurface: { enabled: false },
|
|
30433
30617
|
userProfile: {
|
|
@@ -30437,6 +30621,9 @@ function defaultRuntimeConfig() {
|
|
|
30437
30621
|
encrypted: true
|
|
30438
30622
|
},
|
|
30439
30623
|
skillState: { quarantineThreshold: 5 },
|
|
30624
|
+
quickPrompts: [],
|
|
30625
|
+
quickPromptsSeeded: false,
|
|
30626
|
+
quickPromptLimit: 8,
|
|
30440
30627
|
dismissedAutoEntries: []
|
|
30441
30628
|
};
|
|
30442
30629
|
}
|
|
@@ -30508,7 +30695,8 @@ function mergeRuntimeConfigDefaults(partial) {
|
|
|
30508
30695
|
typescript: {
|
|
30509
30696
|
...d.sandbox.typescript,
|
|
30510
30697
|
...p["sandbox"]?.["typescript"] ?? {}
|
|
30511
|
-
}
|
|
30698
|
+
},
|
|
30699
|
+
dataSources: readDataSourceEntries(p["sandbox"]?.["dataSources"])
|
|
30512
30700
|
},
|
|
30513
30701
|
llm: mergeLlmSelection(p["llm"]),
|
|
30514
30702
|
agentCapabilities: {
|
|
@@ -30521,6 +30709,9 @@ function mergeRuntimeConfigDefaults(partial) {
|
|
|
30521
30709
|
documentSurface: mergeDocumentSurface(p["documentSurface"], d.documentSurface),
|
|
30522
30710
|
userProfile: mergeUserProfile(p["userProfile"], d.userProfile),
|
|
30523
30711
|
skillState: mergeSkillState(p["skillState"], d.skillState),
|
|
30712
|
+
quickPrompts: readQuickPrompts(p["quickPrompts"]),
|
|
30713
|
+
quickPromptsSeeded: p["quickPromptsSeeded"] === true,
|
|
30714
|
+
quickPromptLimit: clampQuickPromptLimit(p["quickPromptLimit"]),
|
|
30524
30715
|
dismissedAutoEntries: readStringList$1(p["dismissedAutoEntries"], [])
|
|
30525
30716
|
};
|
|
30526
30717
|
}
|
|
@@ -30543,6 +30734,58 @@ function readStringList$1(raw, fallback) {
|
|
|
30543
30734
|
if (!Array.isArray(raw)) return [...fallback];
|
|
30544
30735
|
return raw.filter((item) => typeof item === "string");
|
|
30545
30736
|
}
|
|
30737
|
+
/**
|
|
30738
|
+
* 快捷指令逐条校验(FR-17.2):缺 `id` 或文案全语种皆空的条目**单条丢弃**,
|
|
30739
|
+
* 不因为一条脏数据把整段回退——用户其余的配置不该被连坐。
|
|
30740
|
+
* `icon` 取值不在枚举内时抹掉该字段(渲染侧按无图标处理)。
|
|
30741
|
+
*
|
|
30742
|
+
* 0.13.0 分册 25 FR-25.3:`text: string` 的存量条目一并丢弃,不再升级为语种对象——
|
|
30743
|
+
* 它们在 console 的快捷指令页里没有身份可言,留着就是「看得见、删不掉」。
|
|
30744
|
+
*/
|
|
30745
|
+
function readQuickPrompts(raw) {
|
|
30746
|
+
if (!Array.isArray(raw)) return [];
|
|
30747
|
+
const out = [];
|
|
30748
|
+
for (const item of raw) {
|
|
30749
|
+
if (typeof item !== "object" || item === null) continue;
|
|
30750
|
+
const entry = item;
|
|
30751
|
+
const id = typeof entry["id"] === "string" ? entry["id"].trim() : "";
|
|
30752
|
+
const rawText = entry["text"];
|
|
30753
|
+
if (id === "" || typeof rawText !== "object" || rawText === null) continue;
|
|
30754
|
+
const text = normalizeLocalizedText(rawText);
|
|
30755
|
+
if (!hasLocalizedText(text)) continue;
|
|
30756
|
+
const icon = entry["icon"];
|
|
30757
|
+
const valid = typeof icon === "string" && QUICK_PROMPT_ICON_NAMES.includes(icon) ? icon : void 0;
|
|
30758
|
+
out.push({
|
|
30759
|
+
id,
|
|
30760
|
+
text,
|
|
30761
|
+
...valid !== void 0 ? { icon: valid } : {}
|
|
30762
|
+
});
|
|
30763
|
+
}
|
|
30764
|
+
return out;
|
|
30765
|
+
}
|
|
30766
|
+
/**
|
|
30767
|
+
* 逐条校验用户配的数据源:`id` / `url` 缺失或非字符串即整条丢弃。
|
|
30768
|
+
* 不整段回退,一条脏数据不该连坐其余几条。
|
|
30769
|
+
*/
|
|
30770
|
+
function readDataSourceEntries(raw) {
|
|
30771
|
+
if (!Array.isArray(raw)) return [];
|
|
30772
|
+
const out = [];
|
|
30773
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30774
|
+
for (const item of raw) {
|
|
30775
|
+
if (typeof item !== "object" || item === null) continue;
|
|
30776
|
+
const entry = item;
|
|
30777
|
+
const id = typeof entry["id"] === "string" ? entry["id"].trim() : "";
|
|
30778
|
+
const url = typeof entry["url"] === "string" ? entry["url"].trim() : "";
|
|
30779
|
+
if (id === "" || url === "" || seen.has(id)) continue;
|
|
30780
|
+
seen.add(id);
|
|
30781
|
+
out.push({
|
|
30782
|
+
id,
|
|
30783
|
+
url,
|
|
30784
|
+
description: typeof entry["description"] === "string" ? entry["description"] : ""
|
|
30785
|
+
});
|
|
30786
|
+
}
|
|
30787
|
+
return out;
|
|
30788
|
+
}
|
|
30546
30789
|
/** 上限字段非正数时回退默认值:0 或负数会把整条通道变成永远拒收 */
|
|
30547
30790
|
function mergeMultimodal(raw, d) {
|
|
30548
30791
|
const p = typeof raw === "object" && raw !== null ? raw : {};
|
|
@@ -30551,9 +30794,14 @@ function mergeMultimodal(raw, d) {
|
|
|
30551
30794
|
imageAttachments: typeof p["imageAttachments"] === "boolean" ? p["imageAttachments"] : d.imageAttachments,
|
|
30552
30795
|
pageImageCapture: typeof p["pageImageCapture"] === "boolean" ? p["pageImageCapture"] : d.pageImageCapture,
|
|
30553
30796
|
maxImageBytes: positive(p["maxImageBytes"], d.maxImageBytes),
|
|
30554
|
-
maxImagesPerMessage: positive(p["maxImagesPerMessage"], d.maxImagesPerMessage)
|
|
30797
|
+
maxImagesPerMessage: positive(p["maxImagesPerMessage"], d.maxImagesPerMessage),
|
|
30798
|
+
minImageArea: nonNegative(p["minImageArea"], d.minImageArea)
|
|
30555
30799
|
};
|
|
30556
30800
|
}
|
|
30801
|
+
/** 非有限数 / 负数回退默认值,`0` 原样保留 */
|
|
30802
|
+
function nonNegative(value, fallback) {
|
|
30803
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
|
|
30804
|
+
}
|
|
30557
30805
|
/**
|
|
30558
30806
|
* CSP 白名单只收字符串数组;存储被污染时回退默认值(空),
|
|
30559
30807
|
* **不能**把非法值原样带进 CSP —— `viewerCspHeader` 会抛,等于整条投放面瘫掉。
|
|
@@ -31292,6 +31540,9 @@ const chatbotDictionary = defineDictionary({
|
|
|
31292
31540
|
"app.title": "WebSkill Chat",
|
|
31293
31541
|
"header.sessions": "Sessions",
|
|
31294
31542
|
"header.settings": "Settings",
|
|
31543
|
+
"header.dock": "Dock to page",
|
|
31544
|
+
"header.undock": "Undock from page",
|
|
31545
|
+
"header.close": "Close chat",
|
|
31295
31546
|
"header.console": "Open Console",
|
|
31296
31547
|
"header.theme.light": "Switch to light theme",
|
|
31297
31548
|
"header.theme.dark": "Switch to dark theme",
|
|
@@ -31337,10 +31588,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31337
31588
|
"welcome.capability.transparency.title": "Run transparency",
|
|
31338
31589
|
"welcome.capability.transparency.description": "Routing, skill activation, tool calls and results are visible live, step by step.",
|
|
31339
31590
|
"welcome.quickPrompts": "Try an example",
|
|
31340
|
-
"welcome.prompt.1": "What skills are installed in this workspace?",
|
|
31341
|
-
"welcome.prompt.2": "Draft a short product update announcement for me",
|
|
31342
|
-
"welcome.prompt.3": "Show me a bar chart of this quarter’s sales",
|
|
31343
|
-
"welcome.prompt.4": "Explain how WebSkill runs skill scripts safely",
|
|
31344
31591
|
"message.copy": "Copy",
|
|
31345
31592
|
"message.copied": "Copied",
|
|
31346
31593
|
"message.retry": "Retry",
|
|
@@ -31426,11 +31673,20 @@ const chatbotDictionary = defineDictionary({
|
|
|
31426
31673
|
"interaction.pageAction.click": "Allow the assistant to click “{target}”?",
|
|
31427
31674
|
"interaction.pageAction.fill": "Allow the assistant to fill “{target}” with:",
|
|
31428
31675
|
"interaction.pageAction.submit": "Allow the assistant to submit “{target}”?",
|
|
31676
|
+
"interaction.pageAction.select": "Allow the assistant to select an option in “{target}”?",
|
|
31677
|
+
"interaction.pageAction.set": "Allow the assistant to toggle “{target}”?",
|
|
31678
|
+
"interaction.pageAction.attach": "Allow the assistant to attach a file to “{target}”?",
|
|
31429
31679
|
"interaction.pageAction.role.click": "this control",
|
|
31430
31680
|
"interaction.pageAction.role.fill": "this field",
|
|
31431
31681
|
"interaction.pageAction.role.submit": "this form",
|
|
31682
|
+
"interaction.pageAction.role.select": "this list",
|
|
31683
|
+
"interaction.pageAction.role.set": "this switch",
|
|
31684
|
+
"interaction.pageAction.role.attach": "this upload field",
|
|
31685
|
+
"interaction.pageAction.remember": "Don’t ask again for this kind of action",
|
|
31432
31686
|
"interaction.pageAction.frame": "The target is in the embedded frame \"{frame}\".",
|
|
31433
31687
|
"interaction.pageAction.elevated": "This dialog is outside the usual allowlist; it was opened by this task.",
|
|
31688
|
+
"interaction.downloadedFile.file": "{name} · {size}",
|
|
31689
|
+
"interaction.downloadedFile.remember": "Don’t ask again for this kind of access",
|
|
31434
31690
|
"interaction.traceEvidence.title": "Steps this skill claims",
|
|
31435
31691
|
"interaction.traceEvidence.columnDraft": "In the skill",
|
|
31436
31692
|
"interaction.traceEvidence.columnTrace": "Actually run in this session",
|
|
@@ -31495,9 +31751,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31495
31751
|
"composer.noModel.description": "A large language model has not been configured yet. Configure one on the model settings page before chatting.",
|
|
31496
31752
|
"composer.noModel.configure": "Configure model",
|
|
31497
31753
|
"capability.group": "Model capabilities",
|
|
31498
|
-
"composer.pageCapture": "Read the page view",
|
|
31499
|
-
"composer.pageCapture.disabled.setting": "page image capture is off in Settings → Multimodal",
|
|
31500
|
-
"composer.pageCapture.disabled.model": "the selected model does not accept images",
|
|
31501
31754
|
"composer.addAttachment.imagesOff": "images are off in Settings → Multimodal",
|
|
31502
31755
|
"composer.addAttachment.modelNoImages": "the selected model does not accept images",
|
|
31503
31756
|
"attachment.imagesDropped": "Only {limit} images can be sent per message. Not sent: {names}",
|
|
@@ -31648,6 +31901,9 @@ const chatbotDictionary = defineDictionary({
|
|
|
31648
31901
|
"app.title": "WebSkill Chat",
|
|
31649
31902
|
"header.sessions": "会话",
|
|
31650
31903
|
"header.settings": "设置",
|
|
31904
|
+
"header.dock": "固定到页面",
|
|
31905
|
+
"header.undock": "取消固定",
|
|
31906
|
+
"header.close": "关闭对话框",
|
|
31651
31907
|
"header.console": "打开 Console",
|
|
31652
31908
|
"header.theme.light": "切换到亮色主题",
|
|
31653
31909
|
"header.theme.dark": "切换到暗色主题",
|
|
@@ -31693,10 +31949,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31693
31949
|
"welcome.capability.transparency.title": "运行透明",
|
|
31694
31950
|
"welcome.capability.transparency.description": "路由、技能激活、工具调用与结果,逐步实时可见。",
|
|
31695
31951
|
"welcome.quickPrompts": "试试这些示例",
|
|
31696
|
-
"welcome.prompt.1": "这个工作区里安装了哪些技能?",
|
|
31697
|
-
"welcome.prompt.2": "帮我起草一份简短的产品更新公告",
|
|
31698
|
-
"welcome.prompt.3": "给我展示本季度销售额的柱状图",
|
|
31699
|
-
"welcome.prompt.4": "解释一下 WebSkill 如何安全地运行技能脚本",
|
|
31700
31952
|
"message.copy": "复制",
|
|
31701
31953
|
"message.copied": "已复制",
|
|
31702
31954
|
"message.edit": "编辑消息",
|
|
@@ -31782,11 +32034,20 @@ const chatbotDictionary = defineDictionary({
|
|
|
31782
32034
|
"interaction.pageAction.click": "允许助手点击「{target}」吗?",
|
|
31783
32035
|
"interaction.pageAction.fill": "允许助手在「{target}」中填入:",
|
|
31784
32036
|
"interaction.pageAction.submit": "允许助手提交「{target}」吗?",
|
|
32037
|
+
"interaction.pageAction.select": "允许助手在「{target}」中选择选项吗?",
|
|
32038
|
+
"interaction.pageAction.set": "允许助手切换「{target}」吗?",
|
|
32039
|
+
"interaction.pageAction.attach": "允许助手向「{target}」附加文件吗?",
|
|
31785
32040
|
"interaction.pageAction.role.click": "这个控件",
|
|
31786
32041
|
"interaction.pageAction.role.fill": "这个输入框",
|
|
31787
32042
|
"interaction.pageAction.role.submit": "这个表单",
|
|
32043
|
+
"interaction.pageAction.role.select": "这个选择框",
|
|
32044
|
+
"interaction.pageAction.role.set": "这个开关",
|
|
32045
|
+
"interaction.pageAction.role.attach": "这个上传控件",
|
|
32046
|
+
"interaction.pageAction.remember": "以后不再询问此类操作",
|
|
31788
32047
|
"interaction.pageAction.frame": "目标位于嵌入帧「{frame}」中。",
|
|
31789
32048
|
"interaction.pageAction.elevated": "该对话框不在常规允许清单内,是本次任务打开的。",
|
|
32049
|
+
"interaction.downloadedFile.file": "{name} · {size}",
|
|
32050
|
+
"interaction.downloadedFile.remember": "以后不再询问此类访问",
|
|
31790
32051
|
"interaction.traceEvidence.title": "该技能声称的步骤",
|
|
31791
32052
|
"interaction.traceEvidence.columnDraft": "技能里写的",
|
|
31792
32053
|
"interaction.traceEvidence.columnTrace": "本会话实际执行的",
|
|
@@ -31851,9 +32112,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31851
32112
|
"composer.noModel.description": "大模型尚未配置,请先前往大模型配置页面完成配置,再开始对话。",
|
|
31852
32113
|
"composer.noModel.configure": "配置大模型",
|
|
31853
32114
|
"capability.group": "模型能力",
|
|
31854
|
-
"composer.pageCapture": "读取页面画面",
|
|
31855
|
-
"composer.pageCapture.disabled.setting": "页面图像抓取已在「设置 → 多模态」中关闭",
|
|
31856
|
-
"composer.pageCapture.disabled.model": "当前模型不接受图片",
|
|
31857
32115
|
"composer.addAttachment.imagesOff": "图片已在「设置 → 多模态」中关闭",
|
|
31858
32116
|
"composer.addAttachment.modelNoImages": "当前模型不接受图片",
|
|
31859
32117
|
"attachment.imagesDropped": "每条消息最多发送 {limit} 张图片。未发送:{names}",
|
|
@@ -32162,8 +32420,6 @@ function redactInteractionValues(value, request) {
|
|
|
32162
32420
|
}
|
|
32163
32421
|
return out;
|
|
32164
32422
|
}
|
|
32165
|
-
/** 注入模型的单个附件正文上限(超出截断并标注) */
|
|
32166
|
-
const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
|
|
32167
32423
|
/**
|
|
32168
32424
|
* 行为记录与画像归属的 user 作用域标识。chatbot 是单人本地应用,没有账号体系,
|
|
32169
32425
|
* 固定值即可;它只决定 memory 里的作用域名,不参与任何鉴权。
|
|
@@ -32613,6 +32869,17 @@ var ChatEngine = class {
|
|
|
32613
32869
|
this.#maxHistoryMessages = void 0;
|
|
32614
32870
|
}
|
|
32615
32871
|
/**
|
|
32872
|
+
* 技能仓变更后调用(安装/卸载/发布):作废 runtime 缓存的技能目录,下次 send 重新扫描。
|
|
32873
|
+
*
|
|
32874
|
+
* 比 `reloadConfig()` 轻,因为它不清 `#handles`——那会连带丢掉每个会话的模型上下文。
|
|
32875
|
+
* 装技能的界面与对话不在同一个页面时(如扩展的 options 页 vs side panel),
|
|
32876
|
+
* 宿主必须自己把变更通知过来,否则新技能要等页面重载才出现在 catalog 里。
|
|
32877
|
+
* @experimental
|
|
32878
|
+
*/
|
|
32879
|
+
invalidateSkills() {
|
|
32880
|
+
this.#ready?.then((runtime) => runtime.invalidate(), () => void 0);
|
|
32881
|
+
}
|
|
32882
|
+
/**
|
|
32616
32883
|
* 会话列表页。归档会话也返回:UI 自己分区展示。
|
|
32617
32884
|
* `limit` 不给默认值——缺省属于 `SessionStore` 实现,在这里兜底就等于只下推了一半。
|
|
32618
32885
|
*/
|
|
@@ -32787,7 +33054,7 @@ var ChatEngine = class {
|
|
|
32787
33054
|
const rc = await this.#runtimeConfigStore()?.load();
|
|
32788
33055
|
if (!(rc?.multimodal.imageAttachments ?? false)) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Image attachments are turned off. Enable them in settings before sending images.");
|
|
32789
33056
|
if (!entryCapabilities(pickLlmEntry(rc, this.#selectedModelId)).image) throw new WebSkillError("MODEL_IMAGE_UNSUPPORTED", "Current model does not support image input. Switch model or remove the image.");
|
|
32790
|
-
const limit = rc?.multimodal.maxImagesPerMessage ??
|
|
33057
|
+
const limit = rc?.multimodal.maxImagesPerMessage ?? defaultRuntimeConfig().multimodal.maxImagesPerMessage;
|
|
32791
33058
|
if (images.length <= limit) return { accepted: attachments };
|
|
32792
33059
|
const dropped = images.slice(limit);
|
|
32793
33060
|
const droppedIds = new Set(dropped.map((a) => a.id));
|
|
@@ -32811,11 +33078,14 @@ var ChatEngine = class {
|
|
|
32811
33078
|
if (!await this.#adapter.storage.exists(path)) continue;
|
|
32812
33079
|
if (attachment.kind === "text" || attachment.kind === "document-text") {
|
|
32813
33080
|
const body = await this.#adapter.storage.readText(path);
|
|
32814
|
-
const clipped = body.length > ATTACHMENT_TEXT_LIMIT ? `${body.slice(0, ATTACHMENT_TEXT_LIMIT)}\n[truncated]` : body;
|
|
32815
|
-
const label = attachment.kind === "document-text" ? `${attachment.name} (${attachment.contentType}, body text only)` : `${attachment.name} (${attachment.contentType})`;
|
|
32816
33081
|
parts.push({
|
|
32817
33082
|
type: "text",
|
|
32818
|
-
text:
|
|
33083
|
+
text: formatAttachmentText({
|
|
33084
|
+
name: attachment.name,
|
|
33085
|
+
contentType: attachment.contentType,
|
|
33086
|
+
body,
|
|
33087
|
+
kind: attachment.kind
|
|
33088
|
+
})
|
|
32819
33089
|
});
|
|
32820
33090
|
continue;
|
|
32821
33091
|
}
|
|
@@ -33165,16 +33435,30 @@ var ChatEngine = class {
|
|
|
33165
33435
|
})] : [],
|
|
33166
33436
|
...pagePerception ? [createPagePerceptionToolSource({
|
|
33167
33437
|
policy: pagePerception,
|
|
33438
|
+
...this.#adapter.pagePerceptionPaging === void 0 ? {} : { paging: () => this.#adapter.pagePerceptionPaging },
|
|
33168
33439
|
imageCapture: async () => {
|
|
33169
33440
|
const live = await this.#runtimeConfigStore()?.load();
|
|
33170
33441
|
return {
|
|
33171
33442
|
enabled: (live?.multimodal.pageImageCapture ?? false) && entryCapabilities(pickLlmEntry(live, this.#selectedModelId)).image,
|
|
33172
33443
|
maxImageBytes: live?.multimodal.maxImageBytes ?? 0,
|
|
33173
|
-
maxImages: live?.multimodal.maxImagesPerMessage ?? 0
|
|
33444
|
+
maxImages: live?.multimodal.maxImagesPerMessage ?? 0,
|
|
33445
|
+
minImageArea: live?.multimodal.minImageArea ?? 0
|
|
33174
33446
|
};
|
|
33175
33447
|
}
|
|
33176
33448
|
})] : [],
|
|
33177
|
-
...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : []
|
|
33449
|
+
...this.#adapter.pageActions ? [createPageActionToolSource({ policy: this.#adapter.pageActions })] : [],
|
|
33450
|
+
...this.#adapter.downloads ? [createDownloadedFileToolSource({ policy: new DownloadedFilePolicy({
|
|
33451
|
+
reader: this.#adapter.downloads.reader,
|
|
33452
|
+
ui: this.#uiBridge,
|
|
33453
|
+
capabilityEnabled: () => rc?.sandbox.downloadedFiles === true,
|
|
33454
|
+
imageCapable: async () => {
|
|
33455
|
+
return entryCapabilities(pickLlmEntry(await this.#runtimeConfigStore()?.load(), this.#selectedModelId)).image;
|
|
33456
|
+
},
|
|
33457
|
+
...this.#adapter.downloads.consent ? { consent: this.#adapter.downloads.consent } : {},
|
|
33458
|
+
...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
|
|
33459
|
+
...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
|
|
33460
|
+
...this.#adapter.documentAudit ? { audit: this.#adapter.documentAudit } : {}
|
|
33461
|
+
}) })] : []
|
|
33178
33462
|
];
|
|
33179
33463
|
const deps = {
|
|
33180
33464
|
fs,
|
|
@@ -33209,8 +33493,11 @@ var ChatEngine = class {
|
|
|
33209
33493
|
...Object.keys(loopConfig).length > 0 ? { config: loopConfig } : {},
|
|
33210
33494
|
...model ? { model } : {},
|
|
33211
33495
|
...this.#options.fetchData ? { fetchData: this.#options.fetchData } : {},
|
|
33496
|
+
...this.#options.dataSources ? { dataSources: this.#options.dataSources } : {},
|
|
33212
33497
|
...this.#adapter.linkedDocuments ? { linkedDocuments: this.#adapter.linkedDocuments } : {},
|
|
33213
33498
|
...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
|
|
33499
|
+
...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
|
|
33500
|
+
...this.#adapter.pdfExtractor ? { pdfExtractor: this.#adapter.pdfExtractor } : {},
|
|
33214
33501
|
...this.#adapter.documentAudit ? { documentAudit: this.#adapter.documentAudit } : {},
|
|
33215
33502
|
...toolStepStore ? { toolSteps: toolStepStore } : {}
|
|
33216
33503
|
};
|
|
@@ -34129,47 +34416,6 @@ function useWebSkillAui({ messages, live, sessions, currentSessionId, sessionsLo
|
|
|
34129
34416
|
}) });
|
|
34130
34417
|
}
|
|
34131
34418
|
|
|
34132
|
-
//#endregion
|
|
34133
|
-
//#region src/react/quickPromptIcons.ts
|
|
34134
|
-
/**
|
|
34135
|
-
* 快捷指令图标表(0.12.0 分册 21)。
|
|
34136
|
-
* 这张表不导出到包外:宿主只认名字,具体用哪套图标是实现细节。
|
|
34137
|
-
*/
|
|
34138
|
-
const ICONS = {
|
|
34139
|
-
chart: ChartColumn,
|
|
34140
|
-
document: FileText,
|
|
34141
|
-
report: ClipboardList,
|
|
34142
|
-
search: Search,
|
|
34143
|
-
list: List,
|
|
34144
|
-
bug: Bug,
|
|
34145
|
-
test: FlaskConical,
|
|
34146
|
-
metric: Gauge,
|
|
34147
|
-
compare: GitCompare,
|
|
34148
|
-
page: AppWindow,
|
|
34149
|
-
run: Play,
|
|
34150
|
-
warn: TriangleAlert,
|
|
34151
|
-
sparkles: Sparkles,
|
|
34152
|
-
settings: Settings
|
|
34153
|
-
};
|
|
34154
|
-
/** 未知名字每个只提示一次,免得每次渲染都刷屏 */
|
|
34155
|
-
const warned$1 = /* @__PURE__ */ new Set();
|
|
34156
|
-
/**
|
|
34157
|
-
* 解析图标名;取值不在枚举内时返回 undefined(按无图标渲染),
|
|
34158
|
-
* 并在控制台提示一次可用取值——宿主拼错时不至于只看到「图标没出来」。
|
|
34159
|
-
*/
|
|
34160
|
-
function resolveQuickPromptIcon(icon) {
|
|
34161
|
-
if (icon === void 0) return void 0;
|
|
34162
|
-
const resolved = ICONS[icon];
|
|
34163
|
-
if (resolved !== void 0) return resolved;
|
|
34164
|
-
if (!warned$1.has(icon)) {
|
|
34165
|
-
warned$1.add(icon);
|
|
34166
|
-
console.debug("[webskill] unknown quick prompt icon", {
|
|
34167
|
-
icon,
|
|
34168
|
-
available: Object.keys(ICONS)
|
|
34169
|
-
});
|
|
34170
|
-
}
|
|
34171
|
-
}
|
|
34172
|
-
|
|
34173
34419
|
//#endregion
|
|
34174
34420
|
//#region src/react/AssistantUiShell.tsx
|
|
34175
34421
|
/**
|
|
@@ -34411,63 +34657,94 @@ function AssistantUiSessionList({ className = "", onNavigate, onCollapse, hasEar
|
|
|
34411
34657
|
});
|
|
34412
34658
|
}
|
|
34413
34659
|
/** Minimal Base toolbar: workspace controls stay secondary to the thread itself. */
|
|
34414
|
-
function AssistantUiThreadToolbar({ title, onOpenSessions, onCreateSession, onOpenSettings }) {
|
|
34660
|
+
function AssistantUiThreadToolbar({ title, onOpenSessions, onCreateSession, onOpenSettings, docked, onDock, onClose }) {
|
|
34415
34661
|
const t = useT();
|
|
34662
|
+
const dockLabel = docked === true ? t("header.undock") : t("header.dock");
|
|
34416
34663
|
return /* @__PURE__ */ jsxs("header", {
|
|
34417
34664
|
className: "flex h-14 shrink-0 items-center justify-between bg-background px-3",
|
|
34418
34665
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
34419
34666
|
className: "flex min-w-0 items-center gap-1",
|
|
34420
|
-
children: [
|
|
34421
|
-
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
|
|
34428
|
-
|
|
34429
|
-
|
|
34430
|
-
|
|
34431
|
-
|
|
34432
|
-
|
|
34433
|
-
|
|
34434
|
-
|
|
34667
|
+
children: [
|
|
34668
|
+
/* @__PURE__ */ jsx("button", {
|
|
34669
|
+
type: "button",
|
|
34670
|
+
"aria-label": t("session.expand"),
|
|
34671
|
+
title: t("session.expand"),
|
|
34672
|
+
"data-testid": "chatbot-sessions-button",
|
|
34673
|
+
onClick: onOpenSessions,
|
|
34674
|
+
className: "webskill-aui-sessions-button inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34675
|
+
children: /* @__PURE__ */ jsx(PanelLeft, {
|
|
34676
|
+
className: "size-4",
|
|
34677
|
+
"aria-hidden": true
|
|
34678
|
+
})
|
|
34679
|
+
}),
|
|
34680
|
+
onDock ? /* @__PURE__ */ jsx("button", {
|
|
34681
|
+
type: "button",
|
|
34682
|
+
"data-testid": "header-dock",
|
|
34683
|
+
"aria-label": dockLabel,
|
|
34684
|
+
"aria-pressed": docked === true,
|
|
34685
|
+
title: dockLabel,
|
|
34686
|
+
onClick: () => onDock(docked !== true),
|
|
34687
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34688
|
+
children: docked === true ? /* @__PURE__ */ jsx(PinOff, {
|
|
34689
|
+
className: "size-4",
|
|
34690
|
+
"aria-hidden": true
|
|
34691
|
+
}) : /* @__PURE__ */ jsx(Pin, {
|
|
34692
|
+
className: "size-4",
|
|
34693
|
+
"aria-hidden": true
|
|
34694
|
+
})
|
|
34695
|
+
}) : null,
|
|
34696
|
+
title !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
34697
|
+
className: "truncate px-2 text-sm font-medium text-ink",
|
|
34698
|
+
children: title
|
|
34699
|
+
}) : null
|
|
34700
|
+
]
|
|
34435
34701
|
}), /* @__PURE__ */ jsxs("div", {
|
|
34436
34702
|
className: "flex items-center gap-1",
|
|
34437
|
-
children: [
|
|
34438
|
-
|
|
34439
|
-
|
|
34440
|
-
|
|
34441
|
-
|
|
34442
|
-
|
|
34443
|
-
|
|
34444
|
-
|
|
34445
|
-
|
|
34446
|
-
|
|
34447
|
-
|
|
34448
|
-
|
|
34449
|
-
|
|
34450
|
-
|
|
34451
|
-
|
|
34452
|
-
|
|
34453
|
-
|
|
34454
|
-
|
|
34455
|
-
|
|
34456
|
-
className: "size-
|
|
34457
|
-
|
|
34458
|
-
|
|
34459
|
-
|
|
34703
|
+
children: [
|
|
34704
|
+
/* @__PURE__ */ jsx("button", {
|
|
34705
|
+
type: "button",
|
|
34706
|
+
"data-testid": "header-new-chat",
|
|
34707
|
+
"aria-label": t("session.new"),
|
|
34708
|
+
title: t("session.new"),
|
|
34709
|
+
onClick: onCreateSession,
|
|
34710
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34711
|
+
children: /* @__PURE__ */ jsx(Plus, {
|
|
34712
|
+
className: "size-4",
|
|
34713
|
+
"aria-hidden": true
|
|
34714
|
+
})
|
|
34715
|
+
}),
|
|
34716
|
+
onOpenSettings ? /* @__PURE__ */ jsx("button", {
|
|
34717
|
+
type: "button",
|
|
34718
|
+
"data-testid": "header-settings",
|
|
34719
|
+
"aria-label": t("header.settings"),
|
|
34720
|
+
title: t("header.settings"),
|
|
34721
|
+
onClick: onOpenSettings,
|
|
34722
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34723
|
+
children: /* @__PURE__ */ jsx(Settings, {
|
|
34724
|
+
className: "size-4",
|
|
34725
|
+
"aria-hidden": true
|
|
34726
|
+
})
|
|
34727
|
+
}) : null,
|
|
34728
|
+
onClose ? /* @__PURE__ */ jsx("button", {
|
|
34729
|
+
type: "button",
|
|
34730
|
+
"data-testid": "header-close",
|
|
34731
|
+
"aria-label": t("header.close"),
|
|
34732
|
+
title: t("header.close"),
|
|
34733
|
+
onClick: onClose,
|
|
34734
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34735
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
34736
|
+
className: "size-4",
|
|
34737
|
+
"aria-hidden": true
|
|
34738
|
+
})
|
|
34739
|
+
}) : null
|
|
34740
|
+
]
|
|
34460
34741
|
})]
|
|
34461
34742
|
});
|
|
34462
34743
|
}
|
|
34463
|
-
function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
34744
|
+
function AssistantUiEmptyState({ title, prompts, dynamicPrompts, limit, disabled, onPrompt }) {
|
|
34464
34745
|
const t = useT();
|
|
34465
|
-
const
|
|
34466
|
-
|
|
34467
|
-
t("welcome.prompt.2"),
|
|
34468
|
-
t("welcome.prompt.3"),
|
|
34469
|
-
t("welcome.prompt.4")
|
|
34470
|
-
];
|
|
34746
|
+
const locale = useLocale();
|
|
34747
|
+
const starterPrompts = mergeQuickPrompts(dynamicPrompts, prompts ?? [], locale, limit);
|
|
34471
34748
|
return /* @__PURE__ */ jsx("div", {
|
|
34472
34749
|
"data-testid": "assistant-ui-empty-state",
|
|
34473
34750
|
className: "flex min-h-48 flex-col justify-center py-8 sm:min-h-72 sm:py-10",
|
|
@@ -34478,8 +34755,7 @@ function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
|
34478
34755
|
children: title ?? t("welcome.assistantTitle")
|
|
34479
34756
|
}), starterPrompts.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
34480
34757
|
className: "grid w-full grid-cols-1 gap-2 pt-3 sm:grid-cols-2",
|
|
34481
|
-
children: starterPrompts.map((
|
|
34482
|
-
const item = typeof entry === "string" ? { text: entry } : entry;
|
|
34758
|
+
children: starterPrompts.map((item, index) => {
|
|
34483
34759
|
const Icon = resolveQuickPromptIcon(item.icon);
|
|
34484
34760
|
return /* @__PURE__ */ jsxs("button", {
|
|
34485
34761
|
type: "button",
|
|
@@ -34496,6 +34772,11 @@ function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
|
34496
34772
|
})
|
|
34497
34773
|
});
|
|
34498
34774
|
}
|
|
34775
|
+
/**
|
|
34776
|
+
* 对话进行中的动态快捷指令条(0.13.0 FR-17.6)已废止:
|
|
34777
|
+
* 每轮对话结束都在输入区上方冒出一条横向滚动的胶囊带,构成持续干扰。
|
|
34778
|
+
* `ChatbotConfig.dynamicQuickPrompts` 保留,但只在空态(`AssistantUiEmptyState`)生效。
|
|
34779
|
+
*/
|
|
34499
34780
|
|
|
34500
34781
|
//#endregion
|
|
34501
34782
|
//#region src/react/format.ts
|
|
@@ -34649,12 +34930,10 @@ const DEFAULT_DICTATION_LANG = {
|
|
|
34649
34930
|
zh: "zh-CN"
|
|
34650
34931
|
};
|
|
34651
34932
|
/**
|
|
34652
|
-
*
|
|
34653
|
-
* 交互可供性由 interactive 决定——能力是状态,取像是动作。
|
|
34933
|
+
* 两个图标共用一套外观(FR-28.4):明暗只由 supported 决定。
|
|
34654
34934
|
*/
|
|
34655
|
-
function CAPABILITY_ICON_CLASS(supported
|
|
34656
|
-
|
|
34657
|
-
return interactive ? `${base} transition-colors hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:hover:bg-transparent` : base;
|
|
34935
|
+
function CAPABILITY_ICON_CLASS(supported) {
|
|
34936
|
+
return `inline-flex size-8 shrink-0 items-center justify-center rounded-full ${supported ? "text-ink" : "text-muted"}`;
|
|
34658
34937
|
}
|
|
34659
34938
|
function AttachmentMeta({ info }) {
|
|
34660
34939
|
const t = useT();
|
|
@@ -34714,16 +34993,14 @@ function ModelMenuButton({ models }) {
|
|
|
34714
34993
|
}, option.id))
|
|
34715
34994
|
})] });
|
|
34716
34995
|
}
|
|
34717
|
-
function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang,
|
|
34996
|
+
function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, capabilities }) {
|
|
34718
34997
|
const t = useT();
|
|
34719
34998
|
const locale = useLocale();
|
|
34720
34999
|
const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale]);
|
|
34721
35000
|
const dictationReason = dictation.availability.available ? void 0 : t(`composer.dictate.${dictation.availability.reason}`);
|
|
34722
35001
|
const waitReason = disabled ? t("interaction.wait", { target: waitTarget ?? t("interaction.wait.fallback") }) : void 0;
|
|
34723
35002
|
const attachmentReason = attachmentImages === void 0 || attachmentImages.enabled && attachmentImages.imageCapable ? void 0 : attachmentImages.enabled ? t("composer.addAttachment.modelNoImages") : t("composer.addAttachment.imagesOff");
|
|
34724
|
-
const pageCaptureReason = pageCapture === void 0 || pageCapture.enabled && pageCapture.imageCapable ? void 0 : pageCapture.enabled ? t("composer.pageCapture.disabled.model") : t("composer.pageCapture.disabled.setting");
|
|
34725
35003
|
const plainChatHintId = useId();
|
|
34726
|
-
const pageCaptureHintId = useId();
|
|
34727
35004
|
return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
|
|
34728
35005
|
"data-testid": "assistant-ui-composer",
|
|
34729
35006
|
"data-centered": centered,
|
|
@@ -34823,7 +35100,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34823
35100
|
"aria-label": t(capabilities.tools ? "capability.tools.on" : "capability.tools.off"),
|
|
34824
35101
|
title: capabilities.tools ? t("capability.tools.on") : `${t("capability.tools.off")} — ${t("settings.capability.plainChat")}`,
|
|
34825
35102
|
...capabilities.tools ? {} : { "aria-describedby": plainChatHintId },
|
|
34826
|
-
className: CAPABILITY_ICON_CLASS(capabilities.tools
|
|
35103
|
+
className: CAPABILITY_ICON_CLASS(capabilities.tools),
|
|
34827
35104
|
children: capabilities.tools ? /* @__PURE__ */ jsx(Wrench, {
|
|
34828
35105
|
className: "size-4",
|
|
34829
35106
|
"aria-hidden": true
|
|
@@ -34842,7 +35119,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34842
35119
|
"data-supported": capabilities.image,
|
|
34843
35120
|
"aria-label": t(capabilities.image ? "capability.images.on" : "capability.images.off"),
|
|
34844
35121
|
title: t(capabilities.image ? "capability.images.on" : "capability.images.off"),
|
|
34845
|
-
className: CAPABILITY_ICON_CLASS(capabilities.image
|
|
35122
|
+
className: CAPABILITY_ICON_CLASS(capabilities.image),
|
|
34846
35123
|
children: capabilities.image ? /* @__PURE__ */ jsx(Image, {
|
|
34847
35124
|
className: "size-4",
|
|
34848
35125
|
"aria-hidden": true
|
|
@@ -34852,29 +35129,6 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34852
35129
|
})
|
|
34853
35130
|
})
|
|
34854
35131
|
]
|
|
34855
|
-
}) : null,
|
|
34856
|
-
pageCapture ? /* @__PURE__ */ jsx("button", {
|
|
34857
|
-
type: "button",
|
|
34858
|
-
"data-testid": "capability-page-capture",
|
|
34859
|
-
"data-supported": pageCaptureReason === void 0,
|
|
34860
|
-
"aria-label": t("composer.pageCapture"),
|
|
34861
|
-
title: pageCaptureReason === void 0 ? t("composer.pageCapture") : `${t("composer.pageCapture")} — ${pageCaptureReason}`,
|
|
34862
|
-
disabled: pageCaptureReason !== void 0,
|
|
34863
|
-
...pageCaptureReason !== void 0 ? { "aria-describedby": pageCaptureHintId } : {},
|
|
34864
|
-
onClick: pageCapture.onSelect,
|
|
34865
|
-
className: CAPABILITY_ICON_CLASS(pageCaptureReason === void 0, true),
|
|
34866
|
-
children: pageCaptureReason === void 0 ? /* @__PURE__ */ jsx(Monitor, {
|
|
34867
|
-
className: "size-4",
|
|
34868
|
-
"aria-hidden": true
|
|
34869
|
-
}) : /* @__PURE__ */ jsx(MonitorOff, {
|
|
34870
|
-
className: "size-4",
|
|
34871
|
-
"aria-hidden": true
|
|
34872
|
-
})
|
|
34873
|
-
}) : null,
|
|
34874
|
-
pageCapture && pageCaptureReason !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
34875
|
-
id: pageCaptureHintId,
|
|
34876
|
-
className: "sr-only",
|
|
34877
|
-
children: pageCaptureReason
|
|
34878
35132
|
}) : null
|
|
34879
35133
|
]
|
|
34880
35134
|
}), /* @__PURE__ */ jsxs("div", {
|
|
@@ -46537,52 +46791,6 @@ function AssistantUiMessage(props) {
|
|
|
46537
46791
|
|
|
46538
46792
|
//#endregion
|
|
46539
46793
|
//#region src/core/attachmentKind.ts
|
|
46540
|
-
/** 可作为图片分片外发的 MIME 类型 @experimental */
|
|
46541
|
-
const IMAGE_MIME_TYPES = [
|
|
46542
|
-
"image/png",
|
|
46543
|
-
"image/jpeg",
|
|
46544
|
-
"image/webp",
|
|
46545
|
-
"image/gif"
|
|
46546
|
-
];
|
|
46547
|
-
/**
|
|
46548
|
-
* 可按文本读取的扩展名。accept 白名单与判定白名单共用这一份——
|
|
46549
|
-
* 0.5.x 两处不一致,`log`/`yaml`/`yml` 能通过判定却在文件选择器里选不到。
|
|
46550
|
-
*/
|
|
46551
|
-
const TEXT_EXTENSIONS = [
|
|
46552
|
-
"txt",
|
|
46553
|
-
"md",
|
|
46554
|
-
"csv",
|
|
46555
|
-
"json",
|
|
46556
|
-
"log",
|
|
46557
|
-
"yaml",
|
|
46558
|
-
"yml"
|
|
46559
|
-
];
|
|
46560
|
-
const TEXT_EXTENSION_SET = new Set(TEXT_EXTENSIONS);
|
|
46561
|
-
const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
|
|
46562
|
-
/** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) */
|
|
46563
|
-
const FILE_MIME_TYPES = ["application/pdf"];
|
|
46564
|
-
const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
|
|
46565
|
-
/**
|
|
46566
|
-
* 需要**客户端先抽取成文本**才能外发的类型(FR-23.9)。
|
|
46567
|
-
* 不走直通的 `file`:docx 发给 provider 会被当成二进制垃圾。
|
|
46568
|
-
*/
|
|
46569
|
-
const EXTRACTED_MIME_TYPES = ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"];
|
|
46570
|
-
const EXTRACTED_MIME_SET = new Set(EXTRACTED_MIME_TYPES);
|
|
46571
|
-
const EXTRACTED_EXTENSIONS = ["docx"];
|
|
46572
|
-
const EXTRACTED_EXTENSION_SET = new Set(EXTRACTED_EXTENSIONS);
|
|
46573
|
-
function extensionOf(fileName) {
|
|
46574
|
-
return fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
46575
|
-
}
|
|
46576
|
-
/** 分类集中一处,不散落在各 UI 分支里 @experimental */
|
|
46577
|
-
function classifyAttachment(contentType, fileName) {
|
|
46578
|
-
const type = contentType.toLowerCase();
|
|
46579
|
-
if (IMAGE_MIME_SET.has(type)) return "image";
|
|
46580
|
-
if (EXTRACTED_MIME_SET.has(type)) return "document-text";
|
|
46581
|
-
if (FILE_MIME_SET.has(type)) return "file";
|
|
46582
|
-
if (type.startsWith("text/") || type === "application/json") return "text";
|
|
46583
|
-
if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
|
|
46584
|
-
if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
|
|
46585
|
-
}
|
|
46586
46794
|
const TEXT_ACCEPT = [
|
|
46587
46795
|
"text/plain",
|
|
46588
46796
|
"text/markdown",
|
|
@@ -46598,7 +46806,8 @@ function attachmentAccept(options) {
|
|
|
46598
46806
|
const parts = [...TEXT_ACCEPT, ...TEXT_EXTENSIONS.map((ext) => `.${ext}`)];
|
|
46599
46807
|
if (options.images) parts.push(...IMAGE_MIME_TYPES);
|
|
46600
46808
|
if (options.files) parts.push(...FILE_MIME_TYPES);
|
|
46601
|
-
if (options.documents) parts.push(
|
|
46809
|
+
if (options.documents) parts.push(DOCX_MIME, ".docx");
|
|
46810
|
+
if (options.spreadsheets) parts.push(XLSX_MIME, ".xlsx");
|
|
46602
46811
|
return parts.join(",");
|
|
46603
46812
|
}
|
|
46604
46813
|
|
|
@@ -46607,17 +46816,19 @@ function attachmentAccept(options) {
|
|
|
46607
46816
|
/** 非图片附件无法压缩,沿用固定上限;图片走 `RuntimeMultimodalConfig.maxImageBytes` */
|
|
46608
46817
|
const MAX_BYTES = 10 * 1024 * 1024;
|
|
46609
46818
|
const MAX_TEXT_CHARS = 32 * 1024;
|
|
46610
|
-
|
|
46611
|
-
|
|
46612
|
-
pageImageCapture: false,
|
|
46613
|
-
maxImageBytes: MAX_BYTES,
|
|
46614
|
-
maxImagesPerMessage: 5
|
|
46615
|
-
};
|
|
46819
|
+
/** 0.13.0 FR-13.3:兜底值必须与 SDK 默认配置同源,各自写一份字面量必然静默漂移 */
|
|
46820
|
+
const DEFAULT_MULTIMODAL = defaultRuntimeConfig().multimodal;
|
|
46616
46821
|
/** 去掉路径分隔符与 `..`,防止写出附件目录 */
|
|
46617
46822
|
function safeName(name) {
|
|
46618
46823
|
const cleaned = (name.split(/[\\/]/).pop() ?? "attachment").replace(/[^\w.\- ]+/g, "_").replace(/^\.+/, "");
|
|
46619
46824
|
return cleaned === "" ? "attachment" : cleaned.slice(0, 80);
|
|
46620
46825
|
}
|
|
46826
|
+
/** 两种待抽取格式共用 `document-text` 这一种 kind,选抽取器时要按格式再分一次 */
|
|
46827
|
+
function isSpreadsheet(file) {
|
|
46828
|
+
const type = file.type.toLowerCase();
|
|
46829
|
+
if (type !== "") return type === SUPPORTED_DOCUMENT_MIME.xlsx;
|
|
46830
|
+
return file.name.toLowerCase().endsWith(".xlsx");
|
|
46831
|
+
}
|
|
46621
46832
|
/**
|
|
46622
46833
|
* 附件落盘到 `<attachmentsRoot>/<sessionId>/`,`ChatEngine.send` 再按路径读回构造 `LlmContentPart[]`。
|
|
46623
46834
|
* UI 只持有元数据,正文的唯一真相是存储。
|
|
@@ -46634,7 +46845,8 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46634
46845
|
return attachmentAccept({
|
|
46635
46846
|
images: this.#imagesAllowed(),
|
|
46636
46847
|
files: true,
|
|
46637
|
-
documents: this.#options.docxExtractor !== void 0
|
|
46848
|
+
documents: this.#options.docxExtractor !== void 0,
|
|
46849
|
+
spreadsheets: this.#options.xlsxExtractor !== void 0
|
|
46638
46850
|
});
|
|
46639
46851
|
}
|
|
46640
46852
|
#multimodal() {
|
|
@@ -46643,13 +46855,16 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46643
46855
|
#imagesAllowed() {
|
|
46644
46856
|
return this.#multimodal().imageAttachments && (this.#options.imageCapable?.() ?? false);
|
|
46645
46857
|
}
|
|
46858
|
+
#extractorFor(file) {
|
|
46859
|
+
return isSpreadsheet(file) ? this.#options.xlsxExtractor : this.#options.docxExtractor;
|
|
46860
|
+
}
|
|
46646
46861
|
async add({ file }) {
|
|
46647
46862
|
const kind = classifyAttachment(file.type, file.name);
|
|
46648
46863
|
if (kind === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", `Attachment "${file.name}" has an unsupported type "${file.type === "" ? "unknown" : file.type}"`);
|
|
46649
46864
|
if (kind === "image") {
|
|
46650
46865
|
if (!this.#multimodal().imageAttachments) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Image attachments are turned off. Enable them in settings before sending images.");
|
|
46651
46866
|
if (!(this.#options.imageCapable?.() ?? false)) throw new WebSkillError("MODEL_IMAGE_UNSUPPORTED", "Current model does not support image input. Switch model or remove the image.");
|
|
46652
|
-
} else if (kind === "document-text" && this.#
|
|
46867
|
+
} else if (kind === "document-text" && this.#extractorFor(file) === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", `${isSpreadsheet(file) ? "Excel workbooks" : "Word documents"} are not supported in this environment: no ${isSpreadsheet(file) ? "xlsx" : "docx"} extractor is wired up.`);
|
|
46653
46868
|
else if (file.size > MAX_BYTES) throw new WebSkillError("ATTACHMENT_TOO_LARGE", `Attachment "${file.name}" is ${Math.round(file.size / 1024 / 1024)} MB, which exceeds the 10 MB limit`);
|
|
46654
46869
|
const id = `att-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
46655
46870
|
const relative = `${this.#options.sessionId() ?? "pending"}/${id}-${safeName(file.name)}`;
|
|
@@ -46692,8 +46907,8 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46692
46907
|
};
|
|
46693
46908
|
}
|
|
46694
46909
|
if (kind === "document-text") {
|
|
46695
|
-
const extract = this.#
|
|
46696
|
-
if (extract === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "No
|
|
46910
|
+
const extract = this.#extractorFor(file);
|
|
46911
|
+
if (extract === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "No document text extractor is wired up in this environment.");
|
|
46697
46912
|
await this.#options.storage.writeText(path, await extract(new Uint8Array(await file.arrayBuffer())));
|
|
46698
46913
|
return {
|
|
46699
46914
|
...base,
|
|
@@ -47097,18 +47312,43 @@ function pageActionDetails(details) {
|
|
|
47097
47312
|
const record = details;
|
|
47098
47313
|
const action = record["action"];
|
|
47099
47314
|
const target = record["target"];
|
|
47100
|
-
if (
|
|
47315
|
+
if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return void 0;
|
|
47101
47316
|
if (typeof target !== "object" || target === null) return void 0;
|
|
47102
47317
|
const { role, name, frame } = target;
|
|
47103
47318
|
if (typeof role !== "string") return void 0;
|
|
47104
47319
|
const value = record["value"];
|
|
47320
|
+
const rememberLabel = record["rememberLabel"];
|
|
47105
47321
|
return {
|
|
47106
47322
|
action,
|
|
47107
47323
|
role,
|
|
47108
47324
|
...typeof name === "string" ? { name } : {},
|
|
47109
47325
|
...typeof value === "string" ? { value } : {},
|
|
47110
47326
|
...typeof frame === "string" ? { frame } : {},
|
|
47111
|
-
...record["elevated"] === true ? { elevated: true } : {}
|
|
47327
|
+
...record["elevated"] === true ? { elevated: true } : {},
|
|
47328
|
+
...record["rememberable"] === true ? { rememberable: true } : {},
|
|
47329
|
+
...typeof rememberLabel === "string" ? { rememberLabel } : {}
|
|
47330
|
+
};
|
|
47331
|
+
}
|
|
47332
|
+
/**
|
|
47333
|
+
* 下载文件确认卡的载荷(0.14.0 分册 20)。与 `pageActionDetails` 同一条规矩:
|
|
47334
|
+
* 形状不对就整段不渲染,退回通用授权展示,不猜。
|
|
47335
|
+
*/
|
|
47336
|
+
function downloadedFileDetails(details) {
|
|
47337
|
+
if (typeof details !== "object" || details === null) return void 0;
|
|
47338
|
+
const record = details;
|
|
47339
|
+
const action = record["action"];
|
|
47340
|
+
if (action !== "list" && action !== "read") return void 0;
|
|
47341
|
+
const file = record["file"];
|
|
47342
|
+
const rememberLabel = record["rememberLabel"];
|
|
47343
|
+
const named = typeof file === "object" && file !== null ? file : void 0;
|
|
47344
|
+
return {
|
|
47345
|
+
action,
|
|
47346
|
+
...typeof named?.["name"] === "string" && typeof named["size"] === "number" ? { file: {
|
|
47347
|
+
name: named["name"],
|
|
47348
|
+
size: named["size"]
|
|
47349
|
+
} } : {},
|
|
47350
|
+
...record["rememberable"] === true ? { rememberable: true } : {},
|
|
47351
|
+
...typeof rememberLabel === "string" ? { rememberLabel } : {}
|
|
47112
47352
|
};
|
|
47113
47353
|
}
|
|
47114
47354
|
/**
|
|
@@ -47141,8 +47381,10 @@ function traceEvidenceDetails(details) {
|
|
|
47141
47381
|
}
|
|
47142
47382
|
function ActiveInteractionCard({ request, bridge }) {
|
|
47143
47383
|
const t = useT();
|
|
47384
|
+
const [remember, setRemember] = useState(false);
|
|
47144
47385
|
const isAuthorize = request.type === "authorize";
|
|
47145
47386
|
const pageAction = isAuthorize && request.capability === "pageAction" ? pageActionDetails(request.details) : void 0;
|
|
47387
|
+
const downloadedFile = isAuthorize && request.capability === "readDownloadedFile" ? downloadedFileDetails(request.details) : void 0;
|
|
47146
47388
|
const traceEvidence = isAuthorize ? traceEvidenceDetails(request.details) : void 0;
|
|
47147
47389
|
const spec = interactionToUiSpec(request.type === "authorize" && pageAction !== void 0 ? {
|
|
47148
47390
|
...request,
|
|
@@ -47170,7 +47412,8 @@ function ActiveInteractionCard({ request, bridge }) {
|
|
|
47170
47412
|
const value = shapeInteractionValue(interactionToFormModel(request), event.value ?? {});
|
|
47171
47413
|
bridge.resolve({
|
|
47172
47414
|
id: request.id,
|
|
47173
|
-
value
|
|
47415
|
+
value,
|
|
47416
|
+
...remember ? { remembered: true } : {}
|
|
47174
47417
|
});
|
|
47175
47418
|
};
|
|
47176
47419
|
return /* @__PURE__ */ jsxs(motion.div, {
|
|
@@ -47246,8 +47489,33 @@ function ActiveInteractionCard({ request, bridge }) {
|
|
|
47246
47489
|
"data-testid": "interaction-page-action-elevated",
|
|
47247
47490
|
className: "text-sm font-medium text-warning",
|
|
47248
47491
|
children: t("interaction.pageAction.elevated")
|
|
47492
|
+
}) : null,
|
|
47493
|
+
pageAction.rememberable === true ? /* @__PURE__ */ jsxs("label", {
|
|
47494
|
+
className: "flex items-center gap-2 text-sm text-ink",
|
|
47495
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
47496
|
+
type: "checkbox",
|
|
47497
|
+
"data-testid": "interaction-page-action-remember",
|
|
47498
|
+
checked: remember,
|
|
47499
|
+
onChange: (event) => setRemember(event.currentTarget.checked)
|
|
47500
|
+
}), pageAction.rememberLabel ?? t("interaction.pageAction.remember")]
|
|
47249
47501
|
}) : null
|
|
47250
47502
|
] }) : null,
|
|
47503
|
+
downloadedFile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [downloadedFile.file ? /* @__PURE__ */ jsx("p", {
|
|
47504
|
+
"data-testid": "interaction-downloaded-file",
|
|
47505
|
+
className: "text-sm text-ink",
|
|
47506
|
+
children: t("interaction.downloadedFile.file", {
|
|
47507
|
+
name: downloadedFile.file.name,
|
|
47508
|
+
size: formatBytes(downloadedFile.file.size)
|
|
47509
|
+
})
|
|
47510
|
+
}) : null, downloadedFile.rememberable === true ? /* @__PURE__ */ jsxs("label", {
|
|
47511
|
+
className: "flex items-center gap-2 text-sm text-ink",
|
|
47512
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
47513
|
+
type: "checkbox",
|
|
47514
|
+
"data-testid": "interaction-downloaded-file-remember",
|
|
47515
|
+
checked: remember,
|
|
47516
|
+
onChange: (event) => setRemember(event.currentTarget.checked)
|
|
47517
|
+
}), downloadedFile.rememberLabel ?? t("interaction.downloadedFile.remember")]
|
|
47518
|
+
}) : null] }) : null,
|
|
47251
47519
|
traceEvidence ? /* @__PURE__ */ jsxs("div", {
|
|
47252
47520
|
"data-testid": "interaction-trace-evidence",
|
|
47253
47521
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -47666,8 +47934,35 @@ function LocalizedUndoToastProvider({ children }) {
|
|
|
47666
47934
|
});
|
|
47667
47935
|
}
|
|
47668
47936
|
const toErrorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
47669
|
-
/**
|
|
47670
|
-
const
|
|
47937
|
+
/** 与 console 的快捷指令条目同款 id:时间戳 + 随机后缀 */
|
|
47938
|
+
const newQuickPromptId = () => `qp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
47939
|
+
/**
|
|
47940
|
+
* 宿主种子只注入一次(0.13.0 分册 21 FR-21.6)。
|
|
47941
|
+
* 写盘会触发 subscribe 再跑一遍装载,届时 `quickPromptsSeeded` 已为 true,不会二次写入。
|
|
47942
|
+
* 宿主没传种子(`undefined`)时什么都不做——不该为「宿主没配」产生一次持久化;
|
|
47943
|
+
* 传空数组是「本应用不要快捷指令」,照样落盘打标记(FR-25.4)。
|
|
47944
|
+
* 返回已注入的条目(可能是空数组),调用方要立刻拿去更新界面:写盘是 fire-and-forget。
|
|
47945
|
+
*/
|
|
47946
|
+
function seedQuickPrompts(store, config, seed) {
|
|
47947
|
+
if (config.quickPromptsSeeded || seed === void 0) return void 0;
|
|
47948
|
+
const entries = [];
|
|
47949
|
+
for (const item of seed) {
|
|
47950
|
+
const text = normalizeLocalizedText(typeof item === "string" ? item : item.text);
|
|
47951
|
+
if (Object.keys(text).length === 0) continue;
|
|
47952
|
+
const icon = typeof item === "string" ? void 0 : item.icon;
|
|
47953
|
+
entries.push({
|
|
47954
|
+
id: newQuickPromptId(),
|
|
47955
|
+
text,
|
|
47956
|
+
...icon !== void 0 ? { icon } : {}
|
|
47957
|
+
});
|
|
47958
|
+
}
|
|
47959
|
+
store.save({
|
|
47960
|
+
...config,
|
|
47961
|
+
quickPrompts: entries,
|
|
47962
|
+
quickPromptsSeeded: true
|
|
47963
|
+
}).catch(() => void 0);
|
|
47964
|
+
return entries;
|
|
47965
|
+
}
|
|
47671
47966
|
/** 读 artifact 存储并触发浏览器下载(Blob + a[download]) */
|
|
47672
47967
|
async function downloadArtifactFile(storage, chatRoot, file) {
|
|
47673
47968
|
const fullPath = `${chatRoot}/artifacts/${file.runId ?? ""}/${file.path}`;
|
|
@@ -47689,7 +47984,7 @@ async function downloadArtifactFile(storage, chatRoot, file) {
|
|
|
47689
47984
|
* presentation shell and accessible thread primitives.
|
|
47690
47985
|
* @stable
|
|
47691
47986
|
*/
|
|
47692
|
-
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, onEngineReady }) {
|
|
47987
|
+
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, docked, onDock, onClose, onEngineReady }) {
|
|
47693
47988
|
const engine = useMemo(() => new ChatEngine(adapter, {
|
|
47694
47989
|
...config?.chatRoot ? { chatRoot: config.chatRoot } : {},
|
|
47695
47990
|
...config?.llm ? { llm: config.llm } : {},
|
|
@@ -47700,6 +47995,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47700
47995
|
...config?.governance ? { governance: config.governance } : {},
|
|
47701
47996
|
...config?.skillCandidates ? { skillCandidates: config.skillCandidates } : {},
|
|
47702
47997
|
...config?.fetchData ? { fetchData: config.fetchData } : {},
|
|
47998
|
+
...config?.dataSources ? { dataSources: config.dataSources } : {},
|
|
47999
|
+
...config?.executorFactory ? { executorFactory: config.executorFactory } : {},
|
|
47703
48000
|
...rendererProp ? { renderer: rendererProp } : {}
|
|
47704
48001
|
}), [
|
|
47705
48002
|
adapter,
|
|
@@ -47712,6 +48009,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47712
48009
|
config?.governance,
|
|
47713
48010
|
config?.skillCandidates,
|
|
47714
48011
|
config?.fetchData,
|
|
48012
|
+
config?.dataSources,
|
|
48013
|
+
config?.executorFactory,
|
|
47715
48014
|
rendererProp
|
|
47716
48015
|
]);
|
|
47717
48016
|
useEffect(() => {
|
|
@@ -47770,6 +48069,29 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47770
48069
|
});
|
|
47771
48070
|
/** 当前进行中 run 的 part 累积器(流式正文 / 工具 / 生命周期步进的唯一来源) */
|
|
47772
48071
|
const [live, setLive] = useState();
|
|
48072
|
+
/** RuntimeConfig 里快捷指令的镜像(FR-21.5):console 改完经 subscribe 回来 */
|
|
48073
|
+
const [storedQuickPrompts, setStoredQuickPrompts] = useState([]);
|
|
48074
|
+
/** 空态与动态指令条最多展示多少条(console 可改) */
|
|
48075
|
+
const [quickPromptLimit, setQuickPromptLimit] = useState(8);
|
|
48076
|
+
/** 种子的 ref 镜像:props 每次渲染都是新数组,进 effect 依赖表会让配置订阅反复重建 */
|
|
48077
|
+
const seedRef = useRef(config?.quickPrompts);
|
|
48078
|
+
seedRef.current = config?.quickPrompts;
|
|
48079
|
+
/**
|
|
48080
|
+
* 静态清单(FR-21.6 / FR-25.2):存储里有什么就是什么。
|
|
48081
|
+
* 未接配置存储的宿主用 props;接了存储就以存储为准,两者皆空即一张卡都不渲染——
|
|
48082
|
+
* 内置示例已下线(分册 25),chatbot 显示的每一条都要在 console 的快捷指令页里管得到。
|
|
48083
|
+
*/
|
|
48084
|
+
const staticQuickPrompts = useMemo(() => {
|
|
48085
|
+
if (storedQuickPrompts.length > 0) return storedQuickPrompts.map(({ text, icon }) => ({
|
|
48086
|
+
text,
|
|
48087
|
+
...icon !== void 0 ? { icon } : {}
|
|
48088
|
+
}));
|
|
48089
|
+
return configStore === void 0 ? config?.quickPrompts : [];
|
|
48090
|
+
}, [
|
|
48091
|
+
storedQuickPrompts,
|
|
48092
|
+
config?.quickPrompts,
|
|
48093
|
+
configStore
|
|
48094
|
+
]);
|
|
47773
48095
|
const [interactionPending, setInteractionPending] = useState(false);
|
|
47774
48096
|
/** 等待对象名称(interaction-requested 携带):等待指示器要说清在等什么 */
|
|
47775
48097
|
const [interactionLabel, setInteractionLabel] = useState();
|
|
@@ -47823,7 +48145,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47823
48145
|
sessionId: () => sessionRef.current,
|
|
47824
48146
|
multimodal: () => multimodalRef.current,
|
|
47825
48147
|
imageCapable: () => imageCapableRef.current,
|
|
47826
|
-
...adapter.docxExtractor ? { docxExtractor: adapter.docxExtractor } : {}
|
|
48148
|
+
...adapter.docxExtractor ? { docxExtractor: adapter.docxExtractor } : {},
|
|
48149
|
+
...adapter.xlsxExtractor ? { xlsxExtractor: adapter.xlsxExtractor } : {}
|
|
47827
48150
|
}), [adapter, engine]);
|
|
47828
48151
|
useEffect(() => {
|
|
47829
48152
|
let cancelled = false;
|
|
@@ -47850,6 +48173,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47850
48173
|
...rc.llm.defaultId ? { defaultId: rc.llm.defaultId } : {}
|
|
47851
48174
|
});
|
|
47852
48175
|
setLlmSelectionLoaded(true);
|
|
48176
|
+
const seeded = seedQuickPrompts(merged, rc, seedRef.current);
|
|
48177
|
+
setStoredQuickPrompts(seeded ?? rc.quickPrompts);
|
|
48178
|
+
setQuickPromptLimit(rc.quickPromptLimit);
|
|
47853
48179
|
multimodalRef.current = rc.multimodal;
|
|
47854
48180
|
setMultimodal(rc.multimodal);
|
|
47855
48181
|
setLoopLimits({
|
|
@@ -47868,7 +48194,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47868
48194
|
unsubscribe?.();
|
|
47869
48195
|
};
|
|
47870
48196
|
}, [configStore, engine]);
|
|
48197
|
+
const hasBuiltinEntry = llmSelection.entries.some((entry) => entry.provider === "chrome-builtin");
|
|
47871
48198
|
useEffect(() => {
|
|
48199
|
+
if (!hasBuiltinEntry) return void 0;
|
|
47872
48200
|
let cancelled = false;
|
|
47873
48201
|
probeChromeBuiltinAvailability().then((result) => {
|
|
47874
48202
|
if (!cancelled) setChromeBuiltin(result);
|
|
@@ -47876,7 +48204,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47876
48204
|
return () => {
|
|
47877
48205
|
cancelled = true;
|
|
47878
48206
|
};
|
|
47879
|
-
}, []);
|
|
48207
|
+
}, [hasBuiltinEntry]);
|
|
47880
48208
|
/** 选中模型条目 id:能力受限提示、设置面板置灰与附件门槛都以它为准 */
|
|
47881
48209
|
const selectedModelId = modelId ?? llmSelection.defaultId ?? llmSelection.entries[0]?.id ?? "";
|
|
47882
48210
|
const selectedEntry = llmSelection.entries.find((entry) => entry.id === selectedModelId);
|
|
@@ -48087,6 +48415,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48087
48415
|
setCurrentSessionId(id);
|
|
48088
48416
|
setMessages(history.items);
|
|
48089
48417
|
setMessagesCursor(history.nextCursor);
|
|
48418
|
+
setError(void 0);
|
|
48090
48419
|
setModelId(engine.modelId);
|
|
48091
48420
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
48092
48421
|
}, [engine, flushPendingDeletes]);
|
|
@@ -48146,6 +48475,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48146
48475
|
setCurrentSessionId(meta.id);
|
|
48147
48476
|
setMessages([]);
|
|
48148
48477
|
setMessagesCursor(void 0);
|
|
48478
|
+
setError(void 0);
|
|
48149
48479
|
setModelId(engine.modelId);
|
|
48150
48480
|
await refreshSessions();
|
|
48151
48481
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
@@ -48181,6 +48511,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48181
48511
|
if (id === currentSessionId) {
|
|
48182
48512
|
setCurrentSessionId(void 0);
|
|
48183
48513
|
setMessages([]);
|
|
48514
|
+
setError(void 0);
|
|
48184
48515
|
}
|
|
48185
48516
|
await refreshSessions();
|
|
48186
48517
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
@@ -48246,7 +48577,10 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48246
48577
|
...config?.title ? { title: config.title } : {},
|
|
48247
48578
|
onOpenSessions: () => setSessionsOpen(true),
|
|
48248
48579
|
onCreateSession: handleCreate,
|
|
48249
|
-
...onOpenSettings ? { onOpenSettings: () => onOpenSettings() } : {}
|
|
48580
|
+
...onOpenSettings ? { onOpenSettings: () => onOpenSettings() } : {},
|
|
48581
|
+
...docked !== void 0 ? { docked } : {},
|
|
48582
|
+
...onDock ? { onDock } : {},
|
|
48583
|
+
...onClose ? { onClose } : {}
|
|
48250
48584
|
}),
|
|
48251
48585
|
/* @__PURE__ */ jsx(InterruptedBanner, {
|
|
48252
48586
|
engine,
|
|
@@ -48262,7 +48596,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48262
48596
|
children: [
|
|
48263
48597
|
messages.length === 0 && !sending ? /* @__PURE__ */ jsx(AssistantUiEmptyState, {
|
|
48264
48598
|
...config?.title ? { title: config.title } : {},
|
|
48265
|
-
...
|
|
48599
|
+
...staticQuickPrompts ? { prompts: staticQuickPrompts } : {},
|
|
48600
|
+
...config?.dynamicQuickPrompts ? { dynamicPrompts: config.dynamicQuickPrompts } : {},
|
|
48601
|
+
limit: quickPromptLimit,
|
|
48266
48602
|
disabled: sending,
|
|
48267
48603
|
onPrompt: handleSend
|
|
48268
48604
|
}) : null,
|
|
@@ -48343,11 +48679,6 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48343
48679
|
enabled: multimodal.imageAttachments,
|
|
48344
48680
|
imageCapable: modelTakesImages
|
|
48345
48681
|
},
|
|
48346
|
-
pageCapture: {
|
|
48347
|
-
enabled: multimodal.pageImageCapture,
|
|
48348
|
-
imageCapable: modelTakesImages,
|
|
48349
|
-
onSelect: () => handleSend(PAGE_CAPTURE_PROMPT)
|
|
48350
|
-
},
|
|
48351
48682
|
...dictationLang !== "" ? { dictationLang } : {},
|
|
48352
48683
|
...composerModels && !noUsableModel ? { models: composerModels } : {},
|
|
48353
48684
|
...noUsableModel ? { noModel: { onConfigure: () => setNoModelGuideOpen(true) } } : {},
|
|
@@ -48425,7 +48756,7 @@ function VercelPayloadPreview({ bridge }) {
|
|
|
48425
48756
|
* Version of the published `@webskill/chatbot` package, injected at build time.
|
|
48426
48757
|
* @stable
|
|
48427
48758
|
*/
|
|
48428
|
-
const CHATBOT_VERSION = "0.
|
|
48759
|
+
const CHATBOT_VERSION = "0.11.0";
|
|
48429
48760
|
|
|
48430
48761
|
//#endregion
|
|
48431
48762
|
export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, CHATBOT_VERSION, ChatEngine, Chatbot, CompositeUiBridge, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, ErrorCard, InteractionCard, InterruptedBanner, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SkillBadges, SpecInteraction, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
|