@webskill/chatbot 0.9.0 → 0.10.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 +11 -5
- package/dist/index.d.ts +121 -51
- package/dist/index.js +481 -252
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ 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, 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";
|
|
@@ -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
|
}
|
|
@@ -30348,6 +30385,101 @@ const DEFAULT_LOOP_LIMITS$1 = {
|
|
|
30348
30385
|
maxHistoryMessages: 1e3
|
|
30349
30386
|
};
|
|
30350
30387
|
|
|
30388
|
+
//#endregion
|
|
30389
|
+
//#region ../ui-kit/src/runtime-config/quickPrompts.ts
|
|
30390
|
+
/**
|
|
30391
|
+
* 图标表。这张表不进任何包的公开面:宿主只认名字,具体用哪套图标是实现细节。
|
|
30392
|
+
*/
|
|
30393
|
+
const ICONS = {
|
|
30394
|
+
chart: ChartColumn,
|
|
30395
|
+
document: FileText,
|
|
30396
|
+
report: ClipboardList,
|
|
30397
|
+
search: Search,
|
|
30398
|
+
list: List,
|
|
30399
|
+
bug: Bug,
|
|
30400
|
+
test: FlaskConical,
|
|
30401
|
+
metric: Gauge,
|
|
30402
|
+
compare: GitCompare,
|
|
30403
|
+
page: AppWindow,
|
|
30404
|
+
run: Play,
|
|
30405
|
+
warn: TriangleAlert,
|
|
30406
|
+
sparkles: Sparkles,
|
|
30407
|
+
settings: Settings
|
|
30408
|
+
};
|
|
30409
|
+
/** 受控枚举的全部取值(console 的图标下拉数据源) */
|
|
30410
|
+
const QUICK_PROMPT_ICON_NAMES = Object.keys(ICONS);
|
|
30411
|
+
/** 空态网格是两列,8 条即四行(FR-17.4 / D-17-6);用户可在 console 里上调 */
|
|
30412
|
+
const DEFAULT_QUICK_PROMPT_LIMIT = 8;
|
|
30413
|
+
/** 可调范围的天花板:20 条已经把欢迎区撑成十行,再多就满屏都是按钮 */
|
|
30414
|
+
const MAX_QUICK_PROMPT_LIMIT = 20;
|
|
30415
|
+
/** 未知名字每个只提示一次,免得每次渲染都刷屏 */
|
|
30416
|
+
const warned$1 = /* @__PURE__ */ new Set();
|
|
30417
|
+
/**
|
|
30418
|
+
* 解析图标名;取值不在枚举内时返回 undefined(按无图标渲染),
|
|
30419
|
+
* 并在控制台提示一次可用取值——宿主拼错时不至于只看到「图标没出来」。
|
|
30420
|
+
*/
|
|
30421
|
+
function resolveQuickPromptIcon(icon) {
|
|
30422
|
+
if (icon === void 0) return void 0;
|
|
30423
|
+
const resolved = ICONS[icon];
|
|
30424
|
+
if (resolved !== void 0) return resolved;
|
|
30425
|
+
if (!warned$1.has(icon)) {
|
|
30426
|
+
warned$1.add(icon);
|
|
30427
|
+
console.debug("[webskill] unknown quick prompt icon", {
|
|
30428
|
+
icon,
|
|
30429
|
+
available: Object.keys(ICONS)
|
|
30430
|
+
});
|
|
30431
|
+
}
|
|
30432
|
+
}
|
|
30433
|
+
/** 统一成对象形态:宿主可以只给字符串,该字符串填满全部语种(FR-21.1) */
|
|
30434
|
+
const normalize = (entry) => typeof entry === "string" ? { text: normalizeLocalizedText(entry) } : entry;
|
|
30435
|
+
/**
|
|
30436
|
+
* 拼接动态与静态两批快捷指令(FR-17.4)。
|
|
30437
|
+
*
|
|
30438
|
+
* 动态在前——它是上下文相关的,更容易点到的位置留给它。
|
|
30439
|
+
* 去重**只发生在跨界处**:静态清单里与某条动态指令同文案的会让位;
|
|
30440
|
+
* 静态清单**内部**的重复原样保留(0.12.0 AC-21.6:宿主自定义时文案本就无唯一性约束,
|
|
30441
|
+
* 混排 `['A', {text:'A', icon:'bug'}]` 是合法的两张卡)。
|
|
30442
|
+
*
|
|
30443
|
+
* 0.13.0 分册 21 FR-21.4:去重口径是**按 `locale` 解析后的文案**,
|
|
30444
|
+
* 因此同一对条目在一个语种下会合并、在另一个语种下可能各自保留。
|
|
30445
|
+
* 按 `id` 去重不可行:动态清单传的是 `QuickPrompt`,本来就没有 `id`。
|
|
30446
|
+
*
|
|
30447
|
+
* 结果按 `limit` 截断(默认 {@link DEFAULT_QUICK_PROMPT_LIMIT})。
|
|
30448
|
+
*/
|
|
30449
|
+
function mergeQuickPrompts(dynamic, statics, locale, limit = 8) {
|
|
30450
|
+
const resolve = (entry) => {
|
|
30451
|
+
const prompt = normalize(entry);
|
|
30452
|
+
const text = resolveLocalizedText(prompt.text, locale);
|
|
30453
|
+
if (text === void 0) return void 0;
|
|
30454
|
+
return {
|
|
30455
|
+
text,
|
|
30456
|
+
...prompt.icon !== void 0 ? { icon: prompt.icon } : {}
|
|
30457
|
+
};
|
|
30458
|
+
};
|
|
30459
|
+
const head = [];
|
|
30460
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
30461
|
+
for (const entry of dynamic ?? []) {
|
|
30462
|
+
const prompt = resolve(entry);
|
|
30463
|
+
if (prompt === void 0 || claimed.has(prompt.text)) continue;
|
|
30464
|
+
claimed.add(prompt.text);
|
|
30465
|
+
head.push(prompt);
|
|
30466
|
+
}
|
|
30467
|
+
const tail = [];
|
|
30468
|
+
for (const entry of statics) {
|
|
30469
|
+
const prompt = resolve(entry);
|
|
30470
|
+
if (prompt === void 0 || claimed.has(prompt.text)) continue;
|
|
30471
|
+
tail.push(prompt);
|
|
30472
|
+
}
|
|
30473
|
+
return [...head, ...tail].slice(0, clampQuickPromptLimit(limit));
|
|
30474
|
+
}
|
|
30475
|
+
/** 存储里的脏值不能让空态变成空白或一堵墙;非正整数一律回默认 */
|
|
30476
|
+
function clampQuickPromptLimit(value) {
|
|
30477
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 8;
|
|
30478
|
+
const floored = Math.floor(value);
|
|
30479
|
+
if (floored < 1) return 8;
|
|
30480
|
+
return Math.min(floored, 20);
|
|
30481
|
+
}
|
|
30482
|
+
|
|
30351
30483
|
//#endregion
|
|
30352
30484
|
//#region ../ui-kit/src/runtime-config/types.ts
|
|
30353
30485
|
/**
|
|
@@ -30427,7 +30559,8 @@ function defaultRuntimeConfig() {
|
|
|
30427
30559
|
imageAttachments: false,
|
|
30428
30560
|
pageImageCapture: false,
|
|
30429
30561
|
maxImageBytes: 10 * 1024 * 1024,
|
|
30430
|
-
maxImagesPerMessage:
|
|
30562
|
+
maxImagesPerMessage: 50,
|
|
30563
|
+
minImageArea: 1024
|
|
30431
30564
|
},
|
|
30432
30565
|
documentSurface: { enabled: false },
|
|
30433
30566
|
userProfile: {
|
|
@@ -30437,6 +30570,9 @@ function defaultRuntimeConfig() {
|
|
|
30437
30570
|
encrypted: true
|
|
30438
30571
|
},
|
|
30439
30572
|
skillState: { quarantineThreshold: 5 },
|
|
30573
|
+
quickPrompts: [],
|
|
30574
|
+
quickPromptsSeeded: false,
|
|
30575
|
+
quickPromptLimit: 8,
|
|
30440
30576
|
dismissedAutoEntries: []
|
|
30441
30577
|
};
|
|
30442
30578
|
}
|
|
@@ -30521,6 +30657,9 @@ function mergeRuntimeConfigDefaults(partial) {
|
|
|
30521
30657
|
documentSurface: mergeDocumentSurface(p["documentSurface"], d.documentSurface),
|
|
30522
30658
|
userProfile: mergeUserProfile(p["userProfile"], d.userProfile),
|
|
30523
30659
|
skillState: mergeSkillState(p["skillState"], d.skillState),
|
|
30660
|
+
quickPrompts: readQuickPrompts(p["quickPrompts"]),
|
|
30661
|
+
quickPromptsSeeded: p["quickPromptsSeeded"] === true,
|
|
30662
|
+
quickPromptLimit: clampQuickPromptLimit(p["quickPromptLimit"]),
|
|
30524
30663
|
dismissedAutoEntries: readStringList$1(p["dismissedAutoEntries"], [])
|
|
30525
30664
|
};
|
|
30526
30665
|
}
|
|
@@ -30543,6 +30682,35 @@ function readStringList$1(raw, fallback) {
|
|
|
30543
30682
|
if (!Array.isArray(raw)) return [...fallback];
|
|
30544
30683
|
return raw.filter((item) => typeof item === "string");
|
|
30545
30684
|
}
|
|
30685
|
+
/**
|
|
30686
|
+
* 快捷指令逐条校验(FR-17.2):缺 `id` 或文案全语种皆空的条目**单条丢弃**,
|
|
30687
|
+
* 不因为一条脏数据把整段回退——用户其余的配置不该被连坐。
|
|
30688
|
+
* `icon` 取值不在枚举内时抹掉该字段(渲染侧按无图标处理)。
|
|
30689
|
+
*
|
|
30690
|
+
* 0.13.0 分册 25 FR-25.3:`text: string` 的存量条目一并丢弃,不再升级为语种对象——
|
|
30691
|
+
* 它们在 console 的快捷指令页里没有身份可言,留着就是「看得见、删不掉」。
|
|
30692
|
+
*/
|
|
30693
|
+
function readQuickPrompts(raw) {
|
|
30694
|
+
if (!Array.isArray(raw)) return [];
|
|
30695
|
+
const out = [];
|
|
30696
|
+
for (const item of raw) {
|
|
30697
|
+
if (typeof item !== "object" || item === null) continue;
|
|
30698
|
+
const entry = item;
|
|
30699
|
+
const id = typeof entry["id"] === "string" ? entry["id"].trim() : "";
|
|
30700
|
+
const rawText = entry["text"];
|
|
30701
|
+
if (id === "" || typeof rawText !== "object" || rawText === null) continue;
|
|
30702
|
+
const text = normalizeLocalizedText(rawText);
|
|
30703
|
+
if (!hasLocalizedText(text)) continue;
|
|
30704
|
+
const icon = entry["icon"];
|
|
30705
|
+
const valid = typeof icon === "string" && QUICK_PROMPT_ICON_NAMES.includes(icon) ? icon : void 0;
|
|
30706
|
+
out.push({
|
|
30707
|
+
id,
|
|
30708
|
+
text,
|
|
30709
|
+
...valid !== void 0 ? { icon: valid } : {}
|
|
30710
|
+
});
|
|
30711
|
+
}
|
|
30712
|
+
return out;
|
|
30713
|
+
}
|
|
30546
30714
|
/** 上限字段非正数时回退默认值:0 或负数会把整条通道变成永远拒收 */
|
|
30547
30715
|
function mergeMultimodal(raw, d) {
|
|
30548
30716
|
const p = typeof raw === "object" && raw !== null ? raw : {};
|
|
@@ -30551,9 +30719,14 @@ function mergeMultimodal(raw, d) {
|
|
|
30551
30719
|
imageAttachments: typeof p["imageAttachments"] === "boolean" ? p["imageAttachments"] : d.imageAttachments,
|
|
30552
30720
|
pageImageCapture: typeof p["pageImageCapture"] === "boolean" ? p["pageImageCapture"] : d.pageImageCapture,
|
|
30553
30721
|
maxImageBytes: positive(p["maxImageBytes"], d.maxImageBytes),
|
|
30554
|
-
maxImagesPerMessage: positive(p["maxImagesPerMessage"], d.maxImagesPerMessage)
|
|
30722
|
+
maxImagesPerMessage: positive(p["maxImagesPerMessage"], d.maxImagesPerMessage),
|
|
30723
|
+
minImageArea: nonNegative(p["minImageArea"], d.minImageArea)
|
|
30555
30724
|
};
|
|
30556
30725
|
}
|
|
30726
|
+
/** 非有限数 / 负数回退默认值,`0` 原样保留 */
|
|
30727
|
+
function nonNegative(value, fallback) {
|
|
30728
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
|
|
30729
|
+
}
|
|
30557
30730
|
/**
|
|
30558
30731
|
* CSP 白名单只收字符串数组;存储被污染时回退默认值(空),
|
|
30559
30732
|
* **不能**把非法值原样带进 CSP —— `viewerCspHeader` 会抛,等于整条投放面瘫掉。
|
|
@@ -31292,6 +31465,9 @@ const chatbotDictionary = defineDictionary({
|
|
|
31292
31465
|
"app.title": "WebSkill Chat",
|
|
31293
31466
|
"header.sessions": "Sessions",
|
|
31294
31467
|
"header.settings": "Settings",
|
|
31468
|
+
"header.dock": "Dock to page",
|
|
31469
|
+
"header.undock": "Undock from page",
|
|
31470
|
+
"header.close": "Close chat",
|
|
31295
31471
|
"header.console": "Open Console",
|
|
31296
31472
|
"header.theme.light": "Switch to light theme",
|
|
31297
31473
|
"header.theme.dark": "Switch to dark theme",
|
|
@@ -31337,10 +31513,7 @@ const chatbotDictionary = defineDictionary({
|
|
|
31337
31513
|
"welcome.capability.transparency.title": "Run transparency",
|
|
31338
31514
|
"welcome.capability.transparency.description": "Routing, skill activation, tool calls and results are visible live, step by step.",
|
|
31339
31515
|
"welcome.quickPrompts": "Try an example",
|
|
31340
|
-
"welcome.
|
|
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",
|
|
31516
|
+
"welcome.dynamicPrompts": "Suggested for this page",
|
|
31344
31517
|
"message.copy": "Copy",
|
|
31345
31518
|
"message.copied": "Copied",
|
|
31346
31519
|
"message.retry": "Retry",
|
|
@@ -31495,9 +31668,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31495
31668
|
"composer.noModel.description": "A large language model has not been configured yet. Configure one on the model settings page before chatting.",
|
|
31496
31669
|
"composer.noModel.configure": "Configure model",
|
|
31497
31670
|
"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
31671
|
"composer.addAttachment.imagesOff": "images are off in Settings → Multimodal",
|
|
31502
31672
|
"composer.addAttachment.modelNoImages": "the selected model does not accept images",
|
|
31503
31673
|
"attachment.imagesDropped": "Only {limit} images can be sent per message. Not sent: {names}",
|
|
@@ -31648,6 +31818,9 @@ const chatbotDictionary = defineDictionary({
|
|
|
31648
31818
|
"app.title": "WebSkill Chat",
|
|
31649
31819
|
"header.sessions": "会话",
|
|
31650
31820
|
"header.settings": "设置",
|
|
31821
|
+
"header.dock": "固定到页面",
|
|
31822
|
+
"header.undock": "取消固定",
|
|
31823
|
+
"header.close": "关闭对话框",
|
|
31651
31824
|
"header.console": "打开 Console",
|
|
31652
31825
|
"header.theme.light": "切换到亮色主题",
|
|
31653
31826
|
"header.theme.dark": "切换到暗色主题",
|
|
@@ -31693,10 +31866,7 @@ const chatbotDictionary = defineDictionary({
|
|
|
31693
31866
|
"welcome.capability.transparency.title": "运行透明",
|
|
31694
31867
|
"welcome.capability.transparency.description": "路由、技能激活、工具调用与结果,逐步实时可见。",
|
|
31695
31868
|
"welcome.quickPrompts": "试试这些示例",
|
|
31696
|
-
"welcome.
|
|
31697
|
-
"welcome.prompt.2": "帮我起草一份简短的产品更新公告",
|
|
31698
|
-
"welcome.prompt.3": "给我展示本季度销售额的柱状图",
|
|
31699
|
-
"welcome.prompt.4": "解释一下 WebSkill 如何安全地运行技能脚本",
|
|
31869
|
+
"welcome.dynamicPrompts": "当前页面可用的快捷指令",
|
|
31700
31870
|
"message.copy": "复制",
|
|
31701
31871
|
"message.copied": "已复制",
|
|
31702
31872
|
"message.edit": "编辑消息",
|
|
@@ -31851,9 +32021,6 @@ const chatbotDictionary = defineDictionary({
|
|
|
31851
32021
|
"composer.noModel.description": "大模型尚未配置,请先前往大模型配置页面完成配置,再开始对话。",
|
|
31852
32022
|
"composer.noModel.configure": "配置大模型",
|
|
31853
32023
|
"capability.group": "模型能力",
|
|
31854
|
-
"composer.pageCapture": "读取页面画面",
|
|
31855
|
-
"composer.pageCapture.disabled.setting": "页面图像抓取已在「设置 → 多模态」中关闭",
|
|
31856
|
-
"composer.pageCapture.disabled.model": "当前模型不接受图片",
|
|
31857
32024
|
"composer.addAttachment.imagesOff": "图片已在「设置 → 多模态」中关闭",
|
|
31858
32025
|
"composer.addAttachment.modelNoImages": "当前模型不接受图片",
|
|
31859
32026
|
"attachment.imagesDropped": "每条消息最多发送 {limit} 张图片。未发送:{names}",
|
|
@@ -32787,7 +32954,7 @@ var ChatEngine = class {
|
|
|
32787
32954
|
const rc = await this.#runtimeConfigStore()?.load();
|
|
32788
32955
|
if (!(rc?.multimodal.imageAttachments ?? false)) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Image attachments are turned off. Enable them in settings before sending images.");
|
|
32789
32956
|
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 ??
|
|
32957
|
+
const limit = rc?.multimodal.maxImagesPerMessage ?? defaultRuntimeConfig().multimodal.maxImagesPerMessage;
|
|
32791
32958
|
if (images.length <= limit) return { accepted: attachments };
|
|
32792
32959
|
const dropped = images.slice(limit);
|
|
32793
32960
|
const droppedIds = new Set(dropped.map((a) => a.id));
|
|
@@ -33170,7 +33337,8 @@ var ChatEngine = class {
|
|
|
33170
33337
|
return {
|
|
33171
33338
|
enabled: (live?.multimodal.pageImageCapture ?? false) && entryCapabilities(pickLlmEntry(live, this.#selectedModelId)).image,
|
|
33172
33339
|
maxImageBytes: live?.multimodal.maxImageBytes ?? 0,
|
|
33173
|
-
maxImages: live?.multimodal.maxImagesPerMessage ?? 0
|
|
33340
|
+
maxImages: live?.multimodal.maxImagesPerMessage ?? 0,
|
|
33341
|
+
minImageArea: live?.multimodal.minImageArea ?? 0
|
|
33174
33342
|
};
|
|
33175
33343
|
}
|
|
33176
33344
|
})] : [],
|
|
@@ -33211,6 +33379,8 @@ var ChatEngine = class {
|
|
|
33211
33379
|
...this.#options.fetchData ? { fetchData: this.#options.fetchData } : {},
|
|
33212
33380
|
...this.#adapter.linkedDocuments ? { linkedDocuments: this.#adapter.linkedDocuments } : {},
|
|
33213
33381
|
...this.#adapter.docxExtractor ? { docxExtractor: this.#adapter.docxExtractor } : {},
|
|
33382
|
+
...this.#adapter.xlsxExtractor ? { xlsxExtractor: this.#adapter.xlsxExtractor } : {},
|
|
33383
|
+
...this.#adapter.pdfExtractor ? { pdfExtractor: this.#adapter.pdfExtractor } : {},
|
|
33214
33384
|
...this.#adapter.documentAudit ? { documentAudit: this.#adapter.documentAudit } : {},
|
|
33215
33385
|
...toolStepStore ? { toolSteps: toolStepStore } : {}
|
|
33216
33386
|
};
|
|
@@ -34129,47 +34299,6 @@ function useWebSkillAui({ messages, live, sessions, currentSessionId, sessionsLo
|
|
|
34129
34299
|
}) });
|
|
34130
34300
|
}
|
|
34131
34301
|
|
|
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
34302
|
//#endregion
|
|
34174
34303
|
//#region src/react/AssistantUiShell.tsx
|
|
34175
34304
|
/**
|
|
@@ -34411,63 +34540,94 @@ function AssistantUiSessionList({ className = "", onNavigate, onCollapse, hasEar
|
|
|
34411
34540
|
});
|
|
34412
34541
|
}
|
|
34413
34542
|
/** Minimal Base toolbar: workspace controls stay secondary to the thread itself. */
|
|
34414
|
-
function AssistantUiThreadToolbar({ title, onOpenSessions, onCreateSession, onOpenSettings }) {
|
|
34543
|
+
function AssistantUiThreadToolbar({ title, onOpenSessions, onCreateSession, onOpenSettings, docked, onDock, onClose }) {
|
|
34415
34544
|
const t = useT();
|
|
34545
|
+
const dockLabel = docked === true ? t("header.undock") : t("header.dock");
|
|
34416
34546
|
return /* @__PURE__ */ jsxs("header", {
|
|
34417
34547
|
className: "flex h-14 shrink-0 items-center justify-between bg-background px-3",
|
|
34418
34548
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
34419
34549
|
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
|
-
|
|
34550
|
+
children: [
|
|
34551
|
+
/* @__PURE__ */ jsx("button", {
|
|
34552
|
+
type: "button",
|
|
34553
|
+
"aria-label": t("session.expand"),
|
|
34554
|
+
title: t("session.expand"),
|
|
34555
|
+
"data-testid": "chatbot-sessions-button",
|
|
34556
|
+
onClick: onOpenSessions,
|
|
34557
|
+
className: "webskill-aui-sessions-button inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34558
|
+
children: /* @__PURE__ */ jsx(PanelLeft, {
|
|
34559
|
+
className: "size-4",
|
|
34560
|
+
"aria-hidden": true
|
|
34561
|
+
})
|
|
34562
|
+
}),
|
|
34563
|
+
onDock ? /* @__PURE__ */ jsx("button", {
|
|
34564
|
+
type: "button",
|
|
34565
|
+
"data-testid": "header-dock",
|
|
34566
|
+
"aria-label": dockLabel,
|
|
34567
|
+
"aria-pressed": docked === true,
|
|
34568
|
+
title: dockLabel,
|
|
34569
|
+
onClick: () => onDock(docked !== true),
|
|
34570
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34571
|
+
children: docked === true ? /* @__PURE__ */ jsx(PinOff, {
|
|
34572
|
+
className: "size-4",
|
|
34573
|
+
"aria-hidden": true
|
|
34574
|
+
}) : /* @__PURE__ */ jsx(Pin, {
|
|
34575
|
+
className: "size-4",
|
|
34576
|
+
"aria-hidden": true
|
|
34577
|
+
})
|
|
34578
|
+
}) : null,
|
|
34579
|
+
title !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
34580
|
+
className: "truncate px-2 text-sm font-medium text-ink",
|
|
34581
|
+
children: title
|
|
34582
|
+
}) : null
|
|
34583
|
+
]
|
|
34435
34584
|
}), /* @__PURE__ */ jsxs("div", {
|
|
34436
34585
|
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
|
-
|
|
34586
|
+
children: [
|
|
34587
|
+
/* @__PURE__ */ jsx("button", {
|
|
34588
|
+
type: "button",
|
|
34589
|
+
"data-testid": "header-new-chat",
|
|
34590
|
+
"aria-label": t("session.new"),
|
|
34591
|
+
title: t("session.new"),
|
|
34592
|
+
onClick: onCreateSession,
|
|
34593
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34594
|
+
children: /* @__PURE__ */ jsx(Plus, {
|
|
34595
|
+
className: "size-4",
|
|
34596
|
+
"aria-hidden": true
|
|
34597
|
+
})
|
|
34598
|
+
}),
|
|
34599
|
+
onOpenSettings ? /* @__PURE__ */ jsx("button", {
|
|
34600
|
+
type: "button",
|
|
34601
|
+
"data-testid": "header-settings",
|
|
34602
|
+
"aria-label": t("header.settings"),
|
|
34603
|
+
title: t("header.settings"),
|
|
34604
|
+
onClick: onOpenSettings,
|
|
34605
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34606
|
+
children: /* @__PURE__ */ jsx(Settings, {
|
|
34607
|
+
className: "size-4",
|
|
34608
|
+
"aria-hidden": true
|
|
34609
|
+
})
|
|
34610
|
+
}) : null,
|
|
34611
|
+
onClose ? /* @__PURE__ */ jsx("button", {
|
|
34612
|
+
type: "button",
|
|
34613
|
+
"data-testid": "header-close",
|
|
34614
|
+
"aria-label": t("header.close"),
|
|
34615
|
+
title: t("header.close"),
|
|
34616
|
+
onClick: onClose,
|
|
34617
|
+
className: "inline-flex size-8 items-center justify-center rounded-md text-muted hover:bg-subtle hover:text-ink",
|
|
34618
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
34619
|
+
className: "size-4",
|
|
34620
|
+
"aria-hidden": true
|
|
34621
|
+
})
|
|
34622
|
+
}) : null
|
|
34623
|
+
]
|
|
34460
34624
|
})]
|
|
34461
34625
|
});
|
|
34462
34626
|
}
|
|
34463
|
-
function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
34627
|
+
function AssistantUiEmptyState({ title, prompts, dynamicPrompts, limit, disabled, onPrompt }) {
|
|
34464
34628
|
const t = useT();
|
|
34465
|
-
const
|
|
34466
|
-
|
|
34467
|
-
t("welcome.prompt.2"),
|
|
34468
|
-
t("welcome.prompt.3"),
|
|
34469
|
-
t("welcome.prompt.4")
|
|
34470
|
-
];
|
|
34629
|
+
const locale = useLocale();
|
|
34630
|
+
const starterPrompts = mergeQuickPrompts(dynamicPrompts, prompts ?? [], locale, limit);
|
|
34471
34631
|
return /* @__PURE__ */ jsx("div", {
|
|
34472
34632
|
"data-testid": "assistant-ui-empty-state",
|
|
34473
34633
|
className: "flex min-h-48 flex-col justify-center py-8 sm:min-h-72 sm:py-10",
|
|
@@ -34478,8 +34638,7 @@ function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
|
34478
34638
|
children: title ?? t("welcome.assistantTitle")
|
|
34479
34639
|
}), starterPrompts.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
34480
34640
|
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;
|
|
34641
|
+
children: starterPrompts.map((item, index) => {
|
|
34483
34642
|
const Icon = resolveQuickPromptIcon(item.icon);
|
|
34484
34643
|
return /* @__PURE__ */ jsxs("button", {
|
|
34485
34644
|
type: "button",
|
|
@@ -34496,6 +34655,34 @@ function AssistantUiEmptyState({ title, prompts, disabled, onPrompt }) {
|
|
|
34496
34655
|
})
|
|
34497
34656
|
});
|
|
34498
34657
|
}
|
|
34658
|
+
/**
|
|
34659
|
+
* 对话进行中的动态快捷指令条(FR-17.6)。
|
|
34660
|
+
* 只接**动态**那一批:静态清单不随上下文变,常驻只会变成噪声。
|
|
34661
|
+
* 空列表时整条不渲染,DOM 里不留空容器。
|
|
34662
|
+
*/
|
|
34663
|
+
function AssistantUiDynamicPrompts({ prompts, limit, onPrompt }) {
|
|
34664
|
+
const t = useT();
|
|
34665
|
+
const items = mergeQuickPrompts(prompts, [], useLocale(), limit);
|
|
34666
|
+
if (items.length === 0) return null;
|
|
34667
|
+
return /* @__PURE__ */ jsx("div", {
|
|
34668
|
+
"data-testid": "chatbot-dynamic-prompts",
|
|
34669
|
+
"aria-label": t("welcome.dynamicPrompts"),
|
|
34670
|
+
className: "flex gap-2 overflow-x-auto px-4 pb-2",
|
|
34671
|
+
children: items.map((item, index) => {
|
|
34672
|
+
const Icon = resolveQuickPromptIcon(item.icon);
|
|
34673
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
34674
|
+
type: "button",
|
|
34675
|
+
"data-testid": "chatbot-dynamic-prompt",
|
|
34676
|
+
onClick: () => onPrompt(item.text),
|
|
34677
|
+
className: "inline-flex shrink-0 items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-xs text-muted transition-colors hover:bg-subtle hover:text-ink",
|
|
34678
|
+
children: [Icon ? /* @__PURE__ */ jsx(Icon, {
|
|
34679
|
+
className: "size-3.5 shrink-0",
|
|
34680
|
+
"aria-hidden": true
|
|
34681
|
+
}) : null, item.text]
|
|
34682
|
+
}, `${index}:${item.text}`);
|
|
34683
|
+
})
|
|
34684
|
+
});
|
|
34685
|
+
}
|
|
34499
34686
|
|
|
34500
34687
|
//#endregion
|
|
34501
34688
|
//#region src/react/format.ts
|
|
@@ -34649,12 +34836,10 @@ const DEFAULT_DICTATION_LANG = {
|
|
|
34649
34836
|
zh: "zh-CN"
|
|
34650
34837
|
};
|
|
34651
34838
|
/**
|
|
34652
|
-
*
|
|
34653
|
-
* 交互可供性由 interactive 决定——能力是状态,取像是动作。
|
|
34839
|
+
* 两个图标共用一套外观(FR-28.4):明暗只由 supported 决定。
|
|
34654
34840
|
*/
|
|
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;
|
|
34841
|
+
function CAPABILITY_ICON_CLASS(supported) {
|
|
34842
|
+
return `inline-flex size-8 shrink-0 items-center justify-center rounded-full ${supported ? "text-ink" : "text-muted"}`;
|
|
34658
34843
|
}
|
|
34659
34844
|
function AttachmentMeta({ info }) {
|
|
34660
34845
|
const t = useT();
|
|
@@ -34714,16 +34899,14 @@ function ModelMenuButton({ models }) {
|
|
|
34714
34899
|
}, option.id))
|
|
34715
34900
|
})] });
|
|
34716
34901
|
}
|
|
34717
|
-
function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang,
|
|
34902
|
+
function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, capabilities }) {
|
|
34718
34903
|
const t = useT();
|
|
34719
34904
|
const locale = useLocale();
|
|
34720
34905
|
const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale]);
|
|
34721
34906
|
const dictationReason = dictation.availability.available ? void 0 : t(`composer.dictate.${dictation.availability.reason}`);
|
|
34722
34907
|
const waitReason = disabled ? t("interaction.wait", { target: waitTarget ?? t("interaction.wait.fallback") }) : void 0;
|
|
34723
34908
|
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
34909
|
const plainChatHintId = useId();
|
|
34726
|
-
const pageCaptureHintId = useId();
|
|
34727
34910
|
return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
|
|
34728
34911
|
"data-testid": "assistant-ui-composer",
|
|
34729
34912
|
"data-centered": centered,
|
|
@@ -34823,7 +35006,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34823
35006
|
"aria-label": t(capabilities.tools ? "capability.tools.on" : "capability.tools.off"),
|
|
34824
35007
|
title: capabilities.tools ? t("capability.tools.on") : `${t("capability.tools.off")} — ${t("settings.capability.plainChat")}`,
|
|
34825
35008
|
...capabilities.tools ? {} : { "aria-describedby": plainChatHintId },
|
|
34826
|
-
className: CAPABILITY_ICON_CLASS(capabilities.tools
|
|
35009
|
+
className: CAPABILITY_ICON_CLASS(capabilities.tools),
|
|
34827
35010
|
children: capabilities.tools ? /* @__PURE__ */ jsx(Wrench, {
|
|
34828
35011
|
className: "size-4",
|
|
34829
35012
|
"aria-hidden": true
|
|
@@ -34842,7 +35025,7 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34842
35025
|
"data-supported": capabilities.image,
|
|
34843
35026
|
"aria-label": t(capabilities.image ? "capability.images.on" : "capability.images.off"),
|
|
34844
35027
|
title: t(capabilities.image ? "capability.images.on" : "capability.images.off"),
|
|
34845
|
-
className: CAPABILITY_ICON_CLASS(capabilities.image
|
|
35028
|
+
className: CAPABILITY_ICON_CLASS(capabilities.image),
|
|
34846
35029
|
children: capabilities.image ? /* @__PURE__ */ jsx(Image, {
|
|
34847
35030
|
className: "size-4",
|
|
34848
35031
|
"aria-hidden": true
|
|
@@ -34852,29 +35035,6 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
|
|
|
34852
35035
|
})
|
|
34853
35036
|
})
|
|
34854
35037
|
]
|
|
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
35038
|
}) : null
|
|
34879
35039
|
]
|
|
34880
35040
|
}), /* @__PURE__ */ jsxs("div", {
|
|
@@ -46563,13 +46723,13 @@ const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
|
|
|
46563
46723
|
const FILE_MIME_TYPES = ["application/pdf"];
|
|
46564
46724
|
const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
|
|
46565
46725
|
/**
|
|
46566
|
-
* 需要**客户端先抽取成文本**才能外发的类型(FR-23.9)。
|
|
46567
|
-
* 不走直通的 `file`:docx 发给 provider 会被当成二进制垃圾。
|
|
46726
|
+
* 需要**客户端先抽取成文本**才能外发的类型(FR-23.9 / 0.13.0 FR-12.5)。
|
|
46727
|
+
* 不走直通的 `file`:docx / xlsx 发给 provider 会被当成二进制垃圾。
|
|
46568
46728
|
*/
|
|
46569
|
-
const
|
|
46570
|
-
const
|
|
46571
|
-
const
|
|
46572
|
-
const EXTRACTED_EXTENSION_SET = new Set(
|
|
46729
|
+
const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
46730
|
+
const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
46731
|
+
const EXTRACTED_MIME_SET = /* @__PURE__ */ new Set([DOCX_MIME, XLSX_MIME]);
|
|
46732
|
+
const EXTRACTED_EXTENSION_SET = /* @__PURE__ */ new Set(["docx", "xlsx"]);
|
|
46573
46733
|
function extensionOf(fileName) {
|
|
46574
46734
|
return fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
46575
46735
|
}
|
|
@@ -46598,7 +46758,8 @@ function attachmentAccept(options) {
|
|
|
46598
46758
|
const parts = [...TEXT_ACCEPT, ...TEXT_EXTENSIONS.map((ext) => `.${ext}`)];
|
|
46599
46759
|
if (options.images) parts.push(...IMAGE_MIME_TYPES);
|
|
46600
46760
|
if (options.files) parts.push(...FILE_MIME_TYPES);
|
|
46601
|
-
if (options.documents) parts.push(
|
|
46761
|
+
if (options.documents) parts.push(DOCX_MIME, ".docx");
|
|
46762
|
+
if (options.spreadsheets) parts.push(XLSX_MIME, ".xlsx");
|
|
46602
46763
|
return parts.join(",");
|
|
46603
46764
|
}
|
|
46604
46765
|
|
|
@@ -46607,17 +46768,19 @@ function attachmentAccept(options) {
|
|
|
46607
46768
|
/** 非图片附件无法压缩,沿用固定上限;图片走 `RuntimeMultimodalConfig.maxImageBytes` */
|
|
46608
46769
|
const MAX_BYTES = 10 * 1024 * 1024;
|
|
46609
46770
|
const MAX_TEXT_CHARS = 32 * 1024;
|
|
46610
|
-
|
|
46611
|
-
|
|
46612
|
-
pageImageCapture: false,
|
|
46613
|
-
maxImageBytes: MAX_BYTES,
|
|
46614
|
-
maxImagesPerMessage: 5
|
|
46615
|
-
};
|
|
46771
|
+
/** 0.13.0 FR-13.3:兜底值必须与 SDK 默认配置同源,各自写一份字面量必然静默漂移 */
|
|
46772
|
+
const DEFAULT_MULTIMODAL = defaultRuntimeConfig().multimodal;
|
|
46616
46773
|
/** 去掉路径分隔符与 `..`,防止写出附件目录 */
|
|
46617
46774
|
function safeName(name) {
|
|
46618
46775
|
const cleaned = (name.split(/[\\/]/).pop() ?? "attachment").replace(/[^\w.\- ]+/g, "_").replace(/^\.+/, "");
|
|
46619
46776
|
return cleaned === "" ? "attachment" : cleaned.slice(0, 80);
|
|
46620
46777
|
}
|
|
46778
|
+
/** 两种待抽取格式共用 `document-text` 这一种 kind,选抽取器时要按格式再分一次 */
|
|
46779
|
+
function isSpreadsheet(file) {
|
|
46780
|
+
const type = file.type.toLowerCase();
|
|
46781
|
+
if (type !== "") return type === SUPPORTED_DOCUMENT_MIME.xlsx;
|
|
46782
|
+
return file.name.toLowerCase().endsWith(".xlsx");
|
|
46783
|
+
}
|
|
46621
46784
|
/**
|
|
46622
46785
|
* 附件落盘到 `<attachmentsRoot>/<sessionId>/`,`ChatEngine.send` 再按路径读回构造 `LlmContentPart[]`。
|
|
46623
46786
|
* UI 只持有元数据,正文的唯一真相是存储。
|
|
@@ -46634,7 +46797,8 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46634
46797
|
return attachmentAccept({
|
|
46635
46798
|
images: this.#imagesAllowed(),
|
|
46636
46799
|
files: true,
|
|
46637
|
-
documents: this.#options.docxExtractor !== void 0
|
|
46800
|
+
documents: this.#options.docxExtractor !== void 0,
|
|
46801
|
+
spreadsheets: this.#options.xlsxExtractor !== void 0
|
|
46638
46802
|
});
|
|
46639
46803
|
}
|
|
46640
46804
|
#multimodal() {
|
|
@@ -46643,13 +46807,16 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46643
46807
|
#imagesAllowed() {
|
|
46644
46808
|
return this.#multimodal().imageAttachments && (this.#options.imageCapable?.() ?? false);
|
|
46645
46809
|
}
|
|
46810
|
+
#extractorFor(file) {
|
|
46811
|
+
return isSpreadsheet(file) ? this.#options.xlsxExtractor : this.#options.docxExtractor;
|
|
46812
|
+
}
|
|
46646
46813
|
async add({ file }) {
|
|
46647
46814
|
const kind = classifyAttachment(file.type, file.name);
|
|
46648
46815
|
if (kind === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", `Attachment "${file.name}" has an unsupported type "${file.type === "" ? "unknown" : file.type}"`);
|
|
46649
46816
|
if (kind === "image") {
|
|
46650
46817
|
if (!this.#multimodal().imageAttachments) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "Image attachments are turned off. Enable them in settings before sending images.");
|
|
46651
46818
|
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.#
|
|
46819
|
+
} 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
46820
|
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
46821
|
const id = `att-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
46655
46822
|
const relative = `${this.#options.sessionId() ?? "pending"}/${id}-${safeName(file.name)}`;
|
|
@@ -46692,8 +46859,8 @@ var WebSkillAttachmentAdapter = class {
|
|
|
46692
46859
|
};
|
|
46693
46860
|
}
|
|
46694
46861
|
if (kind === "document-text") {
|
|
46695
|
-
const extract = this.#
|
|
46696
|
-
if (extract === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "No
|
|
46862
|
+
const extract = this.#extractorFor(file);
|
|
46863
|
+
if (extract === void 0) throw new WebSkillError("ATTACHMENT_TYPE_REJECTED", "No document text extractor is wired up in this environment.");
|
|
46697
46864
|
await this.#options.storage.writeText(path, await extract(new Uint8Array(await file.arrayBuffer())));
|
|
46698
46865
|
return {
|
|
46699
46866
|
...base,
|
|
@@ -47666,8 +47833,35 @@ function LocalizedUndoToastProvider({ children }) {
|
|
|
47666
47833
|
});
|
|
47667
47834
|
}
|
|
47668
47835
|
const toErrorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
47669
|
-
/**
|
|
47670
|
-
const
|
|
47836
|
+
/** 与 console 的快捷指令条目同款 id:时间戳 + 随机后缀 */
|
|
47837
|
+
const newQuickPromptId = () => `qp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
47838
|
+
/**
|
|
47839
|
+
* 宿主种子只注入一次(0.13.0 分册 21 FR-21.6)。
|
|
47840
|
+
* 写盘会触发 subscribe 再跑一遍装载,届时 `quickPromptsSeeded` 已为 true,不会二次写入。
|
|
47841
|
+
* 宿主没传种子(`undefined`)时什么都不做——不该为「宿主没配」产生一次持久化;
|
|
47842
|
+
* 传空数组是「本应用不要快捷指令」,照样落盘打标记(FR-25.4)。
|
|
47843
|
+
* 返回已注入的条目(可能是空数组),调用方要立刻拿去更新界面:写盘是 fire-and-forget。
|
|
47844
|
+
*/
|
|
47845
|
+
function seedQuickPrompts(store, config, seed) {
|
|
47846
|
+
if (config.quickPromptsSeeded || seed === void 0) return void 0;
|
|
47847
|
+
const entries = [];
|
|
47848
|
+
for (const item of seed) {
|
|
47849
|
+
const text = normalizeLocalizedText(typeof item === "string" ? item : item.text);
|
|
47850
|
+
if (Object.keys(text).length === 0) continue;
|
|
47851
|
+
const icon = typeof item === "string" ? void 0 : item.icon;
|
|
47852
|
+
entries.push({
|
|
47853
|
+
id: newQuickPromptId(),
|
|
47854
|
+
text,
|
|
47855
|
+
...icon !== void 0 ? { icon } : {}
|
|
47856
|
+
});
|
|
47857
|
+
}
|
|
47858
|
+
store.save({
|
|
47859
|
+
...config,
|
|
47860
|
+
quickPrompts: entries,
|
|
47861
|
+
quickPromptsSeeded: true
|
|
47862
|
+
}).catch(() => void 0);
|
|
47863
|
+
return entries;
|
|
47864
|
+
}
|
|
47671
47865
|
/** 读 artifact 存储并触发浏览器下载(Blob + a[download]) */
|
|
47672
47866
|
async function downloadArtifactFile(storage, chatRoot, file) {
|
|
47673
47867
|
const fullPath = `${chatRoot}/artifacts/${file.runId ?? ""}/${file.path}`;
|
|
@@ -47689,7 +47883,7 @@ async function downloadArtifactFile(storage, chatRoot, file) {
|
|
|
47689
47883
|
* presentation shell and accessible thread primitives.
|
|
47690
47884
|
* @stable
|
|
47691
47885
|
*/
|
|
47692
|
-
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, onEngineReady }) {
|
|
47886
|
+
function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, docked, onDock, onClose, onEngineReady }) {
|
|
47693
47887
|
const engine = useMemo(() => new ChatEngine(adapter, {
|
|
47694
47888
|
...config?.chatRoot ? { chatRoot: config.chatRoot } : {},
|
|
47695
47889
|
...config?.llm ? { llm: config.llm } : {},
|
|
@@ -47770,6 +47964,29 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47770
47964
|
});
|
|
47771
47965
|
/** 当前进行中 run 的 part 累积器(流式正文 / 工具 / 生命周期步进的唯一来源) */
|
|
47772
47966
|
const [live, setLive] = useState();
|
|
47967
|
+
/** RuntimeConfig 里快捷指令的镜像(FR-21.5):console 改完经 subscribe 回来 */
|
|
47968
|
+
const [storedQuickPrompts, setStoredQuickPrompts] = useState([]);
|
|
47969
|
+
/** 空态与动态指令条最多展示多少条(console 可改) */
|
|
47970
|
+
const [quickPromptLimit, setQuickPromptLimit] = useState(8);
|
|
47971
|
+
/** 种子的 ref 镜像:props 每次渲染都是新数组,进 effect 依赖表会让配置订阅反复重建 */
|
|
47972
|
+
const seedRef = useRef(config?.quickPrompts);
|
|
47973
|
+
seedRef.current = config?.quickPrompts;
|
|
47974
|
+
/**
|
|
47975
|
+
* 静态清单(FR-21.6 / FR-25.2):存储里有什么就是什么。
|
|
47976
|
+
* 未接配置存储的宿主用 props;接了存储就以存储为准,两者皆空即一张卡都不渲染——
|
|
47977
|
+
* 内置示例已下线(分册 25),chatbot 显示的每一条都要在 console 的快捷指令页里管得到。
|
|
47978
|
+
*/
|
|
47979
|
+
const staticQuickPrompts = useMemo(() => {
|
|
47980
|
+
if (storedQuickPrompts.length > 0) return storedQuickPrompts.map(({ text, icon }) => ({
|
|
47981
|
+
text,
|
|
47982
|
+
...icon !== void 0 ? { icon } : {}
|
|
47983
|
+
}));
|
|
47984
|
+
return configStore === void 0 ? config?.quickPrompts : [];
|
|
47985
|
+
}, [
|
|
47986
|
+
storedQuickPrompts,
|
|
47987
|
+
config?.quickPrompts,
|
|
47988
|
+
configStore
|
|
47989
|
+
]);
|
|
47773
47990
|
const [interactionPending, setInteractionPending] = useState(false);
|
|
47774
47991
|
/** 等待对象名称(interaction-requested 携带):等待指示器要说清在等什么 */
|
|
47775
47992
|
const [interactionLabel, setInteractionLabel] = useState();
|
|
@@ -47823,7 +48040,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47823
48040
|
sessionId: () => sessionRef.current,
|
|
47824
48041
|
multimodal: () => multimodalRef.current,
|
|
47825
48042
|
imageCapable: () => imageCapableRef.current,
|
|
47826
|
-
...adapter.docxExtractor ? { docxExtractor: adapter.docxExtractor } : {}
|
|
48043
|
+
...adapter.docxExtractor ? { docxExtractor: adapter.docxExtractor } : {},
|
|
48044
|
+
...adapter.xlsxExtractor ? { xlsxExtractor: adapter.xlsxExtractor } : {}
|
|
47827
48045
|
}), [adapter, engine]);
|
|
47828
48046
|
useEffect(() => {
|
|
47829
48047
|
let cancelled = false;
|
|
@@ -47850,6 +48068,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
47850
48068
|
...rc.llm.defaultId ? { defaultId: rc.llm.defaultId } : {}
|
|
47851
48069
|
});
|
|
47852
48070
|
setLlmSelectionLoaded(true);
|
|
48071
|
+
const seeded = seedQuickPrompts(merged, rc, seedRef.current);
|
|
48072
|
+
setStoredQuickPrompts(seeded ?? rc.quickPrompts);
|
|
48073
|
+
setQuickPromptLimit(rc.quickPromptLimit);
|
|
47853
48074
|
multimodalRef.current = rc.multimodal;
|
|
47854
48075
|
setMultimodal(rc.multimodal);
|
|
47855
48076
|
setLoopLimits({
|
|
@@ -48087,6 +48308,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48087
48308
|
setCurrentSessionId(id);
|
|
48088
48309
|
setMessages(history.items);
|
|
48089
48310
|
setMessagesCursor(history.nextCursor);
|
|
48311
|
+
setError(void 0);
|
|
48090
48312
|
setModelId(engine.modelId);
|
|
48091
48313
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
48092
48314
|
}, [engine, flushPendingDeletes]);
|
|
@@ -48146,6 +48368,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48146
48368
|
setCurrentSessionId(meta.id);
|
|
48147
48369
|
setMessages([]);
|
|
48148
48370
|
setMessagesCursor(void 0);
|
|
48371
|
+
setError(void 0);
|
|
48149
48372
|
setModelId(engine.modelId);
|
|
48150
48373
|
await refreshSessions();
|
|
48151
48374
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
@@ -48181,6 +48404,7 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48181
48404
|
if (id === currentSessionId) {
|
|
48182
48405
|
setCurrentSessionId(void 0);
|
|
48183
48406
|
setMessages([]);
|
|
48407
|
+
setError(void 0);
|
|
48184
48408
|
}
|
|
48185
48409
|
await refreshSessions();
|
|
48186
48410
|
}).catch((e) => setError({ message: toErrorMessage(e) }));
|
|
@@ -48246,7 +48470,10 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48246
48470
|
...config?.title ? { title: config.title } : {},
|
|
48247
48471
|
onOpenSessions: () => setSessionsOpen(true),
|
|
48248
48472
|
onCreateSession: handleCreate,
|
|
48249
|
-
...onOpenSettings ? { onOpenSettings: () => onOpenSettings() } : {}
|
|
48473
|
+
...onOpenSettings ? { onOpenSettings: () => onOpenSettings() } : {},
|
|
48474
|
+
...docked !== void 0 ? { docked } : {},
|
|
48475
|
+
...onDock ? { onDock } : {},
|
|
48476
|
+
...onClose ? { onClose } : {}
|
|
48250
48477
|
}),
|
|
48251
48478
|
/* @__PURE__ */ jsx(InterruptedBanner, {
|
|
48252
48479
|
engine,
|
|
@@ -48262,7 +48489,9 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48262
48489
|
children: [
|
|
48263
48490
|
messages.length === 0 && !sending ? /* @__PURE__ */ jsx(AssistantUiEmptyState, {
|
|
48264
48491
|
...config?.title ? { title: config.title } : {},
|
|
48265
|
-
...
|
|
48492
|
+
...staticQuickPrompts ? { prompts: staticQuickPrompts } : {},
|
|
48493
|
+
...config?.dynamicQuickPrompts ? { dynamicPrompts: config.dynamicQuickPrompts } : {},
|
|
48494
|
+
limit: quickPromptLimit,
|
|
48266
48495
|
disabled: sending,
|
|
48267
48496
|
onPrompt: handleSend
|
|
48268
48497
|
}) : null,
|
|
@@ -48328,6 +48557,11 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48328
48557
|
} : {},
|
|
48329
48558
|
onLocate: handleLocateInteraction
|
|
48330
48559
|
}),
|
|
48560
|
+
messages.length > 0 && !sending && config?.dynamicQuickPrompts ? /* @__PURE__ */ jsx(AssistantUiDynamicPrompts, {
|
|
48561
|
+
prompts: config.dynamicQuickPrompts,
|
|
48562
|
+
limit: quickPromptLimit,
|
|
48563
|
+
onPrompt: handleSend
|
|
48564
|
+
}) : null,
|
|
48331
48565
|
/* @__PURE__ */ jsx(AssistantUiComposer, {
|
|
48332
48566
|
...modelCapabilities ? { capabilities: {
|
|
48333
48567
|
tools: modelCapabilities.tools,
|
|
@@ -48343,11 +48577,6 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
|
|
|
48343
48577
|
enabled: multimodal.imageAttachments,
|
|
48344
48578
|
imageCapable: modelTakesImages
|
|
48345
48579
|
},
|
|
48346
|
-
pageCapture: {
|
|
48347
|
-
enabled: multimodal.pageImageCapture,
|
|
48348
|
-
imageCapable: modelTakesImages,
|
|
48349
|
-
onSelect: () => handleSend(PAGE_CAPTURE_PROMPT)
|
|
48350
|
-
},
|
|
48351
48580
|
...dictationLang !== "" ? { dictationLang } : {},
|
|
48352
48581
|
...composerModels && !noUsableModel ? { models: composerModels } : {},
|
|
48353
48582
|
...noUsableModel ? { noModel: { onConfigure: () => setNoModelGuideOpen(true) } } : {},
|
|
@@ -48425,7 +48654,7 @@ function VercelPayloadPreview({ bridge }) {
|
|
|
48425
48654
|
* Version of the published `@webskill/chatbot` package, injected at build time.
|
|
48426
48655
|
* @stable
|
|
48427
48656
|
*/
|
|
48428
|
-
const CHATBOT_VERSION = "0.
|
|
48657
|
+
const CHATBOT_VERSION = "0.10.0";
|
|
48429
48658
|
|
|
48430
48659
|
//#endregion
|
|
48431
48660
|
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 };
|