@xmanrui/dsh-im 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/assets/logo-icon.png +0 -0
- package/assets/logo.png +0 -0
- package/lib/client.js +765 -295
- package/lib/index.js +112 -110
- package/package.json +2 -1
- package/plugin-src/client/build.mjs +6 -1
- package/plugin-src/client/channels/dingtalk/api.js +2 -0
- package/plugin-src/client/channels/dingtalk/index.js +67 -14
- package/plugin-src/client/channels/feishu/api.js +2 -0
- package/plugin-src/client/channels/feishu/index.js +70 -22
- package/plugin-src/client/channels/qq/api.js +2 -0
- package/plugin-src/client/channels/qq/index.js +45 -8
- package/plugin-src/client/channels/shared/token-api.js +2 -0
- package/plugin-src/client/channels/shared/token-channel.js +34 -8
- package/plugin-src/client/channels/wecom/api.js +2 -0
- package/plugin-src/client/channels/wecom/index.js +45 -8
- package/plugin-src/client/channels/weixin/api.js +2 -0
- package/plugin-src/client/channels/weixin/index.js +68 -8
- package/plugin-src/client/channels/whatsapp/api.js +2 -0
- package/plugin-src/client/channels/whatsapp/index.js +27 -5
- package/plugin-src/client/i18n.js +15 -1
- package/plugin-src/client/index.js +28 -1
- package/plugin-src/client/styles.js +32 -9
- package/plugin-src/client/workspace-editor.js +96 -0
- package/plugin-src/client/workspace-snapshot-fence.js +28 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +34 -11
- package/plugin-src/host/channels/dingtalk/rpc.mjs +17 -2
- package/plugin-src/host/channels/feishu/production.mjs +42 -5
- package/plugin-src/host/channels/feishu/rpc.mjs +17 -2
- package/plugin-src/host/channels/qq/production.mjs +48 -22
- package/plugin-src/host/channels/qq/rpc.mjs +16 -2
- package/plugin-src/host/channels/shared/production.mjs +48 -22
- package/plugin-src/host/channels/shared/rpc.mjs +15 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +25 -0
- package/plugin-src/host/channels/slack/production.mjs +48 -23
- package/plugin-src/host/channels/slack/rpc.mjs +16 -0
- package/plugin-src/host/channels/wecom/production.mjs +49 -23
- package/plugin-src/host/channels/wecom/rpc.mjs +16 -2
- package/plugin-src/host/channels/weixin/production.mjs +31 -11
- package/plugin-src/host/channels/weixin/rpc.mjs +21 -2
- package/plugin-src/host/channels/whatsapp/production.mjs +49 -23
- package/plugin-src/host/channels/whatsapp/rpc.mjs +16 -2
- package/src/channels/dingtalk/dingtalk-bridge.mjs +22 -11
- package/src/channels/dingtalk/dingtalk-controller.mjs +6 -1
- package/src/channels/dingtalk/harness-client.mjs +4 -3
- package/src/channels/dingtalk/state-store.mjs +5 -0
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/feishu/bridge.mjs +36 -16
- package/src/channels/feishu/harness-client.mjs +6 -5
- package/src/channels/feishu/state-store.mjs +5 -0
- package/src/channels/qq/qq-bridge.mjs +25 -15
- package/src/channels/qq/qq-controller.mjs +8 -1
- package/src/channels/qq/state-store.mjs +5 -0
- package/src/channels/shared/bot-workspace-store.mjs +584 -0
- package/src/channels/shared/conversation-state-store.mjs +5 -0
- package/src/channels/shared/text-harness-bridge.mjs +23 -13
- package/src/channels/shared/workspace-command.mjs +26 -0
- package/src/channels/shared/workspace-session.mjs +44 -0
- package/src/channels/wecom/state-store.mjs +5 -0
- package/src/channels/wecom/wecom-bridge.mjs +22 -13
- package/src/channels/wecom/wecom-controller.mjs +8 -1
- package/src/channels/weixin/harness-client.mjs +6 -5
- package/src/channels/weixin/state-store.mjs +5 -0
- package/src/channels/weixin/weixin-api.mjs +1 -1
- package/src/channels/weixin/weixin-bridge.mjs +16 -6
- package/src/channels/weixin/weixin-controller.mjs +6 -1
- package/src/channels/whatsapp/whatsapp-controller.mjs +8 -1
package/lib/client.js
CHANGED
|
@@ -40,7 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
name: () => name
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(index_exports);
|
|
43
|
-
var
|
|
43
|
+
var React15 = __toESM(require("react"), 1);
|
|
44
44
|
|
|
45
45
|
// plugin-src/client/channel-logos.js
|
|
46
46
|
var React = __toESM(require("react"), 1);
|
|
@@ -181,7 +181,8 @@ var DINGTALK_ENDPOINTS = Object.freeze({
|
|
|
181
181
|
cancelProvisioning: "provision.cancel",
|
|
182
182
|
bindCredentials: "bot.bind-credentials",
|
|
183
183
|
reconnectBot: "bot.reconnect",
|
|
184
|
-
deleteBot: "bot.delete"
|
|
184
|
+
deleteBot: "bot.delete",
|
|
185
|
+
setWorkspace: "bot.workspace.set"
|
|
185
186
|
});
|
|
186
187
|
var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
|
187
188
|
var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
|
|
@@ -301,6 +302,7 @@ function normalizeBot(value) {
|
|
|
301
302
|
state,
|
|
302
303
|
connected,
|
|
303
304
|
configured: value.configured !== false,
|
|
305
|
+
workspace: optionalString(value.workspace, 4096) ?? "",
|
|
304
306
|
bot: {
|
|
305
307
|
name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
|
|
306
308
|
clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
|
|
@@ -353,7 +355,7 @@ function formatRemaining(milliseconds) {
|
|
|
353
355
|
}
|
|
354
356
|
|
|
355
357
|
// plugin-src/client/channels/dingtalk/index.js
|
|
356
|
-
var
|
|
358
|
+
var React6 = __toESM(require("react"), 1);
|
|
357
359
|
|
|
358
360
|
// plugin-src/client/credential-binding.js
|
|
359
361
|
var React3 = __toESM(require("react"), 1);
|
|
@@ -366,7 +368,8 @@ var EN = Object.freeze({
|
|
|
366
368
|
"IM\u673A\u5668\u4EBA": "IM bots",
|
|
367
369
|
"IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings",
|
|
368
370
|
"IM \u6E20\u9053": "IM channels",
|
|
369
|
-
"\
|
|
371
|
+
"\u8BA9\u804A\u5929\u673A\u5668\u4EBA\u8F7B\u677E\u63A5\u5165 DeepSeek Harness": "Connect IM bots to DeepSeek Harness with ease",
|
|
372
|
+
"\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub": "Help & feedback \xB7 Open GitHub",
|
|
370
373
|
"\u5FAE\u4FE1": "WeChat",
|
|
371
374
|
"\u98DE\u4E66": "Feishu",
|
|
372
375
|
"\u9489\u9489": "DingTalk",
|
|
@@ -402,6 +405,19 @@ var EN = Object.freeze({
|
|
|
402
405
|
"\u5DF2\u65AD\u5F00": "Disconnected",
|
|
403
406
|
"\u6D88\u606F\u901A\u9053": "Message channel",
|
|
404
407
|
"\u6700\u8FD1\u68C0\u67E5": "Last checked",
|
|
408
|
+
"\u5F53\u524D\u5DE5\u4F5C\u533A": "Current workspace",
|
|
409
|
+
"\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84": "Absolute workspace path",
|
|
410
|
+
"/\u7EDD\u5BF9\u8DEF\u5F84/\u5230/\u5DE5\u4F5C\u533A": "/absolute/path/to/workspace",
|
|
411
|
+
"\u4FEE\u6539": "Change",
|
|
412
|
+
"\u4FDD\u5B58": "Save",
|
|
413
|
+
"\u4FDD\u5B58\u4E2D\u2026": "Saving\u2026",
|
|
414
|
+
"\u672A\u8BBE\u7F6E": "Not set",
|
|
415
|
+
"\u5DE5\u4F5C\u533A\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the workspace. Try again.",
|
|
416
|
+
"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002": "Enter an absolute workspace path.",
|
|
417
|
+
"\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002": "The workspace must be an absolute path.",
|
|
418
|
+
"\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002": "The workspace path does not exist.",
|
|
419
|
+
"\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002": "The workspace path must point to a directory.",
|
|
420
|
+
"\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002": "The bot could not be found.",
|
|
405
421
|
"\u5C1A\u672A\u68C0\u67E5": "Not checked yet",
|
|
406
422
|
"\u521A\u521A": "Just now",
|
|
407
423
|
"\u68C0\u67E5\u8FDE\u63A5": "Check connection",
|
|
@@ -946,6 +962,132 @@ function CredentialBindingPanel({
|
|
|
946
962
|
);
|
|
947
963
|
}
|
|
948
964
|
|
|
965
|
+
// plugin-src/client/workspace-editor.js
|
|
966
|
+
var React4 = __toESM(require("react"), 1);
|
|
967
|
+
function looksLikeAbsolutePath(value) {
|
|
968
|
+
return value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || /^\\\\/.test(value);
|
|
969
|
+
}
|
|
970
|
+
function WorkspaceEditor({ workspace, disabled = false, onSave }) {
|
|
971
|
+
const [editing, setEditing] = React4.useState(false);
|
|
972
|
+
const [draft, setDraft] = React4.useState(workspace ?? "");
|
|
973
|
+
const [saving, setSaving] = React4.useState(false);
|
|
974
|
+
const [error, setError] = React4.useState(null);
|
|
975
|
+
const inputRef = React4.useRef(null);
|
|
976
|
+
const editButtonRef = React4.useRef(null);
|
|
977
|
+
const restoreFocus = React4.useRef(false);
|
|
978
|
+
React4.useEffect(() => {
|
|
979
|
+
if (!editing) setDraft(workspace ?? "");
|
|
980
|
+
}, [workspace, editing]);
|
|
981
|
+
React4.useEffect(() => {
|
|
982
|
+
if (editing) inputRef.current?.focus();
|
|
983
|
+
else if (restoreFocus.current) {
|
|
984
|
+
restoreFocus.current = false;
|
|
985
|
+
editButtonRef.current?.focus();
|
|
986
|
+
}
|
|
987
|
+
}, [editing]);
|
|
988
|
+
const cancel = () => {
|
|
989
|
+
setDraft(workspace ?? "");
|
|
990
|
+
setError(null);
|
|
991
|
+
restoreFocus.current = true;
|
|
992
|
+
setEditing(false);
|
|
993
|
+
};
|
|
994
|
+
const submit = async (event) => {
|
|
995
|
+
event.preventDefault();
|
|
996
|
+
const value = draft.trim();
|
|
997
|
+
if (!value || saving || disabled) return;
|
|
998
|
+
if (!looksLikeAbsolutePath(value)) {
|
|
999
|
+
setError("\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002");
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
setSaving(true);
|
|
1003
|
+
setError(null);
|
|
1004
|
+
try {
|
|
1005
|
+
await onSave?.(value);
|
|
1006
|
+
restoreFocus.current = true;
|
|
1007
|
+
setEditing(false);
|
|
1008
|
+
} catch (cause) {
|
|
1009
|
+
setError(cause?.message ?? "\u5DE5\u4F5C\u533A\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
1010
|
+
} finally {
|
|
1011
|
+
setSaving(false);
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
return h2(
|
|
1015
|
+
"div",
|
|
1016
|
+
{ className: "dim-workspace" },
|
|
1017
|
+
h2(
|
|
1018
|
+
"div",
|
|
1019
|
+
{ className: "dim-workspaceHeader" },
|
|
1020
|
+
h2("span", null, "\u5F53\u524D\u5DE5\u4F5C\u533A"),
|
|
1021
|
+
editing ? null : h2("button", {
|
|
1022
|
+
type: "button",
|
|
1023
|
+
ref: editButtonRef,
|
|
1024
|
+
className: "dim-workspaceEdit",
|
|
1025
|
+
onClick: () => {
|
|
1026
|
+
setEditing(true);
|
|
1027
|
+
setError(null);
|
|
1028
|
+
},
|
|
1029
|
+
disabled
|
|
1030
|
+
}, "\u4FEE\u6539")
|
|
1031
|
+
),
|
|
1032
|
+
editing ? h2(
|
|
1033
|
+
"form",
|
|
1034
|
+
{ className: "dim-workspaceForm", onSubmit: submit },
|
|
1035
|
+
h2("input", {
|
|
1036
|
+
ref: inputRef,
|
|
1037
|
+
value: draft,
|
|
1038
|
+
onChange: (event) => setDraft(event.target.value),
|
|
1039
|
+
placeholder: "/\u7EDD\u5BF9\u8DEF\u5F84/\u5230/\u5DE5\u4F5C\u533A",
|
|
1040
|
+
"aria-label": "\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84",
|
|
1041
|
+
autoCapitalize: "none",
|
|
1042
|
+
autoCorrect: "off",
|
|
1043
|
+
spellCheck: false,
|
|
1044
|
+
maxLength: 4096,
|
|
1045
|
+
required: true,
|
|
1046
|
+
disabled: saving || disabled
|
|
1047
|
+
}),
|
|
1048
|
+
h2(
|
|
1049
|
+
"div",
|
|
1050
|
+
{ className: "dim-workspaceActions" },
|
|
1051
|
+
h2("button", {
|
|
1052
|
+
type: "submit",
|
|
1053
|
+
disabled: saving || disabled || !draft.trim()
|
|
1054
|
+
}, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"),
|
|
1055
|
+
h2("button", { type: "button", onClick: cancel, disabled: saving || disabled }, "\u53D6\u6D88")
|
|
1056
|
+
),
|
|
1057
|
+
error ? h2("p", { className: "dim-workspaceError", role: "alert" }, error) : null
|
|
1058
|
+
) : workspace ? React4.createElement("code", {
|
|
1059
|
+
className: "dim-workspacePath",
|
|
1060
|
+
title: workspace
|
|
1061
|
+
}, workspace) : h2("code", { className: "dim-workspacePath" }, "\u672A\u8BBE\u7F6E")
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// plugin-src/client/workspace-snapshot-fence.js
|
|
1066
|
+
var React5 = __toESM(require("react"), 1);
|
|
1067
|
+
function useWorkspaceSnapshotFence() {
|
|
1068
|
+
const state = React5.useRef({ version: 0, pendingMutations: 0 });
|
|
1069
|
+
return React5.useMemo(() => Object.freeze({
|
|
1070
|
+
beginStatus() {
|
|
1071
|
+
return state.current.pendingMutations === 0 ? state.current.version : null;
|
|
1072
|
+
},
|
|
1073
|
+
canCommitStatus(version) {
|
|
1074
|
+
return version !== null && state.current.pendingMutations === 0 && state.current.version === version;
|
|
1075
|
+
},
|
|
1076
|
+
beginMutation() {
|
|
1077
|
+
state.current.pendingMutations += 1;
|
|
1078
|
+
state.current.version += 1;
|
|
1079
|
+
return state.current.version;
|
|
1080
|
+
},
|
|
1081
|
+
canCommitMutation(version) {
|
|
1082
|
+
return state.current.version === version;
|
|
1083
|
+
},
|
|
1084
|
+
endMutation() {
|
|
1085
|
+
state.current.pendingMutations = Math.max(0, state.current.pendingMutations - 1);
|
|
1086
|
+
return state.current.pendingMutations === 0;
|
|
1087
|
+
}
|
|
1088
|
+
}), []);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
949
1091
|
// plugin-src/client/channels/dingtalk/styles.js
|
|
950
1092
|
var DINGTALK_STYLE_ID = "xmanrui-dsh-dingtalk-settings";
|
|
951
1093
|
var CSS = String.raw`
|
|
@@ -1104,7 +1246,7 @@ function DingtalkIcon({ size = 28 }) {
|
|
|
1104
1246
|
d: "M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498"
|
|
1105
1247
|
}));
|
|
1106
1248
|
}
|
|
1107
|
-
var Button =
|
|
1249
|
+
var Button = React6.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
1108
1250
|
return h2("button", {
|
|
1109
1251
|
...props,
|
|
1110
1252
|
ref,
|
|
@@ -1200,13 +1342,13 @@ function EmptyView({ busy, onStart }) {
|
|
|
1200
1342
|
);
|
|
1201
1343
|
}
|
|
1202
1344
|
function QrPanel({ provision, now, busy, onRefresh, onCancel }) {
|
|
1203
|
-
const [imageFailed, setImageFailed] =
|
|
1345
|
+
const [imageFailed, setImageFailed] = React6.useState(false);
|
|
1204
1346
|
const source = safeQrSource(provision.qrCodeDataUrl);
|
|
1205
1347
|
const remaining = Math.max(0, provision.expiresAt - now);
|
|
1206
1348
|
const expired = remaining === 0 || provision.status === "expired";
|
|
1207
1349
|
const duration = Math.max(1, provision.durationMs ?? 10 * 6e4);
|
|
1208
1350
|
const progress = Math.round(Math.min(1, remaining / duration) * 100);
|
|
1209
|
-
|
|
1351
|
+
React6.useEffect(() => setImageFailed(false), [source]);
|
|
1210
1352
|
return h2(
|
|
1211
1353
|
"div",
|
|
1212
1354
|
{ className: "ddt-card dim-surfaceCard" },
|
|
@@ -1323,8 +1465,8 @@ function checkedTime(value) {
|
|
|
1323
1465
|
}
|
|
1324
1466
|
}
|
|
1325
1467
|
function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
|
|
1326
|
-
const cancelRef =
|
|
1327
|
-
|
|
1468
|
+
const cancelRef = React6.useRef(null);
|
|
1469
|
+
React6.useEffect(() => cancelRef.current?.focus(), []);
|
|
1328
1470
|
return h2(
|
|
1329
1471
|
"div",
|
|
1330
1472
|
{
|
|
@@ -1354,6 +1496,7 @@ function AccountCard({
|
|
|
1354
1496
|
busy,
|
|
1355
1497
|
removing,
|
|
1356
1498
|
onReconnect,
|
|
1499
|
+
onWorkspaceSave,
|
|
1357
1500
|
onRequestRemove,
|
|
1358
1501
|
onConfirmRemove,
|
|
1359
1502
|
onCancelRemove
|
|
@@ -1405,6 +1548,11 @@ function AccountCard({
|
|
|
1405
1548
|
h2("dd", null, checkedTime(account.health.lastCheckedAt))
|
|
1406
1549
|
)
|
|
1407
1550
|
),
|
|
1551
|
+
h2(WorkspaceEditor, {
|
|
1552
|
+
workspace: account.workspace,
|
|
1553
|
+
disabled: Boolean(busy),
|
|
1554
|
+
onSave: onWorkspaceSave
|
|
1555
|
+
}),
|
|
1408
1556
|
h2(
|
|
1409
1557
|
"div",
|
|
1410
1558
|
{ className: "ddt-accountFooter dim-cardFooter" },
|
|
@@ -1450,6 +1598,7 @@ function AccountList(props) {
|
|
|
1450
1598
|
busy: props.busyByBot[account.botId],
|
|
1451
1599
|
removing: props.removeTarget === account.botId,
|
|
1452
1600
|
onReconnect: () => props.onReconnect(account),
|
|
1601
|
+
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
|
1453
1602
|
onRequestRemove: () => props.onRequestRemove(account),
|
|
1454
1603
|
onConfirmRemove: () => props.onConfirmRemove(account),
|
|
1455
1604
|
onCancelRemove: props.onCancelRemove
|
|
@@ -1459,27 +1608,28 @@ function AccountList(props) {
|
|
|
1459
1608
|
}
|
|
1460
1609
|
var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
|
|
1461
1610
|
function DingtalkSettingsTab({ rpcCall }) {
|
|
1462
|
-
const [model, setModel] =
|
|
1611
|
+
const [model, setModel] = React6.useState({
|
|
1463
1612
|
phase: "loading",
|
|
1464
1613
|
bots: [],
|
|
1465
1614
|
totals: EMPTY_TOTALS,
|
|
1466
1615
|
revision: 0,
|
|
1467
1616
|
error: null
|
|
1468
1617
|
});
|
|
1469
|
-
const [provision, setProvision] =
|
|
1470
|
-
const [busy, setBusy] =
|
|
1471
|
-
const [busyByBot, setBusyByBot] =
|
|
1472
|
-
const [removeTarget, setRemoveTarget] =
|
|
1473
|
-
const [credentialOpen, setCredentialOpen] =
|
|
1474
|
-
const [credentialError, setCredentialError] =
|
|
1475
|
-
const [notice, setNotice] =
|
|
1476
|
-
const [now, setNow] =
|
|
1477
|
-
const addButtonRef =
|
|
1478
|
-
const mountedRef =
|
|
1479
|
-
const statusRequestRef =
|
|
1480
|
-
const
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1618
|
+
const [provision, setProvision] = React6.useState(null);
|
|
1619
|
+
const [busy, setBusy] = React6.useState(false);
|
|
1620
|
+
const [busyByBot, setBusyByBot] = React6.useState({});
|
|
1621
|
+
const [removeTarget, setRemoveTarget] = React6.useState(null);
|
|
1622
|
+
const [credentialOpen, setCredentialOpen] = React6.useState(false);
|
|
1623
|
+
const [credentialError, setCredentialError] = React6.useState(null);
|
|
1624
|
+
const [notice, setNotice] = React6.useState("");
|
|
1625
|
+
const [now, setNow] = React6.useState(() => Date.now());
|
|
1626
|
+
const addButtonRef = React6.useRef(null);
|
|
1627
|
+
const mountedRef = React6.useRef(true);
|
|
1628
|
+
const statusRequestRef = React6.useRef(0);
|
|
1629
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
1630
|
+
const noticeFrameRef = React6.useRef(null);
|
|
1631
|
+
const focusFrameRef = React6.useRef(null);
|
|
1632
|
+
React6.useEffect(() => {
|
|
1483
1633
|
mountedRef.current = true;
|
|
1484
1634
|
return () => {
|
|
1485
1635
|
mountedRef.current = false;
|
|
@@ -1494,8 +1644,8 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1494
1644
|
}
|
|
1495
1645
|
};
|
|
1496
1646
|
}, []);
|
|
1497
|
-
|
|
1498
|
-
const announce =
|
|
1647
|
+
React6.useEffect(() => installDingtalkStyles(), []);
|
|
1648
|
+
const announce = React6.useCallback((message) => {
|
|
1499
1649
|
if (!mountedRef.current) return;
|
|
1500
1650
|
if (noticeFrameRef.current !== null) {
|
|
1501
1651
|
window.cancelAnimationFrame(noticeFrameRef.current);
|
|
@@ -1509,7 +1659,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1509
1659
|
});
|
|
1510
1660
|
}
|
|
1511
1661
|
}, []);
|
|
1512
|
-
const focusAddButton =
|
|
1662
|
+
const focusAddButton = React6.useCallback(() => {
|
|
1513
1663
|
if (!mountedRef.current) return;
|
|
1514
1664
|
if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
|
|
1515
1665
|
focusFrameRef.current = window.requestAnimationFrame(() => {
|
|
@@ -1517,19 +1667,21 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1517
1667
|
if (mountedRef.current) addButtonRef.current?.focus();
|
|
1518
1668
|
});
|
|
1519
1669
|
}, []);
|
|
1520
|
-
const invoke =
|
|
1670
|
+
const invoke = React6.useCallback(async (endpoint, payload = {}, signal) => {
|
|
1521
1671
|
if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
|
|
1522
1672
|
return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
|
|
1523
1673
|
}, [rpcCall]);
|
|
1524
|
-
const loadStatus =
|
|
1674
|
+
const loadStatus = React6.useCallback(async ({
|
|
1525
1675
|
signal,
|
|
1526
1676
|
silent = false,
|
|
1527
1677
|
restoreProvisioning = false
|
|
1528
1678
|
} = {}) => {
|
|
1529
1679
|
if (!mountedRef.current || signal?.aborted) return void 0;
|
|
1680
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
1681
|
+
if (workspaceVersion === null) return void 0;
|
|
1530
1682
|
const requestId = statusRequestRef.current + 1;
|
|
1531
1683
|
statusRequestRef.current = requestId;
|
|
1532
|
-
const canCommit = () => mountedRef.current && !signal?.aborted && statusRequestRef.current === requestId;
|
|
1684
|
+
const canCommit = () => mountedRef.current && !signal?.aborted && statusRequestRef.current === requestId && workspaceFence.canCommitStatus(workspaceVersion);
|
|
1533
1685
|
if (!silent && canCommit()) {
|
|
1534
1686
|
setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
1535
1687
|
}
|
|
@@ -1560,13 +1712,13 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1560
1712
|
}));
|
|
1561
1713
|
return void 0;
|
|
1562
1714
|
}
|
|
1563
|
-
}, [invoke]);
|
|
1564
|
-
|
|
1715
|
+
}, [invoke, workspaceFence]);
|
|
1716
|
+
React6.useEffect(() => {
|
|
1565
1717
|
const controller = new AbortController();
|
|
1566
1718
|
void loadStatus({ signal: controller.signal, restoreProvisioning: true });
|
|
1567
1719
|
return () => controller.abort();
|
|
1568
1720
|
}, [loadStatus]);
|
|
1569
|
-
|
|
1721
|
+
React6.useEffect(() => {
|
|
1570
1722
|
if (model.phase !== "ready") return void 0;
|
|
1571
1723
|
const controller = new AbortController();
|
|
1572
1724
|
let running = false;
|
|
@@ -1585,14 +1737,14 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1585
1737
|
window.clearInterval(timer);
|
|
1586
1738
|
};
|
|
1587
1739
|
}, [loadStatus, model.phase]);
|
|
1588
|
-
|
|
1740
|
+
React6.useEffect(() => {
|
|
1589
1741
|
if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
|
|
1590
1742
|
const timer = window.setInterval(() => {
|
|
1591
1743
|
if (mountedRef.current) setNow(Date.now());
|
|
1592
1744
|
}, 1e3);
|
|
1593
1745
|
return () => window.clearInterval(timer);
|
|
1594
1746
|
}, [provision?.attemptId, provision?.status]);
|
|
1595
|
-
const startProvisioning =
|
|
1747
|
+
const startProvisioning = React6.useCallback(async ({ replace = false } = {}) => {
|
|
1596
1748
|
if (!mountedRef.current) return;
|
|
1597
1749
|
setCredentialOpen(false);
|
|
1598
1750
|
setCredentialError(null);
|
|
@@ -1630,8 +1782,9 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1630
1782
|
if (mountedRef.current) setBusy(false);
|
|
1631
1783
|
}
|
|
1632
1784
|
}, [announce, invoke, provision?.attemptId]);
|
|
1633
|
-
const bindCredentials =
|
|
1785
|
+
const bindCredentials = React6.useCallback(async ({ identity, secret }) => {
|
|
1634
1786
|
if (!mountedRef.current) return;
|
|
1787
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
1635
1788
|
setBusy(true);
|
|
1636
1789
|
setCredentialError(null);
|
|
1637
1790
|
try {
|
|
@@ -1640,22 +1793,26 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1640
1793
|
{ clientId: identity, clientSecret: secret }
|
|
1641
1794
|
));
|
|
1642
1795
|
if (!mountedRef.current) return;
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1796
|
+
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
1797
|
+
setModel({
|
|
1798
|
+
phase: "ready",
|
|
1799
|
+
bots: snapshot.bots,
|
|
1800
|
+
totals: snapshot.totals,
|
|
1801
|
+
revision: snapshot.revision,
|
|
1802
|
+
error: null
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1650
1805
|
setCredentialOpen(false);
|
|
1651
1806
|
announce("\u9489\u9489\u673A\u5668\u4EBA\u51ED\u636E\u5DF2\u7ED1\u5B9A\u3002");
|
|
1652
1807
|
} catch (error) {
|
|
1653
1808
|
if (mountedRef.current) setCredentialError(presentError(error));
|
|
1654
1809
|
} finally {
|
|
1810
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
1811
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
1655
1812
|
if (mountedRef.current) setBusy(false);
|
|
1656
1813
|
}
|
|
1657
|
-
}, [announce, invoke]);
|
|
1658
|
-
const cancelProvisioning =
|
|
1814
|
+
}, [announce, invoke, loadStatus, workspaceFence]);
|
|
1815
|
+
const cancelProvisioning = React6.useCallback(async () => {
|
|
1659
1816
|
if (!mountedRef.current) return;
|
|
1660
1817
|
setBusy(true);
|
|
1661
1818
|
try {
|
|
@@ -1673,7 +1830,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1673
1830
|
if (mountedRef.current) setBusy(false);
|
|
1674
1831
|
}
|
|
1675
1832
|
}, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]);
|
|
1676
|
-
|
|
1833
|
+
React6.useEffect(() => {
|
|
1677
1834
|
const attemptId = provision?.attemptId;
|
|
1678
1835
|
if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
|
|
1679
1836
|
const controller = new AbortController();
|
|
@@ -1732,7 +1889,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1732
1889
|
timer = null;
|
|
1733
1890
|
};
|
|
1734
1891
|
}, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
|
|
1735
|
-
const setBotBusy =
|
|
1892
|
+
const setBotBusy = React6.useCallback((botId, operation) => {
|
|
1736
1893
|
if (!mountedRef.current) return;
|
|
1737
1894
|
setBusyByBot((current) => {
|
|
1738
1895
|
const next = { ...current };
|
|
@@ -1741,14 +1898,22 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1741
1898
|
return next;
|
|
1742
1899
|
});
|
|
1743
1900
|
}, []);
|
|
1744
|
-
const runBotAction =
|
|
1901
|
+
const runBotAction = React6.useCallback(async ({ account, operation, endpoint, payload, success }) => {
|
|
1745
1902
|
if (!mountedRef.current) return void 0;
|
|
1903
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
1746
1904
|
setBotBusy(account.botId, operation);
|
|
1747
1905
|
try {
|
|
1748
|
-
await invoke(endpoint, payload);
|
|
1749
|
-
if (!mountedRef.current) return void 0;
|
|
1750
|
-
const snapshot = await loadStatus({ silent: true, restoreProvisioning: false });
|
|
1906
|
+
const snapshot = normalizeSnapshot(await invoke(endpoint, payload));
|
|
1751
1907
|
if (!mountedRef.current) return void 0;
|
|
1908
|
+
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
1909
|
+
setModel({
|
|
1910
|
+
phase: "ready",
|
|
1911
|
+
bots: snapshot.bots,
|
|
1912
|
+
totals: snapshot.totals,
|
|
1913
|
+
revision: snapshot.revision,
|
|
1914
|
+
error: null
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1752
1917
|
announce(typeof success === "function" ? success(snapshot) : success);
|
|
1753
1918
|
return snapshot;
|
|
1754
1919
|
} catch (error) {
|
|
@@ -1756,17 +1921,42 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1756
1921
|
announce(`\u64CD\u4F5C\u5931\u8D25\uFF1A${presentError(error).message}`);
|
|
1757
1922
|
return void 0;
|
|
1758
1923
|
} finally {
|
|
1924
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
1925
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true, restoreProvisioning: false });
|
|
1759
1926
|
if (mountedRef.current) setBotBusy(account.botId, null);
|
|
1760
1927
|
}
|
|
1761
|
-
}, [announce, invoke, loadStatus, setBotBusy]);
|
|
1762
|
-
const reconnect =
|
|
1928
|
+
}, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
|
|
1929
|
+
const reconnect = React6.useCallback((account) => runBotAction({
|
|
1763
1930
|
account,
|
|
1764
1931
|
operation: "reconnect",
|
|
1765
1932
|
endpoint: DINGTALK_ENDPOINTS.reconnectBot,
|
|
1766
1933
|
payload: { botId: account.botId },
|
|
1767
1934
|
success: (snapshot) => snapshot?.bots.find((bot) => bot.botId === account.botId)?.connected ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002" : "\u9489\u9489\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002"
|
|
1768
1935
|
}), [runBotAction]);
|
|
1769
|
-
const
|
|
1936
|
+
const saveWorkspace = React6.useCallback(async (account, workspace) => {
|
|
1937
|
+
const workspaceVersion = workspaceFence.beginMutation();
|
|
1938
|
+
setBotBusy(account.botId, "workspace");
|
|
1939
|
+
try {
|
|
1940
|
+
const snapshot = normalizeSnapshot(await invoke(
|
|
1941
|
+
DINGTALK_ENDPOINTS.setWorkspace,
|
|
1942
|
+
{ botId: account.botId, workspace }
|
|
1943
|
+
));
|
|
1944
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(workspaceVersion)) {
|
|
1945
|
+
setModel({
|
|
1946
|
+
phase: "ready",
|
|
1947
|
+
bots: snapshot.bots,
|
|
1948
|
+
totals: snapshot.totals,
|
|
1949
|
+
revision: snapshot.revision,
|
|
1950
|
+
error: null
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
} finally {
|
|
1954
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
1955
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
1956
|
+
if (mountedRef.current) setBotBusy(account.botId, null);
|
|
1957
|
+
}
|
|
1958
|
+
}, [invoke, loadStatus, setBotBusy, workspaceFence]);
|
|
1959
|
+
const remove = React6.useCallback(async (account) => {
|
|
1770
1960
|
const snapshot = await runBotAction({
|
|
1771
1961
|
account,
|
|
1772
1962
|
operation: "delete",
|
|
@@ -1848,7 +2038,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1848
2038
|
h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
|
|
1849
2039
|
)
|
|
1850
2040
|
) : h2(
|
|
1851
|
-
|
|
2041
|
+
React6.Fragment,
|
|
1852
2042
|
null,
|
|
1853
2043
|
credentialView,
|
|
1854
2044
|
provisionView,
|
|
@@ -1858,6 +2048,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|
|
1858
2048
|
busyByBot,
|
|
1859
2049
|
removeTarget,
|
|
1860
2050
|
onReconnect: (account) => void reconnect(account),
|
|
2051
|
+
onWorkspaceSave: saveWorkspace,
|
|
1861
2052
|
onRequestRemove: (account) => setRemoveTarget(account.botId),
|
|
1862
2053
|
onConfirmRemove: (account) => void remove(account),
|
|
1863
2054
|
onCancelRemove: () => setRemoveTarget(null)
|
|
@@ -1887,7 +2078,8 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|
|
1887
2078
|
status: "connection.status",
|
|
1888
2079
|
bindCredentials: "bot.bind-credentials",
|
|
1889
2080
|
reconnectBot: "bot.reconnect",
|
|
1890
|
-
deleteBot: "bot.delete"
|
|
2081
|
+
deleteBot: "bot.delete",
|
|
2082
|
+
setWorkspace: "bot.workspace.set"
|
|
1891
2083
|
});
|
|
1892
2084
|
function createTokenChannelApi(channel4, connectionSummary) {
|
|
1893
2085
|
const unwrapRpcResult10 = (result) => {
|
|
@@ -1909,6 +2101,7 @@ function createTokenChannelApi(channel4, connectionSummary) {
|
|
|
1909
2101
|
botId: id(value.botId),
|
|
1910
2102
|
connected,
|
|
1911
2103
|
state: connected ? "connected" : state,
|
|
2104
|
+
workspace: text(value.workspace, "", 4096),
|
|
1912
2105
|
bot: {
|
|
1913
2106
|
name: text(value.bot?.name, `${channel4}\u673A\u5668\u4EBA`, 100),
|
|
1914
2107
|
username: text(value.bot?.username, "", 100),
|
|
@@ -1955,8 +2148,8 @@ var normalizeSnapshot2 = api.normalizeSnapshot;
|
|
|
1955
2148
|
var presentError2 = api.presentError;
|
|
1956
2149
|
|
|
1957
2150
|
// plugin-src/client/channels/shared/token-channel.js
|
|
1958
|
-
var
|
|
1959
|
-
var Button3 =
|
|
2151
|
+
var React7 = __toESM(require("react"), 1);
|
|
2152
|
+
var Button3 = React7.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
1960
2153
|
return h2("button", {
|
|
1961
2154
|
...props,
|
|
1962
2155
|
ref,
|
|
@@ -1999,7 +2192,7 @@ function createTokenChannelSettings(definition) {
|
|
|
1999
2192
|
credentialNoun = "Bot Token",
|
|
2000
2193
|
emptyActionLabel = "\u586B\u5199 Bot Token"
|
|
2001
2194
|
} = definition;
|
|
2002
|
-
function AccountCard5({ account, busy, removing, onReconnect, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
|
2195
|
+
function AccountCard5({ account, busy, removing, onReconnect, onWorkspaceSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
|
2003
2196
|
const state = busy === "reconnect" ? "connecting" : account.state;
|
|
2004
2197
|
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
|
2005
2198
|
const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
|
@@ -2052,6 +2245,11 @@ function createTokenChannelSettings(definition) {
|
|
|
2052
2245
|
h2("dd", null, checkedTime2(account.health.lastCheckedAt))
|
|
2053
2246
|
)
|
|
2054
2247
|
),
|
|
2248
|
+
h2(WorkspaceEditor, {
|
|
2249
|
+
workspace: account.workspace,
|
|
2250
|
+
disabled: Boolean(busy),
|
|
2251
|
+
onSave: onWorkspaceSave
|
|
2252
|
+
}),
|
|
2055
2253
|
h2(
|
|
2056
2254
|
"div",
|
|
2057
2255
|
{ className: "ddt-accountFooter dim-cardFooter" },
|
|
@@ -2092,19 +2290,20 @@ function createTokenChannelSettings(definition) {
|
|
|
2092
2290
|
);
|
|
2093
2291
|
}
|
|
2094
2292
|
function SettingsTab({ rpcCall }) {
|
|
2095
|
-
const [model, setModel] =
|
|
2293
|
+
const [model, setModel] = React7.useState({
|
|
2096
2294
|
phase: "loading",
|
|
2097
2295
|
bots: [],
|
|
2098
2296
|
totals: { configured: 0, connected: 0 },
|
|
2099
2297
|
error: null
|
|
2100
2298
|
});
|
|
2101
|
-
const [credentialOpen, setCredentialOpen] =
|
|
2102
|
-
const [credentialError, setCredentialError] =
|
|
2103
|
-
const [busy, setBusy] =
|
|
2104
|
-
const [busyByBot, setBusyByBot] =
|
|
2105
|
-
const [removeTarget, setRemoveTarget] =
|
|
2106
|
-
const mounted =
|
|
2107
|
-
|
|
2299
|
+
const [credentialOpen, setCredentialOpen] = React7.useState(false);
|
|
2300
|
+
const [credentialError, setCredentialError] = React7.useState(null);
|
|
2301
|
+
const [busy, setBusy] = React7.useState(false);
|
|
2302
|
+
const [busyByBot, setBusyByBot] = React7.useState({});
|
|
2303
|
+
const [removeTarget, setRemoveTarget] = React7.useState(null);
|
|
2304
|
+
const mounted = React7.useRef(true);
|
|
2305
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
2306
|
+
React7.useEffect(() => {
|
|
2108
2307
|
const disposeDingtalk = installDingtalkStyles();
|
|
2109
2308
|
const disposeChannel = installStyles();
|
|
2110
2309
|
mounted.current = true;
|
|
@@ -2114,18 +2313,20 @@ function createTokenChannelSettings(definition) {
|
|
|
2114
2313
|
disposeDingtalk();
|
|
2115
2314
|
};
|
|
2116
2315
|
}, []);
|
|
2117
|
-
const invoke =
|
|
2316
|
+
const invoke = React7.useCallback(async (endpoint, payload = {}, signal) => {
|
|
2118
2317
|
if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`);
|
|
2119
2318
|
return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal));
|
|
2120
2319
|
}, [rpcCall]);
|
|
2121
|
-
const loadStatus =
|
|
2320
|
+
const loadStatus = React7.useCallback(async ({ signal, silent = false } = {}) => {
|
|
2321
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
2322
|
+
if (workspaceVersion === null) return;
|
|
2122
2323
|
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
2123
2324
|
try {
|
|
2124
2325
|
const snapshot = api4.normalizeSnapshot(await invoke(endpoints.status, {}, signal));
|
|
2125
|
-
if (!mounted.current || signal?.aborted) return;
|
|
2326
|
+
if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return;
|
|
2126
2327
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
2127
2328
|
} catch (error) {
|
|
2128
|
-
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted) {
|
|
2329
|
+
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
|
|
2129
2330
|
setModel((current) => ({
|
|
2130
2331
|
...current,
|
|
2131
2332
|
phase: silent ? current.phase : "error",
|
|
@@ -2133,13 +2334,13 @@ function createTokenChannelSettings(definition) {
|
|
|
2133
2334
|
}));
|
|
2134
2335
|
}
|
|
2135
2336
|
}
|
|
2136
|
-
}, [invoke]);
|
|
2137
|
-
|
|
2337
|
+
}, [invoke, workspaceFence]);
|
|
2338
|
+
React7.useEffect(() => {
|
|
2138
2339
|
const controller = new AbortController();
|
|
2139
2340
|
void loadStatus({ signal: controller.signal });
|
|
2140
2341
|
return () => controller.abort();
|
|
2141
2342
|
}, [loadStatus]);
|
|
2142
|
-
|
|
2343
|
+
React7.useEffect(() => {
|
|
2143
2344
|
if (model.phase !== "ready") return void 0;
|
|
2144
2345
|
const controller = new AbortController();
|
|
2145
2346
|
const timer = window.setInterval(
|
|
@@ -2151,7 +2352,8 @@ function createTokenChannelSettings(definition) {
|
|
|
2151
2352
|
window.clearInterval(timer);
|
|
2152
2353
|
};
|
|
2153
2354
|
}, [loadStatus, model.phase]);
|
|
2154
|
-
const bindCredentials =
|
|
2355
|
+
const bindCredentials = React7.useCallback(async (values) => {
|
|
2356
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
2155
2357
|
setBusy(true);
|
|
2156
2358
|
setCredentialError(null);
|
|
2157
2359
|
try {
|
|
@@ -2160,29 +2362,36 @@ function createTokenChannelSettings(definition) {
|
|
|
2160
2362
|
credentialPayload(values)
|
|
2161
2363
|
));
|
|
2162
2364
|
if (!mounted.current) return;
|
|
2163
|
-
|
|
2365
|
+
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
2366
|
+
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
2367
|
+
}
|
|
2164
2368
|
setCredentialOpen(false);
|
|
2165
2369
|
} catch (error) {
|
|
2166
2370
|
if (mounted.current) setCredentialError(api4.presentError(error));
|
|
2167
2371
|
} finally {
|
|
2372
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
2373
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
2168
2374
|
if (mounted.current) setBusy(false);
|
|
2169
2375
|
}
|
|
2170
|
-
}, [invoke]);
|
|
2171
|
-
const botAction =
|
|
2376
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
2377
|
+
const botAction = React7.useCallback(async (account, operation, endpoint, payload) => {
|
|
2378
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
2172
2379
|
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
|
2173
2380
|
try {
|
|
2174
2381
|
const snapshot = api4.normalizeSnapshot(await invoke(endpoint, payload));
|
|
2175
|
-
if (mounted.current) {
|
|
2382
|
+
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
2176
2383
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
2177
2384
|
}
|
|
2178
2385
|
} finally {
|
|
2386
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
2387
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
2179
2388
|
if (mounted.current) setBusyByBot((current) => {
|
|
2180
2389
|
const next = { ...current };
|
|
2181
2390
|
delete next[account.botId];
|
|
2182
2391
|
return next;
|
|
2183
2392
|
});
|
|
2184
2393
|
}
|
|
2185
|
-
}, [invoke]);
|
|
2394
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
2186
2395
|
const botList = model.bots.length > 0 ? h2(
|
|
2187
2396
|
"section",
|
|
2188
2397
|
{ className: "dim-listSection" },
|
|
@@ -2201,6 +2410,12 @@ function createTokenChannelSettings(definition) {
|
|
|
2201
2410
|
endpoints.reconnectBot,
|
|
2202
2411
|
{ botId: account.botId }
|
|
2203
2412
|
),
|
|
2413
|
+
onWorkspaceSave: (workspace) => botAction(
|
|
2414
|
+
account,
|
|
2415
|
+
"workspace",
|
|
2416
|
+
endpoints.setWorkspace,
|
|
2417
|
+
{ botId: account.botId, workspace }
|
|
2418
|
+
),
|
|
2204
2419
|
onRequestRemove: () => setRemoveTarget(account.botId),
|
|
2205
2420
|
onCancelRemove: () => setRemoveTarget(null),
|
|
2206
2421
|
onConfirmRemove: async () => {
|
|
@@ -2260,7 +2475,7 @@ function createTokenChannelSettings(definition) {
|
|
|
2260
2475
|
h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
|
|
2261
2476
|
)
|
|
2262
2477
|
) : h2(
|
|
2263
|
-
|
|
2478
|
+
React7.Fragment,
|
|
2264
2479
|
null,
|
|
2265
2480
|
credentialOpen ? CredentialPanel ? h2(CredentialPanel, {
|
|
2266
2481
|
busy,
|
|
@@ -2361,7 +2576,7 @@ var DiscordSettingsTab = channel.SettingsTab;
|
|
|
2361
2576
|
var DiscordAccountCard = channel.AccountCard;
|
|
2362
2577
|
|
|
2363
2578
|
// plugin-src/client/channels/feishu/index.js
|
|
2364
|
-
var
|
|
2579
|
+
var React9 = __toESM(require("react"), 1);
|
|
2365
2580
|
|
|
2366
2581
|
// plugin-src/client/channels/feishu/api.js
|
|
2367
2582
|
var FEISHU_RPC_CHANNEL = "/feishu";
|
|
@@ -2374,6 +2589,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
|
|
|
2374
2589
|
reconnectBot: "bot.reconnect",
|
|
2375
2590
|
disconnectBot: "bot.disconnect",
|
|
2376
2591
|
deleteBot: "bot.delete",
|
|
2592
|
+
setWorkspace: "bot.workspace.set",
|
|
2377
2593
|
// Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
|
|
2378
2594
|
testConnection: "connection.test",
|
|
2379
2595
|
disconnect: "connection.disconnect"
|
|
@@ -2490,6 +2706,7 @@ function normalizeBotConnection(value, fallbackBotId) {
|
|
|
2490
2706
|
state: authoritativeState(value.state, connected),
|
|
2491
2707
|
connected,
|
|
2492
2708
|
configured: value.configured !== false,
|
|
2709
|
+
workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
|
|
2493
2710
|
bot: normalizeBot2(value.bot),
|
|
2494
2711
|
health: normalizeHealth(value.health, connected),
|
|
2495
2712
|
error: normalizeError2(value.error)
|
|
@@ -2579,7 +2796,7 @@ function formatRemaining2(milliseconds) {
|
|
|
2579
2796
|
}
|
|
2580
2797
|
|
|
2581
2798
|
// plugin-src/client/lifecycle.js
|
|
2582
|
-
var
|
|
2799
|
+
var React8 = __toESM(require("react"), 1);
|
|
2583
2800
|
function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
|
|
2584
2801
|
let disposed = false;
|
|
2585
2802
|
let timer;
|
|
@@ -2641,8 +2858,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
|
|
|
2641
2858
|
};
|
|
2642
2859
|
}
|
|
2643
2860
|
function useAnimationFrameScheduler() {
|
|
2644
|
-
const schedulerRef =
|
|
2645
|
-
|
|
2861
|
+
const schedulerRef = React8.useRef(null);
|
|
2862
|
+
React8.useEffect(() => {
|
|
2646
2863
|
const scheduler = createAnimationFrameScheduler({
|
|
2647
2864
|
requestFrame: (callback) => window.requestAnimationFrame(callback),
|
|
2648
2865
|
cancelFrame: (frame) => window.cancelAnimationFrame(frame)
|
|
@@ -2653,7 +2870,7 @@ function useAnimationFrameScheduler() {
|
|
|
2653
2870
|
if (schedulerRef.current === scheduler) schedulerRef.current = null;
|
|
2654
2871
|
};
|
|
2655
2872
|
}, []);
|
|
2656
|
-
return
|
|
2873
|
+
return React8.useCallback(
|
|
2657
2874
|
(callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
|
|
2658
2875
|
[]
|
|
2659
2876
|
);
|
|
@@ -3221,7 +3438,7 @@ function QrIcon({ size = 58 }) {
|
|
|
3221
3438
|
fill: "currentColor"
|
|
3222
3439
|
}));
|
|
3223
3440
|
}
|
|
3224
|
-
var Button5 =
|
|
3441
|
+
var Button5 = React9.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
|
|
3225
3442
|
return h2("button", {
|
|
3226
3443
|
...props,
|
|
3227
3444
|
ref,
|
|
@@ -3333,13 +3550,13 @@ function safeQrSource2(value) {
|
|
|
3333
3550
|
return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
|
|
3334
3551
|
}
|
|
3335
3552
|
function QrPane({ provision, now, onRefresh, onCancel, busy }) {
|
|
3336
|
-
const [imageFailed, setImageFailed] =
|
|
3553
|
+
const [imageFailed, setImageFailed] = React9.useState(false);
|
|
3337
3554
|
const qrSource = safeQrSource2(provision.qrCodeDataUrl);
|
|
3338
3555
|
const href = safeVerificationHref(provision.verificationUrl);
|
|
3339
3556
|
const remaining = Math.max(0, provision.expiresAt - now);
|
|
3340
3557
|
const expired = provision.expired === true || remaining === 0;
|
|
3341
3558
|
const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining));
|
|
3342
|
-
|
|
3559
|
+
React9.useEffect(() => setImageFailed(false), [qrSource]);
|
|
3343
3560
|
return h2(
|
|
3344
3561
|
"div",
|
|
3345
3562
|
{ className: "bxf-card bxf-provisionCard dim-surfaceCard" },
|
|
@@ -3489,11 +3706,11 @@ function formatCheckedTime(timestamp7) {
|
|
|
3489
3706
|
}
|
|
3490
3707
|
}
|
|
3491
3708
|
function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
|
|
3492
|
-
const cancelRef =
|
|
3709
|
+
const cancelRef = React9.useRef(null);
|
|
3493
3710
|
const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
3494
3711
|
const titleId = `bxf-remove-title-${idPart}`;
|
|
3495
3712
|
const descriptionId = `bxf-remove-description-${idPart}`;
|
|
3496
|
-
|
|
3713
|
+
React9.useEffect(() => cancelRef.current?.focus(), []);
|
|
3497
3714
|
return h2(
|
|
3498
3715
|
"div",
|
|
3499
3716
|
{
|
|
@@ -3532,6 +3749,7 @@ function BotCard({
|
|
|
3532
3749
|
actionError,
|
|
3533
3750
|
removing,
|
|
3534
3751
|
onReconnect,
|
|
3752
|
+
onWorkspaceSave,
|
|
3535
3753
|
onRequestRemove,
|
|
3536
3754
|
onConfirmRemove,
|
|
3537
3755
|
onCancelRemove,
|
|
@@ -3596,6 +3814,11 @@ function BotCard({
|
|
|
3596
3814
|
h2("dd", null, formatCheckedTime(health.lastCheckedAt))
|
|
3597
3815
|
)
|
|
3598
3816
|
),
|
|
3817
|
+
h2(WorkspaceEditor, {
|
|
3818
|
+
workspace: connection.workspace,
|
|
3819
|
+
disabled: Boolean(busy),
|
|
3820
|
+
onSave: onWorkspaceSave
|
|
3821
|
+
}),
|
|
3599
3822
|
h2(
|
|
3600
3823
|
"div",
|
|
3601
3824
|
{ className: "bxf-connectedFooter dim-cardFooter" },
|
|
@@ -3654,6 +3877,7 @@ function BotList(props) {
|
|
|
3654
3877
|
actionError: props.errorsByBot[bot.botId],
|
|
3655
3878
|
removing: props.removeTargetId === bot.botId,
|
|
3656
3879
|
onReconnect: () => props.onReconnect(bot),
|
|
3880
|
+
onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
|
|
3657
3881
|
onRequestRemove: () => props.onRequestRemove(bot),
|
|
3658
3882
|
onConfirmRemove: () => props.onConfirmRemove(bot),
|
|
3659
3883
|
onCancelRemove: props.onCancelRemove,
|
|
@@ -3714,7 +3938,7 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal
|
|
|
3714
3938
|
};
|
|
3715
3939
|
}
|
|
3716
3940
|
function FeishuSettingsTab({ rpcCall }) {
|
|
3717
|
-
const [model, setModel] =
|
|
3941
|
+
const [model, setModel] = React9.useState({
|
|
3718
3942
|
phase: "loading",
|
|
3719
3943
|
revision: 0,
|
|
3720
3944
|
bots: [],
|
|
@@ -3723,31 +3947,39 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3723
3947
|
pageError: null,
|
|
3724
3948
|
statusError: null
|
|
3725
3949
|
});
|
|
3726
|
-
const [pageBusy, setPageBusy] =
|
|
3727
|
-
const [provisionBusy, setProvisionBusy] =
|
|
3728
|
-
const [credentialOpen, setCredentialOpen] =
|
|
3729
|
-
const [credentialBusy, setCredentialBusy] =
|
|
3730
|
-
const [credentialError, setCredentialError] =
|
|
3731
|
-
const [busyByBot, setBusyByBot] =
|
|
3732
|
-
const [errorsByBot, setErrorsByBot] =
|
|
3733
|
-
const [removeTargetId, setRemoveTargetId] =
|
|
3734
|
-
const [announcement, setAnnouncement] =
|
|
3735
|
-
const [now, setNow] =
|
|
3736
|
-
const [focusBotId, setFocusBotId] =
|
|
3737
|
-
const cardRefs =
|
|
3738
|
-
const removeButtonRefs =
|
|
3739
|
-
const addButtonRef =
|
|
3950
|
+
const [pageBusy, setPageBusy] = React9.useState(false);
|
|
3951
|
+
const [provisionBusy, setProvisionBusy] = React9.useState(false);
|
|
3952
|
+
const [credentialOpen, setCredentialOpen] = React9.useState(false);
|
|
3953
|
+
const [credentialBusy, setCredentialBusy] = React9.useState(false);
|
|
3954
|
+
const [credentialError, setCredentialError] = React9.useState(null);
|
|
3955
|
+
const [busyByBot, setBusyByBot] = React9.useState({});
|
|
3956
|
+
const [errorsByBot, setErrorsByBot] = React9.useState({});
|
|
3957
|
+
const [removeTargetId, setRemoveTargetId] = React9.useState(null);
|
|
3958
|
+
const [announcement, setAnnouncement] = React9.useState("");
|
|
3959
|
+
const [now, setNow] = React9.useState(() => Date.now());
|
|
3960
|
+
const [focusBotId, setFocusBotId] = React9.useState(null);
|
|
3961
|
+
const cardRefs = React9.useRef(/* @__PURE__ */ new Map());
|
|
3962
|
+
const removeButtonRefs = React9.useRef(/* @__PURE__ */ new Map());
|
|
3963
|
+
const addButtonRef = React9.useRef(null);
|
|
3964
|
+
const mountedRef = React9.useRef(true);
|
|
3965
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
3740
3966
|
const scheduleAnimationFrame = useAnimationFrameScheduler();
|
|
3741
|
-
|
|
3967
|
+
React9.useEffect(() => {
|
|
3968
|
+
mountedRef.current = true;
|
|
3969
|
+
return () => {
|
|
3970
|
+
mountedRef.current = false;
|
|
3971
|
+
};
|
|
3972
|
+
}, []);
|
|
3973
|
+
const announce = React9.useCallback((message) => {
|
|
3742
3974
|
setAnnouncement("");
|
|
3743
3975
|
scheduleAnimationFrame(() => {
|
|
3744
3976
|
if (message) setAnnouncement(message);
|
|
3745
3977
|
}, "announcement");
|
|
3746
3978
|
}, [scheduleAnimationFrame]);
|
|
3747
|
-
const invoke =
|
|
3979
|
+
const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => {
|
|
3748
3980
|
return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
|
|
3749
3981
|
}, [rpcCall]);
|
|
3750
|
-
const mergeSnapshot =
|
|
3982
|
+
const mergeSnapshot = React9.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
|
|
3751
3983
|
const now2 = Date.now();
|
|
3752
3984
|
setModel((current) => mergeFeishuSnapshotState(
|
|
3753
3985
|
current,
|
|
@@ -3755,28 +3987,30 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3755
3987
|
{ restoreProvisioning, now: now2 }
|
|
3756
3988
|
));
|
|
3757
3989
|
}, []);
|
|
3758
|
-
const loadStatus =
|
|
3990
|
+
const loadStatus = React9.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
|
|
3991
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
3992
|
+
if (workspaceVersion === null || !mountedRef.current) return void 0;
|
|
3759
3993
|
if (!silent) setPageBusy(true);
|
|
3760
3994
|
try {
|
|
3761
3995
|
const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.status, {}, signal));
|
|
3762
|
-
if (signal?.aborted) return void 0;
|
|
3996
|
+
if (signal?.aborted || !mountedRef.current || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
3763
3997
|
mergeSnapshot(snapshot, { restoreProvisioning });
|
|
3764
3998
|
return snapshot;
|
|
3765
3999
|
} catch (error) {
|
|
3766
|
-
if (signal?.aborted || error?.name === "AbortError") return void 0;
|
|
4000
|
+
if (signal?.aborted || error?.name === "AbortError" || !mountedRef.current || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
3767
4001
|
const presented = presentError3(error);
|
|
3768
4002
|
setModel((current) => current.phase === "loading" || !silent ? { ...current, phase: "error", pageError: presented } : { ...current, statusError: presented });
|
|
3769
4003
|
return void 0;
|
|
3770
4004
|
} finally {
|
|
3771
|
-
if (!silent && !signal?.aborted) setPageBusy(false);
|
|
4005
|
+
if (!silent && !signal?.aborted && mountedRef.current) setPageBusy(false);
|
|
3772
4006
|
}
|
|
3773
|
-
}, [invoke, mergeSnapshot]);
|
|
3774
|
-
|
|
4007
|
+
}, [invoke, mergeSnapshot, workspaceFence]);
|
|
4008
|
+
React9.useEffect(() => {
|
|
3775
4009
|
const controller = new AbortController();
|
|
3776
4010
|
void loadStatus({ signal: controller.signal, restoreProvisioning: true });
|
|
3777
4011
|
return () => controller.abort();
|
|
3778
4012
|
}, [loadStatus]);
|
|
3779
|
-
|
|
4013
|
+
React9.useEffect(() => {
|
|
3780
4014
|
if (model.phase !== "ready") return void 0;
|
|
3781
4015
|
const controller = new AbortController();
|
|
3782
4016
|
let inFlight = false;
|
|
@@ -3795,7 +4029,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3795
4029
|
window.clearInterval(timer);
|
|
3796
4030
|
};
|
|
3797
4031
|
}, [loadStatus, model.phase]);
|
|
3798
|
-
|
|
4032
|
+
React9.useEffect(() => {
|
|
3799
4033
|
if (!focusBotId) return;
|
|
3800
4034
|
const node = cardRefs.current.get(focusBotId);
|
|
3801
4035
|
if (!node) return;
|
|
@@ -3803,7 +4037,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3803
4037
|
node.focus({ preventScroll: true });
|
|
3804
4038
|
setFocusBotId(null);
|
|
3805
4039
|
}, [focusBotId, model.bots]);
|
|
3806
|
-
const startProvisioning =
|
|
4040
|
+
const startProvisioning = React9.useCallback(async ({ replace = false } = {}) => {
|
|
3807
4041
|
setCredentialOpen(false);
|
|
3808
4042
|
setCredentialError(null);
|
|
3809
4043
|
setProvisionBusy(true);
|
|
@@ -3843,7 +4077,8 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3843
4077
|
setProvisionBusy(false);
|
|
3844
4078
|
}
|
|
3845
4079
|
}, [announce, invoke, model.provisioning?.attemptId]);
|
|
3846
|
-
const bindCredentials =
|
|
4080
|
+
const bindCredentials = React9.useCallback(async ({ identity, secret }) => {
|
|
4081
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
3847
4082
|
setCredentialBusy(true);
|
|
3848
4083
|
setCredentialError(null);
|
|
3849
4084
|
try {
|
|
@@ -3851,16 +4086,20 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3851
4086
|
FEISHU_ENDPOINTS.bindCredentials,
|
|
3852
4087
|
{ appId: identity, appSecret: secret }
|
|
3853
4088
|
));
|
|
3854
|
-
|
|
4089
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
4090
|
+
mergeSnapshot(snapshot);
|
|
4091
|
+
}
|
|
3855
4092
|
setCredentialOpen(false);
|
|
3856
4093
|
announce("\u98DE\u4E66\u673A\u5668\u4EBA\u51ED\u636E\u5DF2\u7ED1\u5B9A\u3002");
|
|
3857
4094
|
} catch (error) {
|
|
3858
4095
|
setCredentialError(presentError3(error));
|
|
3859
4096
|
} finally {
|
|
4097
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4098
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
3860
4099
|
setCredentialBusy(false);
|
|
3861
4100
|
}
|
|
3862
|
-
}, [announce, invoke, mergeSnapshot]);
|
|
3863
|
-
const cancelProvisioning =
|
|
4101
|
+
}, [announce, invoke, loadStatus, mergeSnapshot, workspaceFence]);
|
|
4102
|
+
const cancelProvisioning = React9.useCallback(async () => {
|
|
3864
4103
|
const attemptId = model.provisioning?.attemptId;
|
|
3865
4104
|
setProvisionBusy(true);
|
|
3866
4105
|
try {
|
|
@@ -3882,7 +4121,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3882
4121
|
const countdownPhase = model.provisioning?.phase;
|
|
3883
4122
|
const countdownExpiresAt = model.provisioning?.expiresAt;
|
|
3884
4123
|
const countdownExpired = model.provisioning?.expired;
|
|
3885
|
-
|
|
4124
|
+
React9.useEffect(() => {
|
|
3886
4125
|
if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
|
|
3887
4126
|
const tick = () => {
|
|
3888
4127
|
const timestamp7 = Date.now();
|
|
@@ -3895,7 +4134,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3895
4134
|
const timer = window.setInterval(tick, 1e3);
|
|
3896
4135
|
return () => window.clearInterval(timer);
|
|
3897
4136
|
}, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]);
|
|
3898
|
-
|
|
4137
|
+
React9.useEffect(() => {
|
|
3899
4138
|
const provision2 = model.provisioning;
|
|
3900
4139
|
if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0;
|
|
3901
4140
|
const controller = new AbortController();
|
|
@@ -3959,7 +4198,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3959
4198
|
window.clearTimeout(timer);
|
|
3960
4199
|
};
|
|
3961
4200
|
}, [announce, invoke, loadStatus, model.provisioning]);
|
|
3962
|
-
const setBotBusy =
|
|
4201
|
+
const setBotBusy = React9.useCallback((botId, value) => {
|
|
3963
4202
|
setBusyByBot((current) => {
|
|
3964
4203
|
const next = { ...current };
|
|
3965
4204
|
if (value) next[botId] = value;
|
|
@@ -3967,7 +4206,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3967
4206
|
return next;
|
|
3968
4207
|
});
|
|
3969
4208
|
}, []);
|
|
3970
|
-
const setBotError =
|
|
4209
|
+
const setBotError = React9.useCallback((botId, error) => {
|
|
3971
4210
|
setErrorsByBot((current) => {
|
|
3972
4211
|
const next = { ...current };
|
|
3973
4212
|
if (error) next[botId] = presentError3(error);
|
|
@@ -3975,13 +4214,16 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3975
4214
|
return next;
|
|
3976
4215
|
});
|
|
3977
4216
|
}, []);
|
|
3978
|
-
const reconnectOneBot =
|
|
4217
|
+
const reconnectOneBot = React9.useCallback(async (connection) => {
|
|
3979
4218
|
const { botId, bot } = connection;
|
|
4219
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
3980
4220
|
setBotBusy(botId, "reconnect");
|
|
3981
4221
|
setBotError(botId, null);
|
|
3982
4222
|
try {
|
|
3983
4223
|
const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.reconnectBot, { botId }));
|
|
3984
|
-
|
|
4224
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
4225
|
+
mergeSnapshot(snapshot);
|
|
4226
|
+
}
|
|
3985
4227
|
const refreshed = snapshot.bots.find((item) => item.botId === botId);
|
|
3986
4228
|
if (!refreshed?.connected) {
|
|
3987
4229
|
const error = new Error(
|
|
@@ -3995,45 +4237,63 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
3995
4237
|
setBotError(botId, error);
|
|
3996
4238
|
announce(`${bot.name}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u673A\u5668\u4EBA\u72B6\u6001\u3002`);
|
|
3997
4239
|
} finally {
|
|
4240
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4241
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
3998
4242
|
setBotBusy(botId, null);
|
|
3999
4243
|
}
|
|
4000
|
-
}, [announce, invoke, mergeSnapshot, setBotBusy, setBotError]);
|
|
4001
|
-
const
|
|
4244
|
+
}, [announce, invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
|
|
4245
|
+
const saveWorkspace = React9.useCallback(async (connection, workspace) => {
|
|
4246
|
+
const { botId } = connection;
|
|
4247
|
+
const workspaceVersion = workspaceFence.beginMutation();
|
|
4248
|
+
setBotBusy(botId, "workspace");
|
|
4249
|
+
setBotError(botId, null);
|
|
4250
|
+
try {
|
|
4251
|
+
const snapshot = normalizeBotsSnapshot(await invoke(
|
|
4252
|
+
FEISHU_ENDPOINTS.setWorkspace,
|
|
4253
|
+
{ botId, workspace }
|
|
4254
|
+
));
|
|
4255
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(workspaceVersion)) {
|
|
4256
|
+
mergeSnapshot(snapshot);
|
|
4257
|
+
}
|
|
4258
|
+
} finally {
|
|
4259
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4260
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
4261
|
+
if (mountedRef.current) setBotBusy(botId, null);
|
|
4262
|
+
}
|
|
4263
|
+
}, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
|
|
4264
|
+
const requestRemove = React9.useCallback((connection) => {
|
|
4002
4265
|
setRemoveTargetId(connection.botId);
|
|
4003
4266
|
}, []);
|
|
4004
|
-
const cancelRemove =
|
|
4267
|
+
const cancelRemove = React9.useCallback(() => {
|
|
4005
4268
|
const botId = removeTargetId;
|
|
4006
4269
|
setRemoveTargetId(null);
|
|
4007
4270
|
scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus");
|
|
4008
4271
|
}, [removeTargetId, scheduleAnimationFrame]);
|
|
4009
|
-
const confirmRemove =
|
|
4272
|
+
const confirmRemove = React9.useCallback(async (connection) => {
|
|
4010
4273
|
const { botId, bot } = connection;
|
|
4274
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
4011
4275
|
setBotBusy(botId, "delete");
|
|
4012
4276
|
setBotError(botId, null);
|
|
4013
4277
|
try {
|
|
4014
|
-
await invoke(
|
|
4278
|
+
const snapshot = normalizeBotsSnapshot(await invoke(
|
|
4279
|
+
FEISHU_ENDPOINTS.deleteBot,
|
|
4280
|
+
{ botId, confirm: true }
|
|
4281
|
+
));
|
|
4015
4282
|
setRemoveTargetId(null);
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
...current,
|
|
4020
|
-
bots,
|
|
4021
|
-
totals: {
|
|
4022
|
-
configured: bots.length,
|
|
4023
|
-
connected: bots.filter((item) => item.connected).length
|
|
4024
|
-
}
|
|
4025
|
-
};
|
|
4026
|
-
});
|
|
4283
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
4284
|
+
mergeSnapshot(snapshot);
|
|
4285
|
+
}
|
|
4027
4286
|
announce(`${bot.name}\u5DF2\u4ECE\u6B64 DeepSeek Harness \u79FB\u9664\uFF1B\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u5E94\u7528\u672A\u88AB\u5220\u9664\u3002`);
|
|
4028
|
-
await loadStatus({ silent: true });
|
|
4029
4287
|
scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus");
|
|
4030
4288
|
} catch (error) {
|
|
4031
4289
|
setBotError(botId, error);
|
|
4032
4290
|
announce(`${bot.name}\u79FB\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002`);
|
|
4033
4291
|
} finally {
|
|
4292
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4293
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
4034
4294
|
setBotBusy(botId, null);
|
|
4035
4295
|
}
|
|
4036
|
-
}, [announce, invoke, loadStatus, scheduleAnimationFrame, setBotBusy, setBotError]);
|
|
4296
|
+
}, [announce, invoke, loadStatus, mergeSnapshot, scheduleAnimationFrame, setBotBusy, setBotError, workspaceFence]);
|
|
4037
4297
|
const provision = model.provisioning;
|
|
4038
4298
|
let provisionContent = null;
|
|
4039
4299
|
if (provision?.phase === "creating") {
|
|
@@ -4074,11 +4334,11 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
4074
4334
|
setCredentialError(null);
|
|
4075
4335
|
}
|
|
4076
4336
|
}) : null;
|
|
4077
|
-
const setCardRef =
|
|
4337
|
+
const setCardRef = React9.useCallback((botId, node) => {
|
|
4078
4338
|
if (node) cardRefs.current.set(botId, node);
|
|
4079
4339
|
else cardRefs.current.delete(botId);
|
|
4080
4340
|
}, []);
|
|
4081
|
-
const setRemoveButtonRef =
|
|
4341
|
+
const setRemoveButtonRef = React9.useCallback((botId, node) => {
|
|
4082
4342
|
if (node) removeButtonRefs.current.set(botId, node);
|
|
4083
4343
|
else removeButtonRefs.current.delete(botId);
|
|
4084
4344
|
}, []);
|
|
@@ -4115,7 +4375,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
4115
4375
|
onRetry: () => void loadStatus(),
|
|
4116
4376
|
busy: pageBusy
|
|
4117
4377
|
}) : h2(
|
|
4118
|
-
|
|
4378
|
+
React9.Fragment,
|
|
4119
4379
|
null,
|
|
4120
4380
|
credentialContent,
|
|
4121
4381
|
provisionContent,
|
|
@@ -4126,6 +4386,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|
|
4126
4386
|
errorsByBot,
|
|
4127
4387
|
removeTargetId,
|
|
4128
4388
|
onReconnect: (bot) => void reconnectOneBot(bot),
|
|
4389
|
+
onWorkspaceSave: saveWorkspace,
|
|
4129
4390
|
onRequestRemove: requestRemove,
|
|
4130
4391
|
onConfirmRemove: (bot) => void confirmRemove(bot),
|
|
4131
4392
|
onCancelRemove: cancelRemove,
|
|
@@ -4145,7 +4406,8 @@ var QQ_ENDPOINTS = Object.freeze({
|
|
|
4145
4406
|
cancelProvisioning: "provision.cancel",
|
|
4146
4407
|
bindCredentials: "bot.bind-credentials",
|
|
4147
4408
|
reconnectBot: "bot.reconnect",
|
|
4148
|
-
deleteBot: "bot.delete"
|
|
4409
|
+
deleteBot: "bot.delete",
|
|
4410
|
+
setWorkspace: "bot.workspace.set"
|
|
4149
4411
|
});
|
|
4150
4412
|
var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
|
|
4151
4413
|
var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
|
@@ -4207,6 +4469,7 @@ function normalizeBot3(value) {
|
|
|
4207
4469
|
botId: id2(value.botId),
|
|
4208
4470
|
connected,
|
|
4209
4471
|
state: connected ? "connected" : state,
|
|
4472
|
+
workspace: text2(value.workspace, "", 4096),
|
|
4210
4473
|
bot: {
|
|
4211
4474
|
name: text2(value.bot?.name, "QQ\u673A\u5668\u4EBA", 100),
|
|
4212
4475
|
appIdMasked: text2(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
|
|
@@ -4244,7 +4507,7 @@ function formatRemaining3(milliseconds) {
|
|
|
4244
4507
|
}
|
|
4245
4508
|
|
|
4246
4509
|
// plugin-src/client/channels/qq/index.js
|
|
4247
|
-
var
|
|
4510
|
+
var React10 = __toESM(require("react"), 1);
|
|
4248
4511
|
|
|
4249
4512
|
// plugin-src/client/channels/qq/styles.js
|
|
4250
4513
|
var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings";
|
|
@@ -4269,7 +4532,7 @@ function installQqStyles() {
|
|
|
4269
4532
|
|
|
4270
4533
|
// plugin-src/client/channels/qq/index.js
|
|
4271
4534
|
var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
|
|
4272
|
-
var Button7 =
|
|
4535
|
+
var Button7 = React10.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
4273
4536
|
return h2("button", {
|
|
4274
4537
|
...props,
|
|
4275
4538
|
ref,
|
|
@@ -4476,7 +4739,16 @@ function RemoveConfirmation3({ account, busy, onConfirm, onCancel }) {
|
|
|
4476
4739
|
)
|
|
4477
4740
|
);
|
|
4478
4741
|
}
|
|
4479
|
-
function AccountCard2({
|
|
4742
|
+
function AccountCard2({
|
|
4743
|
+
account,
|
|
4744
|
+
busy,
|
|
4745
|
+
removing,
|
|
4746
|
+
onReconnect,
|
|
4747
|
+
onWorkspaceSave,
|
|
4748
|
+
onRequestRemove,
|
|
4749
|
+
onConfirmRemove,
|
|
4750
|
+
onCancelRemove
|
|
4751
|
+
}) {
|
|
4480
4752
|
const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
|
|
4481
4753
|
const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
|
4482
4754
|
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
|
@@ -4513,6 +4785,11 @@ function AccountCard2({ account, busy, removing, onReconnect, onRequestRemove, o
|
|
|
4513
4785
|
h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6D88\u606F\u901A\u9053"), h2("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")),
|
|
4514
4786
|
h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime3(account.health.lastCheckedAt)))
|
|
4515
4787
|
),
|
|
4788
|
+
h2(WorkspaceEditor, {
|
|
4789
|
+
workspace: account.workspace,
|
|
4790
|
+
disabled: Boolean(busy),
|
|
4791
|
+
onSave: onWorkspaceSave
|
|
4792
|
+
}),
|
|
4516
4793
|
h2(
|
|
4517
4794
|
"div",
|
|
4518
4795
|
{ className: "ddt-accountFooter dim-cardFooter" },
|
|
@@ -4534,17 +4811,18 @@ function AccountCard2({ account, busy, removing, onReconnect, onRequestRemove, o
|
|
|
4534
4811
|
);
|
|
4535
4812
|
}
|
|
4536
4813
|
function QqSettingsTab({ rpcCall }) {
|
|
4537
|
-
const [model, setModel] =
|
|
4538
|
-
const [provision, setProvision] =
|
|
4539
|
-
const [busy, setBusy] =
|
|
4540
|
-
const [busyByBot, setBusyByBot] =
|
|
4541
|
-
const [removeTarget, setRemoveTarget] =
|
|
4542
|
-
const [credentialOpen, setCredentialOpen] =
|
|
4543
|
-
const [credentialError, setCredentialError] =
|
|
4544
|
-
const [now, setNow] =
|
|
4545
|
-
const mounted =
|
|
4546
|
-
const
|
|
4547
|
-
|
|
4814
|
+
const [model, setModel] = React10.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
|
|
4815
|
+
const [provision, setProvision] = React10.useState(null);
|
|
4816
|
+
const [busy, setBusy] = React10.useState(false);
|
|
4817
|
+
const [busyByBot, setBusyByBot] = React10.useState({});
|
|
4818
|
+
const [removeTarget, setRemoveTarget] = React10.useState(null);
|
|
4819
|
+
const [credentialOpen, setCredentialOpen] = React10.useState(false);
|
|
4820
|
+
const [credentialError, setCredentialError] = React10.useState(null);
|
|
4821
|
+
const [now, setNow] = React10.useState(Date.now());
|
|
4822
|
+
const mounted = React10.useRef(true);
|
|
4823
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
4824
|
+
const addButtonRef = React10.useRef(null);
|
|
4825
|
+
React10.useEffect(() => {
|
|
4548
4826
|
const disposeDingtalk = installDingtalkStyles();
|
|
4549
4827
|
const disposeQq = installQqStyles();
|
|
4550
4828
|
mounted.current = true;
|
|
@@ -4554,15 +4832,17 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4554
4832
|
disposeDingtalk();
|
|
4555
4833
|
};
|
|
4556
4834
|
}, []);
|
|
4557
|
-
const invoke =
|
|
4835
|
+
const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
|
|
4558
4836
|
if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
|
|
4559
4837
|
return unwrapRpcResult4(await rpcCall(endpoint, payload, signal));
|
|
4560
4838
|
}, [rpcCall]);
|
|
4561
|
-
const loadStatus =
|
|
4839
|
+
const loadStatus = React10.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
|
|
4840
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
4841
|
+
if (workspaceVersion === null) return void 0;
|
|
4562
4842
|
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
4563
4843
|
try {
|
|
4564
4844
|
const snapshot = normalizeSnapshot3(await invoke(QQ_ENDPOINTS.status, {}, signal));
|
|
4565
|
-
if (!mounted.current || signal?.aborted) return void 0;
|
|
4845
|
+
if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
4566
4846
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
4567
4847
|
if (restore && snapshot.provisioning) setProvision({
|
|
4568
4848
|
...snapshot.provisioning,
|
|
@@ -4570,18 +4850,18 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4570
4850
|
});
|
|
4571
4851
|
return snapshot;
|
|
4572
4852
|
} catch (error) {
|
|
4573
|
-
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted) {
|
|
4853
|
+
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
|
|
4574
4854
|
setModel((current) => ({ ...current, phase: silent ? current.phase : "error", error: presentError4(error) }));
|
|
4575
4855
|
}
|
|
4576
4856
|
return void 0;
|
|
4577
4857
|
}
|
|
4578
|
-
}, [invoke]);
|
|
4579
|
-
|
|
4858
|
+
}, [invoke, workspaceFence]);
|
|
4859
|
+
React10.useEffect(() => {
|
|
4580
4860
|
const controller = new AbortController();
|
|
4581
4861
|
void loadStatus({ signal: controller.signal, restore: true });
|
|
4582
4862
|
return () => controller.abort();
|
|
4583
4863
|
}, [loadStatus]);
|
|
4584
|
-
|
|
4864
|
+
React10.useEffect(() => {
|
|
4585
4865
|
if (model.phase !== "ready") return void 0;
|
|
4586
4866
|
const controller = new AbortController();
|
|
4587
4867
|
const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
|
|
@@ -4590,12 +4870,12 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4590
4870
|
window.clearInterval(timer);
|
|
4591
4871
|
};
|
|
4592
4872
|
}, [loadStatus, model.phase]);
|
|
4593
|
-
|
|
4873
|
+
React10.useEffect(() => {
|
|
4594
4874
|
if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0;
|
|
4595
4875
|
const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
|
|
4596
4876
|
return () => window.clearInterval(timer);
|
|
4597
4877
|
}, [provision?.attemptId, provision?.status]);
|
|
4598
|
-
const startProvisioning =
|
|
4878
|
+
const startProvisioning = React10.useCallback(async (replace = false) => {
|
|
4599
4879
|
setCredentialOpen(false);
|
|
4600
4880
|
setCredentialError(null);
|
|
4601
4881
|
setBusy(true);
|
|
@@ -4613,7 +4893,8 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4613
4893
|
if (mounted.current) setBusy(false);
|
|
4614
4894
|
}
|
|
4615
4895
|
}, [invoke, provision?.attemptId]);
|
|
4616
|
-
const bindCredentials =
|
|
4896
|
+
const bindCredentials = React10.useCallback(async ({ identity, secret }) => {
|
|
4897
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
4617
4898
|
setBusy(true);
|
|
4618
4899
|
setCredentialError(null);
|
|
4619
4900
|
try {
|
|
@@ -4622,15 +4903,19 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4622
4903
|
{ appId: identity, appSecret: secret }
|
|
4623
4904
|
));
|
|
4624
4905
|
if (!mounted.current) return;
|
|
4625
|
-
|
|
4906
|
+
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
4907
|
+
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
4908
|
+
}
|
|
4626
4909
|
setCredentialOpen(false);
|
|
4627
4910
|
} catch (error) {
|
|
4628
4911
|
if (mounted.current) setCredentialError(presentError4(error));
|
|
4629
4912
|
} finally {
|
|
4913
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4914
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
4630
4915
|
if (mounted.current) setBusy(false);
|
|
4631
4916
|
}
|
|
4632
|
-
}, [invoke]);
|
|
4633
|
-
const closeProvision =
|
|
4917
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
4918
|
+
const closeProvision = React10.useCallback(async () => {
|
|
4634
4919
|
setBusy(true);
|
|
4635
4920
|
try {
|
|
4636
4921
|
if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) {
|
|
@@ -4641,7 +4926,7 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4641
4926
|
if (mounted.current) setBusy(false);
|
|
4642
4927
|
}
|
|
4643
4928
|
}, [invoke, provision?.attemptId, provision?.status]);
|
|
4644
|
-
|
|
4929
|
+
React10.useEffect(() => {
|
|
4645
4930
|
const attemptId = provision?.attemptId;
|
|
4646
4931
|
if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0;
|
|
4647
4932
|
const controller = new AbortController();
|
|
@@ -4671,20 +4956,25 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4671
4956
|
window.clearTimeout(timer);
|
|
4672
4957
|
};
|
|
4673
4958
|
}, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
|
|
4674
|
-
const botAction =
|
|
4959
|
+
const botAction = React10.useCallback(async (account, operation, endpoint, payload) => {
|
|
4960
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
4675
4961
|
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
|
4676
4962
|
try {
|
|
4677
4963
|
const snapshot = normalizeSnapshot3(await invoke(endpoint, payload));
|
|
4678
|
-
if (mounted.current
|
|
4964
|
+
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
4965
|
+
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
4966
|
+
}
|
|
4679
4967
|
return snapshot;
|
|
4680
4968
|
} finally {
|
|
4969
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
4970
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
4681
4971
|
if (mounted.current) setBusyByBot((current) => {
|
|
4682
4972
|
const next = { ...current };
|
|
4683
4973
|
delete next[account.botId];
|
|
4684
4974
|
return next;
|
|
4685
4975
|
});
|
|
4686
4976
|
}
|
|
4687
|
-
}, [invoke]);
|
|
4977
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
4688
4978
|
let provisionView = null;
|
|
4689
4979
|
if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7 QQ \u4E8C\u7EF4\u7801\u2026");
|
|
4690
4980
|
else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel2, {
|
|
@@ -4713,6 +5003,12 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4713
5003
|
busy: busyByBot[account.botId],
|
|
4714
5004
|
removing: removeTarget === account.botId,
|
|
4715
5005
|
onReconnect: () => void botAction(account, "reconnect", QQ_ENDPOINTS.reconnectBot, { botId: account.botId }),
|
|
5006
|
+
onWorkspaceSave: (workspace) => botAction(
|
|
5007
|
+
account,
|
|
5008
|
+
"workspace",
|
|
5009
|
+
QQ_ENDPOINTS.setWorkspace,
|
|
5010
|
+
{ botId: account.botId, workspace }
|
|
5011
|
+
),
|
|
4716
5012
|
onRequestRemove: () => setRemoveTarget(account.botId),
|
|
4717
5013
|
onCancelRemove: () => setRemoveTarget(null),
|
|
4718
5014
|
onConfirmRemove: async () => {
|
|
@@ -4751,7 +5047,7 @@ function QqSettingsTab({ rpcCall }) {
|
|
|
4751
5047
|
addButtonRef
|
|
4752
5048
|
}),
|
|
4753
5049
|
model.phase === "loading" ? h2(LoadingView3) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button7, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
|
|
4754
|
-
|
|
5050
|
+
React10.Fragment,
|
|
4755
5051
|
null,
|
|
4756
5052
|
credentialView,
|
|
4757
5053
|
provisionView,
|
|
@@ -4770,7 +5066,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
|
|
|
4770
5066
|
var presentError5 = api2.presentError;
|
|
4771
5067
|
|
|
4772
5068
|
// plugin-src/client/channels/slack/index.js
|
|
4773
|
-
var
|
|
5069
|
+
var React11 = __toESM(require("react"), 1);
|
|
4774
5070
|
|
|
4775
5071
|
// src/channels/slack/manifest.mjs
|
|
4776
5072
|
var SLACK_APP_MANIFEST_YAML = `_metadata:
|
|
@@ -4842,10 +5138,10 @@ function installSlackStyles() {
|
|
|
4842
5138
|
|
|
4843
5139
|
// plugin-src/client/channels/slack/index.js
|
|
4844
5140
|
function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
|
|
4845
|
-
const [botToken, setBotToken] =
|
|
4846
|
-
const [appToken, setAppToken] =
|
|
4847
|
-
const [copied, setCopied] =
|
|
4848
|
-
const headingId =
|
|
5141
|
+
const [botToken, setBotToken] = React11.useState("");
|
|
5142
|
+
const [appToken, setAppToken] = React11.useState("");
|
|
5143
|
+
const [copied, setCopied] = React11.useState(false);
|
|
5144
|
+
const headingId = React11.useId();
|
|
4849
5145
|
const copyManifest = async () => {
|
|
4850
5146
|
try {
|
|
4851
5147
|
await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
|
|
@@ -5038,7 +5334,8 @@ var WECOM_ENDPOINTS = Object.freeze({
|
|
|
5038
5334
|
cancelProvisioning: "provision.cancel",
|
|
5039
5335
|
bindCredentials: "bot.bind-credentials",
|
|
5040
5336
|
reconnectBot: "bot.reconnect",
|
|
5041
|
-
deleteBot: "bot.delete"
|
|
5337
|
+
deleteBot: "bot.delete",
|
|
5338
|
+
setWorkspace: "bot.workspace.set"
|
|
5042
5339
|
});
|
|
5043
5340
|
var PROVISION_STATES3 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
|
|
5044
5341
|
var ACCOUNT_STATES4 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
|
@@ -5100,6 +5397,7 @@ function normalizeBot4(value) {
|
|
|
5100
5397
|
botId: id3(value.botId),
|
|
5101
5398
|
connected,
|
|
5102
5399
|
state: connected ? "connected" : state,
|
|
5400
|
+
workspace: text3(value.workspace, "", 4096),
|
|
5103
5401
|
bot: {
|
|
5104
5402
|
name: text3(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA", 100),
|
|
5105
5403
|
appIdMasked: text3(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
|
|
@@ -5137,7 +5435,7 @@ function formatRemaining4(milliseconds) {
|
|
|
5137
5435
|
}
|
|
5138
5436
|
|
|
5139
5437
|
// plugin-src/client/channels/wecom/index.js
|
|
5140
|
-
var
|
|
5438
|
+
var React12 = __toESM(require("react"), 1);
|
|
5141
5439
|
|
|
5142
5440
|
// plugin-src/client/channels/wecom/styles.js
|
|
5143
5441
|
var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
|
|
@@ -5162,7 +5460,7 @@ function installWecomStyles() {
|
|
|
5162
5460
|
|
|
5163
5461
|
// plugin-src/client/channels/wecom/index.js
|
|
5164
5462
|
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
|
|
5165
|
-
var Button9 =
|
|
5463
|
+
var Button9 = React12.forwardRef(function Button10({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
5166
5464
|
return h2("button", {
|
|
5167
5465
|
...props,
|
|
5168
5466
|
ref,
|
|
@@ -5369,7 +5667,16 @@ function RemoveConfirmation4({ account, busy, onConfirm, onCancel }) {
|
|
|
5369
5667
|
)
|
|
5370
5668
|
);
|
|
5371
5669
|
}
|
|
5372
|
-
function AccountCard3({
|
|
5670
|
+
function AccountCard3({
|
|
5671
|
+
account,
|
|
5672
|
+
busy,
|
|
5673
|
+
removing,
|
|
5674
|
+
onReconnect,
|
|
5675
|
+
onWorkspaceSave,
|
|
5676
|
+
onRequestRemove,
|
|
5677
|
+
onConfirmRemove,
|
|
5678
|
+
onCancelRemove
|
|
5679
|
+
}) {
|
|
5373
5680
|
const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
|
|
5374
5681
|
const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
|
5375
5682
|
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
|
@@ -5406,6 +5713,11 @@ function AccountCard3({ account, busy, removing, onReconnect, onRequestRemove, o
|
|
|
5406
5713
|
h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6D88\u606F\u901A\u9053"), h2("dd", null, account.connected ? "WebSocket \u957F\u8FDE\u63A5" : "\u79BB\u7EBF")),
|
|
5407
5714
|
h2("div", { className: "ddt-metric dim-botMetric" }, h2("dt", null, "\u6700\u8FD1\u68C0\u67E5"), h2("dd", null, checkedTime4(account.health.lastCheckedAt)))
|
|
5408
5715
|
),
|
|
5716
|
+
h2(WorkspaceEditor, {
|
|
5717
|
+
workspace: account.workspace,
|
|
5718
|
+
disabled: Boolean(busy),
|
|
5719
|
+
onSave: onWorkspaceSave
|
|
5720
|
+
}),
|
|
5409
5721
|
h2(
|
|
5410
5722
|
"div",
|
|
5411
5723
|
{ className: "ddt-accountFooter dim-cardFooter" },
|
|
@@ -5427,17 +5739,18 @@ function AccountCard3({ account, busy, removing, onReconnect, onRequestRemove, o
|
|
|
5427
5739
|
);
|
|
5428
5740
|
}
|
|
5429
5741
|
function WecomSettingsTab({ rpcCall }) {
|
|
5430
|
-
const [model, setModel] =
|
|
5431
|
-
const [provision, setProvision] =
|
|
5432
|
-
const [busy, setBusy] =
|
|
5433
|
-
const [busyByBot, setBusyByBot] =
|
|
5434
|
-
const [removeTarget, setRemoveTarget] =
|
|
5435
|
-
const [credentialOpen, setCredentialOpen] =
|
|
5436
|
-
const [credentialError, setCredentialError] =
|
|
5437
|
-
const [now, setNow] =
|
|
5438
|
-
const mounted =
|
|
5439
|
-
const
|
|
5440
|
-
|
|
5742
|
+
const [model, setModel] = React12.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
|
|
5743
|
+
const [provision, setProvision] = React12.useState(null);
|
|
5744
|
+
const [busy, setBusy] = React12.useState(false);
|
|
5745
|
+
const [busyByBot, setBusyByBot] = React12.useState({});
|
|
5746
|
+
const [removeTarget, setRemoveTarget] = React12.useState(null);
|
|
5747
|
+
const [credentialOpen, setCredentialOpen] = React12.useState(false);
|
|
5748
|
+
const [credentialError, setCredentialError] = React12.useState(null);
|
|
5749
|
+
const [now, setNow] = React12.useState(Date.now());
|
|
5750
|
+
const mounted = React12.useRef(true);
|
|
5751
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
5752
|
+
const addButtonRef = React12.useRef(null);
|
|
5753
|
+
React12.useEffect(() => {
|
|
5441
5754
|
const disposeDingtalk = installDingtalkStyles();
|
|
5442
5755
|
const disposeWecom = installWecomStyles();
|
|
5443
5756
|
mounted.current = true;
|
|
@@ -5447,15 +5760,17 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5447
5760
|
disposeDingtalk();
|
|
5448
5761
|
};
|
|
5449
5762
|
}, []);
|
|
5450
|
-
const invoke =
|
|
5763
|
+
const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
|
|
5451
5764
|
if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
|
|
5452
5765
|
return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
|
|
5453
5766
|
}, [rpcCall]);
|
|
5454
|
-
const loadStatus =
|
|
5767
|
+
const loadStatus = React12.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
|
|
5768
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
5769
|
+
if (workspaceVersion === null) return void 0;
|
|
5455
5770
|
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
5456
5771
|
try {
|
|
5457
5772
|
const snapshot = normalizeSnapshot6(await invoke(WECOM_ENDPOINTS.status, {}, signal));
|
|
5458
|
-
if (!mounted.current || signal?.aborted) return void 0;
|
|
5773
|
+
if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
5459
5774
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
5460
5775
|
if (restore && snapshot.provisioning) setProvision({
|
|
5461
5776
|
...snapshot.provisioning,
|
|
@@ -5463,18 +5778,18 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5463
5778
|
});
|
|
5464
5779
|
return snapshot;
|
|
5465
5780
|
} catch (error) {
|
|
5466
|
-
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted) {
|
|
5781
|
+
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
|
|
5467
5782
|
setModel((current) => ({ ...current, phase: silent ? current.phase : "error", error: presentError7(error) }));
|
|
5468
5783
|
}
|
|
5469
5784
|
return void 0;
|
|
5470
5785
|
}
|
|
5471
|
-
}, [invoke]);
|
|
5472
|
-
|
|
5786
|
+
}, [invoke, workspaceFence]);
|
|
5787
|
+
React12.useEffect(() => {
|
|
5473
5788
|
const controller = new AbortController();
|
|
5474
5789
|
void loadStatus({ signal: controller.signal, restore: true });
|
|
5475
5790
|
return () => controller.abort();
|
|
5476
5791
|
}, [loadStatus]);
|
|
5477
|
-
|
|
5792
|
+
React12.useEffect(() => {
|
|
5478
5793
|
if (model.phase !== "ready") return void 0;
|
|
5479
5794
|
const controller = new AbortController();
|
|
5480
5795
|
const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
|
|
@@ -5483,12 +5798,12 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5483
5798
|
window.clearInterval(timer);
|
|
5484
5799
|
};
|
|
5485
5800
|
}, [loadStatus, model.phase]);
|
|
5486
|
-
|
|
5801
|
+
React12.useEffect(() => {
|
|
5487
5802
|
if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
|
|
5488
5803
|
const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
|
|
5489
5804
|
return () => window.clearInterval(timer);
|
|
5490
5805
|
}, [provision?.attemptId, provision?.status]);
|
|
5491
|
-
const startProvisioning =
|
|
5806
|
+
const startProvisioning = React12.useCallback(async (replace = false) => {
|
|
5492
5807
|
setCredentialOpen(false);
|
|
5493
5808
|
setCredentialError(null);
|
|
5494
5809
|
setBusy(true);
|
|
@@ -5506,7 +5821,8 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5506
5821
|
if (mounted.current) setBusy(false);
|
|
5507
5822
|
}
|
|
5508
5823
|
}, [invoke, provision?.attemptId]);
|
|
5509
|
-
const bindCredentials =
|
|
5824
|
+
const bindCredentials = React12.useCallback(async ({ identity, secret }) => {
|
|
5825
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
5510
5826
|
setBusy(true);
|
|
5511
5827
|
setCredentialError(null);
|
|
5512
5828
|
try {
|
|
@@ -5515,15 +5831,19 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5515
5831
|
{ botId: identity, secret }
|
|
5516
5832
|
));
|
|
5517
5833
|
if (!mounted.current) return;
|
|
5518
|
-
|
|
5834
|
+
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
5835
|
+
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
5836
|
+
}
|
|
5519
5837
|
setCredentialOpen(false);
|
|
5520
5838
|
} catch (error) {
|
|
5521
5839
|
if (mounted.current) setCredentialError(presentError7(error));
|
|
5522
5840
|
} finally {
|
|
5841
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
5842
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
5523
5843
|
if (mounted.current) setBusy(false);
|
|
5524
5844
|
}
|
|
5525
|
-
}, [invoke]);
|
|
5526
|
-
const closeProvision =
|
|
5845
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
5846
|
+
const closeProvision = React12.useCallback(async () => {
|
|
5527
5847
|
setBusy(true);
|
|
5528
5848
|
try {
|
|
5529
5849
|
if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
|
|
@@ -5534,7 +5854,7 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5534
5854
|
if (mounted.current) setBusy(false);
|
|
5535
5855
|
}
|
|
5536
5856
|
}, [invoke, provision?.attemptId, provision?.status]);
|
|
5537
|
-
|
|
5857
|
+
React12.useEffect(() => {
|
|
5538
5858
|
const attemptId = provision?.attemptId;
|
|
5539
5859
|
if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
|
|
5540
5860
|
const controller = new AbortController();
|
|
@@ -5564,20 +5884,25 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5564
5884
|
window.clearTimeout(timer);
|
|
5565
5885
|
};
|
|
5566
5886
|
}, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
|
|
5567
|
-
const botAction =
|
|
5887
|
+
const botAction = React12.useCallback(async (account, operation, endpoint, payload) => {
|
|
5888
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
5568
5889
|
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
|
5569
5890
|
try {
|
|
5570
5891
|
const snapshot = normalizeSnapshot6(await invoke(endpoint, payload));
|
|
5571
|
-
if (mounted.current
|
|
5892
|
+
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
5893
|
+
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
5894
|
+
}
|
|
5572
5895
|
return snapshot;
|
|
5573
5896
|
} finally {
|
|
5897
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
5898
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
5574
5899
|
if (mounted.current) setBusyByBot((current) => {
|
|
5575
5900
|
const next = { ...current };
|
|
5576
5901
|
delete next[account.botId];
|
|
5577
5902
|
return next;
|
|
5578
5903
|
});
|
|
5579
5904
|
}
|
|
5580
|
-
}, [invoke]);
|
|
5905
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
5581
5906
|
let provisionView = null;
|
|
5582
5907
|
if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u2026");
|
|
5583
5908
|
else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel3, {
|
|
@@ -5606,6 +5931,12 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5606
5931
|
busy: busyByBot[account.botId],
|
|
5607
5932
|
removing: removeTarget === account.botId,
|
|
5608
5933
|
onReconnect: () => void botAction(account, "reconnect", WECOM_ENDPOINTS.reconnectBot, { botId: account.botId }),
|
|
5934
|
+
onWorkspaceSave: (workspace) => botAction(
|
|
5935
|
+
account,
|
|
5936
|
+
"workspace",
|
|
5937
|
+
WECOM_ENDPOINTS.setWorkspace,
|
|
5938
|
+
{ botId: account.botId, workspace }
|
|
5939
|
+
),
|
|
5609
5940
|
onRequestRemove: () => setRemoveTarget(account.botId),
|
|
5610
5941
|
onCancelRemove: () => setRemoveTarget(null),
|
|
5611
5942
|
onConfirmRemove: async () => {
|
|
@@ -5644,7 +5975,7 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5644
5975
|
addButtonRef
|
|
5645
5976
|
}),
|
|
5646
5977
|
model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button9, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
|
|
5647
|
-
|
|
5978
|
+
React12.Fragment,
|
|
5648
5979
|
null,
|
|
5649
5980
|
credentialView,
|
|
5650
5981
|
provisionView,
|
|
@@ -5655,7 +5986,7 @@ function WecomSettingsTab({ rpcCall }) {
|
|
|
5655
5986
|
}
|
|
5656
5987
|
|
|
5657
5988
|
// plugin-src/client/channels/weixin/index.js
|
|
5658
|
-
var
|
|
5989
|
+
var React13 = __toESM(require("react"), 1);
|
|
5659
5990
|
|
|
5660
5991
|
// plugin-src/client/channels/weixin/api.js
|
|
5661
5992
|
var WEIXIN_RPC_CHANNEL = "/weixin";
|
|
@@ -5666,7 +5997,8 @@ var WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
5666
5997
|
submitVerification: "provision.verify",
|
|
5667
5998
|
cancelProvisioning: "provision.cancel",
|
|
5668
5999
|
reconnectBot: "bot.reconnect",
|
|
5669
|
-
deleteBot: "bot.delete"
|
|
6000
|
+
deleteBot: "bot.delete",
|
|
6001
|
+
setWorkspace: "bot.workspace.set"
|
|
5670
6002
|
});
|
|
5671
6003
|
var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
|
5672
6004
|
var PROVISION_STATES4 = /* @__PURE__ */ new Set([
|
|
@@ -5748,6 +6080,7 @@ function normalizeBot5(value) {
|
|
|
5748
6080
|
state: connected ? "connected" : state,
|
|
5749
6081
|
connected,
|
|
5750
6082
|
configured: value.configured === true,
|
|
6083
|
+
workspace: string(value.workspace).slice(0, 4096),
|
|
5751
6084
|
bot: {
|
|
5752
6085
|
name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
|
|
5753
6086
|
accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
|
|
@@ -5918,7 +6251,7 @@ function installWeixinStyles() {
|
|
|
5918
6251
|
}
|
|
5919
6252
|
|
|
5920
6253
|
// plugin-src/client/channels/weixin/index.js
|
|
5921
|
-
var Button11 =
|
|
6254
|
+
var Button11 = React13.forwardRef(function Button12({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
5922
6255
|
return h2("button", {
|
|
5923
6256
|
...props,
|
|
5924
6257
|
ref,
|
|
@@ -5991,14 +6324,14 @@ function EmptyView5({ onStart, busy }) {
|
|
|
5991
6324
|
);
|
|
5992
6325
|
}
|
|
5993
6326
|
function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
|
|
5994
|
-
const [imageFailed, setImageFailed] =
|
|
6327
|
+
const [imageFailed, setImageFailed] = React13.useState(false);
|
|
5995
6328
|
const source = safeQrSource5(provision.qrCodeDataUrl);
|
|
5996
6329
|
const href = safeVerificationUrl(provision.verificationUrl);
|
|
5997
6330
|
const remaining = Math.max(0, provision.expiresAt - now);
|
|
5998
6331
|
const expired = remaining === 0 || provision.status === "expired";
|
|
5999
6332
|
const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
|
|
6000
6333
|
const progress = Math.round(Math.min(1, remaining / duration) * 100);
|
|
6001
|
-
|
|
6334
|
+
React13.useEffect(() => setImageFailed(false), [source]);
|
|
6002
6335
|
return h2(
|
|
6003
6336
|
"div",
|
|
6004
6337
|
{ className: "dxw-card dim-surfaceCard" },
|
|
@@ -6065,9 +6398,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
|
|
|
6065
6398
|
);
|
|
6066
6399
|
}
|
|
6067
6400
|
function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
|
|
6068
|
-
const [code, setCode] =
|
|
6401
|
+
const [code, setCode] = React13.useState("");
|
|
6069
6402
|
const valid = /^\d{4,8}$/.test(code);
|
|
6070
|
-
|
|
6403
|
+
React13.useEffect(() => setCode(""), [provision.attemptId]);
|
|
6071
6404
|
return h2(
|
|
6072
6405
|
"div",
|
|
6073
6406
|
{ className: "dxw-card dim-surfaceCard" },
|
|
@@ -6158,7 +6491,16 @@ function checkedTime5(timestamp7) {
|
|
|
6158
6491
|
return "\u521A\u521A";
|
|
6159
6492
|
}
|
|
6160
6493
|
}
|
|
6161
|
-
function AccountCard4({
|
|
6494
|
+
function AccountCard4({
|
|
6495
|
+
account,
|
|
6496
|
+
busy,
|
|
6497
|
+
removing,
|
|
6498
|
+
onReconnect,
|
|
6499
|
+
onWorkspaceSave,
|
|
6500
|
+
onRequestRemove,
|
|
6501
|
+
onConfirmRemove,
|
|
6502
|
+
onCancelRemove
|
|
6503
|
+
}) {
|
|
6162
6504
|
const state = busy === "reconnect" ? "connecting" : account.state;
|
|
6163
6505
|
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
|
6164
6506
|
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
|
@@ -6200,6 +6542,11 @@ function AccountCard4({ account, busy, removing, onReconnect, onRequestRemove, o
|
|
|
6200
6542
|
h2("dd", null, checkedTime5(account.health.lastCheckedAt))
|
|
6201
6543
|
)
|
|
6202
6544
|
),
|
|
6545
|
+
h2(WorkspaceEditor, {
|
|
6546
|
+
workspace: account.workspace,
|
|
6547
|
+
disabled: Boolean(busy),
|
|
6548
|
+
onSave: onWorkspaceSave
|
|
6549
|
+
}),
|
|
6203
6550
|
h2(
|
|
6204
6551
|
"div",
|
|
6205
6552
|
{ className: "dxw-accountFooter dim-cardFooter" },
|
|
@@ -6247,6 +6594,7 @@ function AccountList2(props) {
|
|
|
6247
6594
|
busy: props.busyByBot[account.botId],
|
|
6248
6595
|
removing: props.removeTarget === account.botId,
|
|
6249
6596
|
onReconnect: () => props.onReconnect(account),
|
|
6597
|
+
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
|
6250
6598
|
onRequestRemove: () => props.onRequestRemove(account),
|
|
6251
6599
|
onConfirmRemove: () => props.onConfirmRemove(account),
|
|
6252
6600
|
onCancelRemove: props.onCancelRemove
|
|
@@ -6265,39 +6613,49 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
|
|
|
6265
6613
|
};
|
|
6266
6614
|
}
|
|
6267
6615
|
function WeixinSettingsTab({ rpcCall }) {
|
|
6268
|
-
const [model, setModel] =
|
|
6616
|
+
const [model, setModel] = React13.useState({
|
|
6269
6617
|
phase: "loading",
|
|
6270
6618
|
bots: [],
|
|
6271
6619
|
totals: EMPTY_TOTALS3,
|
|
6272
6620
|
revision: 0,
|
|
6273
6621
|
error: null
|
|
6274
6622
|
});
|
|
6275
|
-
const [provision, setProvision] =
|
|
6276
|
-
const [busy, setBusy] =
|
|
6277
|
-
const [busyByBot, setBusyByBot] =
|
|
6278
|
-
const [removeTarget, setRemoveTarget] =
|
|
6279
|
-
const [notice, setNotice] =
|
|
6280
|
-
const [now, setNow] =
|
|
6281
|
-
const addButtonRef =
|
|
6623
|
+
const [provision, setProvision] = React13.useState(null);
|
|
6624
|
+
const [busy, setBusy] = React13.useState(false);
|
|
6625
|
+
const [busyByBot, setBusyByBot] = React13.useState({});
|
|
6626
|
+
const [removeTarget, setRemoveTarget] = React13.useState(null);
|
|
6627
|
+
const [notice, setNotice] = React13.useState("");
|
|
6628
|
+
const [now, setNow] = React13.useState(() => Date.now());
|
|
6629
|
+
const addButtonRef = React13.useRef(null);
|
|
6630
|
+
const mountedRef = React13.useRef(true);
|
|
6631
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
6282
6632
|
const scheduleAnimationFrame = useAnimationFrameScheduler();
|
|
6283
|
-
|
|
6633
|
+
React13.useEffect(() => {
|
|
6634
|
+
mountedRef.current = true;
|
|
6635
|
+
return () => {
|
|
6636
|
+
mountedRef.current = false;
|
|
6637
|
+
};
|
|
6638
|
+
}, []);
|
|
6639
|
+
const announce = React13.useCallback((value) => {
|
|
6284
6640
|
setNotice("");
|
|
6285
6641
|
scheduleAnimationFrame(() => {
|
|
6286
6642
|
if (value) setNotice(value);
|
|
6287
6643
|
}, "announcement");
|
|
6288
6644
|
}, [scheduleAnimationFrame]);
|
|
6289
|
-
const invoke =
|
|
6645
|
+
const invoke = React13.useCallback(async (endpoint, payload = {}, signal) => {
|
|
6290
6646
|
return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
|
|
6291
6647
|
}, [rpcCall]);
|
|
6292
|
-
const loadStatus =
|
|
6648
|
+
const loadStatus = React13.useCallback(async ({
|
|
6293
6649
|
signal,
|
|
6294
6650
|
silent = false,
|
|
6295
6651
|
restoreProvisioning = false
|
|
6296
6652
|
} = {}) => {
|
|
6653
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
6654
|
+
if (workspaceVersion === null || !mountedRef.current) return void 0;
|
|
6297
6655
|
if (!silent) setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
6298
6656
|
try {
|
|
6299
6657
|
const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.status, {}, signal));
|
|
6300
|
-
if (signal?.aborted) return void 0;
|
|
6658
|
+
if (signal?.aborted || !mountedRef.current || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
6301
6659
|
setModel({
|
|
6302
6660
|
phase: "ready",
|
|
6303
6661
|
bots: snapshot.bots,
|
|
@@ -6314,7 +6672,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6314
6672
|
}
|
|
6315
6673
|
return snapshot;
|
|
6316
6674
|
} catch (error) {
|
|
6317
|
-
if (signal?.aborted || error?.name === "AbortError") return void 0;
|
|
6675
|
+
if (signal?.aborted || error?.name === "AbortError" || !mountedRef.current || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
6318
6676
|
setModel((current) => ({
|
|
6319
6677
|
...current,
|
|
6320
6678
|
phase: silent && current.phase === "ready" ? "ready" : "error",
|
|
@@ -6322,13 +6680,13 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6322
6680
|
}));
|
|
6323
6681
|
return void 0;
|
|
6324
6682
|
}
|
|
6325
|
-
}, [invoke]);
|
|
6326
|
-
|
|
6683
|
+
}, [invoke, workspaceFence]);
|
|
6684
|
+
React13.useEffect(() => {
|
|
6327
6685
|
const controller = new AbortController();
|
|
6328
6686
|
void loadStatus({ signal: controller.signal, restoreProvisioning: true });
|
|
6329
6687
|
return () => controller.abort();
|
|
6330
6688
|
}, [loadStatus]);
|
|
6331
|
-
|
|
6689
|
+
React13.useEffect(() => {
|
|
6332
6690
|
if (model.phase !== "ready") return void 0;
|
|
6333
6691
|
const controller = new AbortController();
|
|
6334
6692
|
let running = false;
|
|
@@ -6347,12 +6705,12 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6347
6705
|
window.clearInterval(timer);
|
|
6348
6706
|
};
|
|
6349
6707
|
}, [loadStatus, model.phase]);
|
|
6350
|
-
|
|
6708
|
+
React13.useEffect(() => {
|
|
6351
6709
|
if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
|
|
6352
6710
|
const timer = window.setInterval(() => setNow(Date.now()), 1e3);
|
|
6353
6711
|
return () => window.clearInterval(timer);
|
|
6354
6712
|
}, [provision?.attemptId, provision?.status]);
|
|
6355
|
-
const startProvisioning =
|
|
6713
|
+
const startProvisioning = React13.useCallback(async ({ replace = false } = {}) => {
|
|
6356
6714
|
setBusy(true);
|
|
6357
6715
|
try {
|
|
6358
6716
|
if (replace && provision?.attemptId) {
|
|
@@ -6373,7 +6731,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6373
6731
|
setBusy(false);
|
|
6374
6732
|
}
|
|
6375
6733
|
}, [announce, invoke, provision?.attemptId]);
|
|
6376
|
-
const cancelProvisioning =
|
|
6734
|
+
const cancelProvisioning = React13.useCallback(async () => {
|
|
6377
6735
|
setBusy(true);
|
|
6378
6736
|
try {
|
|
6379
6737
|
if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
|
|
@@ -6388,7 +6746,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6388
6746
|
setBusy(false);
|
|
6389
6747
|
}
|
|
6390
6748
|
}, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
|
|
6391
|
-
const submitVerification =
|
|
6749
|
+
const submitVerification = React13.useCallback(async (verifyCode) => {
|
|
6392
6750
|
if (!provision?.attemptId) return;
|
|
6393
6751
|
setBusy(true);
|
|
6394
6752
|
try {
|
|
@@ -6404,7 +6762,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6404
6762
|
setBusy(false);
|
|
6405
6763
|
}
|
|
6406
6764
|
}, [announce, invoke, provision?.attemptId]);
|
|
6407
|
-
|
|
6765
|
+
React13.useEffect(() => {
|
|
6408
6766
|
const attemptId = provision?.attemptId;
|
|
6409
6767
|
if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
|
|
6410
6768
|
const controller = new AbortController();
|
|
@@ -6452,7 +6810,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6452
6810
|
controller.abort();
|
|
6453
6811
|
};
|
|
6454
6812
|
}, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
|
|
6455
|
-
const setBotBusy =
|
|
6813
|
+
const setBotBusy = React13.useCallback((botId, value) => {
|
|
6456
6814
|
setBusyByBot((current) => {
|
|
6457
6815
|
const next = { ...current };
|
|
6458
6816
|
if (value) next[botId] = value;
|
|
@@ -6460,35 +6818,68 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6460
6818
|
return next;
|
|
6461
6819
|
});
|
|
6462
6820
|
}, []);
|
|
6463
|
-
const reconnect =
|
|
6821
|
+
const reconnect = React13.useCallback(async (account) => {
|
|
6822
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
6464
6823
|
setBotBusy(account.botId, "reconnect");
|
|
6465
6824
|
try {
|
|
6466
6825
|
const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.reconnectBot, { botId: account.botId }));
|
|
6467
|
-
|
|
6826
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
6827
|
+
setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
|
|
6828
|
+
}
|
|
6468
6829
|
const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
|
|
6469
6830
|
announce(refreshed?.connected ? "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002" : "\u5FAE\u4FE1\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002");
|
|
6470
6831
|
} catch (error) {
|
|
6471
6832
|
announce(`\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF1A${presentError8(error).message}`);
|
|
6472
6833
|
} finally {
|
|
6834
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
6835
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
6473
6836
|
setBotBusy(account.botId, null);
|
|
6474
6837
|
}
|
|
6475
|
-
}, [announce, invoke, setBotBusy]);
|
|
6476
|
-
const
|
|
6838
|
+
}, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
|
|
6839
|
+
const saveWorkspace = React13.useCallback(async (account, workspace) => {
|
|
6840
|
+
const workspaceVersion = workspaceFence.beginMutation();
|
|
6841
|
+
setBotBusy(account.botId, "workspace");
|
|
6842
|
+
try {
|
|
6843
|
+
const snapshot = normalizeSnapshot7(await invoke(
|
|
6844
|
+
WEIXIN_ENDPOINTS.setWorkspace,
|
|
6845
|
+
{ botId: account.botId, workspace }
|
|
6846
|
+
));
|
|
6847
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(workspaceVersion)) {
|
|
6848
|
+
setModel({
|
|
6849
|
+
phase: "ready",
|
|
6850
|
+
bots: snapshot.bots,
|
|
6851
|
+
totals: snapshot.totals,
|
|
6852
|
+
revision: snapshot.revision,
|
|
6853
|
+
error: null
|
|
6854
|
+
});
|
|
6855
|
+
}
|
|
6856
|
+
} finally {
|
|
6857
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
6858
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
6859
|
+
if (mountedRef.current) setBotBusy(account.botId, null);
|
|
6860
|
+
}
|
|
6861
|
+
}, [invoke, loadStatus, setBotBusy, workspaceFence]);
|
|
6862
|
+
const remove = React13.useCallback(async (account) => {
|
|
6863
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
6477
6864
|
setBotBusy(account.botId, "delete");
|
|
6478
6865
|
try {
|
|
6479
6866
|
const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.deleteBot, {
|
|
6480
6867
|
botId: account.botId,
|
|
6481
6868
|
confirm: true
|
|
6482
6869
|
}));
|
|
6483
|
-
|
|
6870
|
+
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
6871
|
+
setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
|
|
6872
|
+
}
|
|
6484
6873
|
setRemoveTarget(null);
|
|
6485
6874
|
announce("\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002");
|
|
6486
6875
|
} catch (error) {
|
|
6487
6876
|
announce(`\u79FB\u9664\u5931\u8D25\uFF1A${presentError8(error).message}`);
|
|
6488
6877
|
} finally {
|
|
6878
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
6879
|
+
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
|
6489
6880
|
setBotBusy(account.botId, null);
|
|
6490
6881
|
}
|
|
6491
|
-
}, [announce, invoke, setBotBusy]);
|
|
6882
|
+
}, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
|
|
6492
6883
|
let provisionView = null;
|
|
6493
6884
|
if (provision?.status === "starting") {
|
|
6494
6885
|
provisionView = h2(ProgressPanel2, { busy });
|
|
@@ -6544,7 +6935,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6544
6935
|
h2(Button11, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
|
|
6545
6936
|
)
|
|
6546
6937
|
) : h2(
|
|
6547
|
-
|
|
6938
|
+
React13.Fragment,
|
|
6548
6939
|
null,
|
|
6549
6940
|
provisionView,
|
|
6550
6941
|
model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
|
|
@@ -6553,6 +6944,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|
|
6553
6944
|
busyByBot,
|
|
6554
6945
|
removeTarget,
|
|
6555
6946
|
onReconnect: (account) => void reconnect(account),
|
|
6947
|
+
onWorkspaceSave: saveWorkspace,
|
|
6556
6948
|
onRequestRemove: (account) => setRemoveTarget(account.botId),
|
|
6557
6949
|
onConfirmRemove: (account) => void remove(account),
|
|
6558
6950
|
onCancelRemove: () => setRemoveTarget(null)
|
|
@@ -6569,7 +6961,8 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
6569
6961
|
pollProvisioning: "provision.poll",
|
|
6570
6962
|
cancelProvisioning: "provision.cancel",
|
|
6571
6963
|
reconnectBot: "bot.reconnect",
|
|
6572
|
-
deleteBot: "bot.delete"
|
|
6964
|
+
deleteBot: "bot.delete",
|
|
6965
|
+
setWorkspace: "bot.workspace.set"
|
|
6573
6966
|
});
|
|
6574
6967
|
var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
|
|
6575
6968
|
var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
|
@@ -6633,6 +7026,7 @@ function normalizeBot6(value) {
|
|
|
6633
7026
|
botId: id4(value.botId),
|
|
6634
7027
|
connected,
|
|
6635
7028
|
state: connected ? "connected" : state,
|
|
7029
|
+
workspace: text4(value.workspace, "", 4096),
|
|
6636
7030
|
bot: {
|
|
6637
7031
|
name: text4(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
|
|
6638
7032
|
idMasked: text4(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
|
|
@@ -6672,7 +7066,7 @@ function formatRemaining6(milliseconds) {
|
|
|
6672
7066
|
}
|
|
6673
7067
|
|
|
6674
7068
|
// plugin-src/client/channels/whatsapp/index.js
|
|
6675
|
-
var
|
|
7069
|
+
var React14 = __toESM(require("react"), 1);
|
|
6676
7070
|
|
|
6677
7071
|
// plugin-src/client/channels/whatsapp/styles.js
|
|
6678
7072
|
var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
|
|
@@ -6697,7 +7091,7 @@ function installWhatsappStyles() {
|
|
|
6697
7091
|
|
|
6698
7092
|
// plugin-src/client/channels/whatsapp/index.js
|
|
6699
7093
|
var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
|
|
6700
|
-
var Button13 =
|
|
7094
|
+
var Button13 = React14.forwardRef(function Button14({ children, kind = "secondary", className = "", ...props }, ref) {
|
|
6701
7095
|
return h2("button", {
|
|
6702
7096
|
...props,
|
|
6703
7097
|
ref,
|
|
@@ -6908,6 +7302,7 @@ function WhatsappAccountCard({
|
|
|
6908
7302
|
busy,
|
|
6909
7303
|
removing,
|
|
6910
7304
|
onReconnect,
|
|
7305
|
+
onWorkspaceSave,
|
|
6911
7306
|
onRequestRemove,
|
|
6912
7307
|
onConfirmRemove,
|
|
6913
7308
|
onCancelRemove
|
|
@@ -6962,6 +7357,11 @@ function WhatsappAccountCard({
|
|
|
6962
7357
|
h2("dd", null, checkedTime6(account.health.lastCheckedAt))
|
|
6963
7358
|
)
|
|
6964
7359
|
),
|
|
7360
|
+
h2(WorkspaceEditor, {
|
|
7361
|
+
workspace: account.workspace,
|
|
7362
|
+
disabled: Boolean(busy),
|
|
7363
|
+
onSave: onWorkspaceSave
|
|
7364
|
+
}),
|
|
6965
7365
|
h2(
|
|
6966
7366
|
"div",
|
|
6967
7367
|
{ className: "ddt-accountFooter dim-cardFooter" },
|
|
@@ -6992,20 +7392,21 @@ function WhatsappAccountCard({
|
|
|
6992
7392
|
);
|
|
6993
7393
|
}
|
|
6994
7394
|
function WhatsappSettingsTab({ rpcCall }) {
|
|
6995
|
-
const [model, setModel] =
|
|
7395
|
+
const [model, setModel] = React14.useState({
|
|
6996
7396
|
phase: "loading",
|
|
6997
7397
|
bots: [],
|
|
6998
7398
|
totals: { configured: 0, connected: 0 },
|
|
6999
7399
|
error: null
|
|
7000
7400
|
});
|
|
7001
|
-
const [provision, setProvision] =
|
|
7002
|
-
const [busy, setBusy] =
|
|
7003
|
-
const [busyByBot, setBusyByBot] =
|
|
7004
|
-
const [removeTarget, setRemoveTarget] =
|
|
7005
|
-
const [now, setNow] =
|
|
7006
|
-
const mounted =
|
|
7007
|
-
const
|
|
7008
|
-
|
|
7401
|
+
const [provision, setProvision] = React14.useState(null);
|
|
7402
|
+
const [busy, setBusy] = React14.useState(false);
|
|
7403
|
+
const [busyByBot, setBusyByBot] = React14.useState({});
|
|
7404
|
+
const [removeTarget, setRemoveTarget] = React14.useState(null);
|
|
7405
|
+
const [now, setNow] = React14.useState(Date.now());
|
|
7406
|
+
const mounted = React14.useRef(true);
|
|
7407
|
+
const workspaceFence = useWorkspaceSnapshotFence();
|
|
7408
|
+
const addButtonRef = React14.useRef(null);
|
|
7409
|
+
React14.useEffect(() => {
|
|
7009
7410
|
const disposeDingtalk = installDingtalkStyles();
|
|
7010
7411
|
const disposeWhatsapp = installWhatsappStyles();
|
|
7011
7412
|
mounted.current = true;
|
|
@@ -7015,15 +7416,17 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7015
7416
|
disposeDingtalk();
|
|
7016
7417
|
};
|
|
7017
7418
|
}, []);
|
|
7018
|
-
const invoke =
|
|
7419
|
+
const invoke = React14.useCallback(async (endpoint, payload = {}, signal) => {
|
|
7019
7420
|
if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
|
|
7020
7421
|
return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
|
|
7021
7422
|
}, [rpcCall]);
|
|
7022
|
-
const loadStatus =
|
|
7423
|
+
const loadStatus = React14.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
|
|
7424
|
+
const workspaceVersion = workspaceFence.beginStatus();
|
|
7425
|
+
if (workspaceVersion === null) return void 0;
|
|
7023
7426
|
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
|
|
7024
7427
|
try {
|
|
7025
7428
|
const snapshot = normalizeSnapshot8(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
|
|
7026
|
-
if (!mounted.current || signal?.aborted) return void 0;
|
|
7429
|
+
if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
|
|
7027
7430
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
7028
7431
|
if (restore && snapshot.provisioning) setProvision({
|
|
7029
7432
|
...snapshot.provisioning,
|
|
@@ -7031,7 +7434,7 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7031
7434
|
});
|
|
7032
7435
|
return snapshot;
|
|
7033
7436
|
} catch (error) {
|
|
7034
|
-
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted) {
|
|
7437
|
+
if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
|
|
7035
7438
|
setModel((current) => ({
|
|
7036
7439
|
...current,
|
|
7037
7440
|
phase: silent ? current.phase : "error",
|
|
@@ -7040,13 +7443,13 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7040
7443
|
}
|
|
7041
7444
|
return void 0;
|
|
7042
7445
|
}
|
|
7043
|
-
}, [invoke]);
|
|
7044
|
-
|
|
7446
|
+
}, [invoke, workspaceFence]);
|
|
7447
|
+
React14.useEffect(() => {
|
|
7045
7448
|
const controller = new AbortController();
|
|
7046
7449
|
void loadStatus({ signal: controller.signal, restore: true });
|
|
7047
7450
|
return () => controller.abort();
|
|
7048
7451
|
}, [loadStatus]);
|
|
7049
|
-
|
|
7452
|
+
React14.useEffect(() => {
|
|
7050
7453
|
if (model.phase !== "ready") return void 0;
|
|
7051
7454
|
const controller = new AbortController();
|
|
7052
7455
|
const timer = window.setInterval(
|
|
@@ -7058,12 +7461,12 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7058
7461
|
window.clearInterval(timer);
|
|
7059
7462
|
};
|
|
7060
7463
|
}, [loadStatus, model.phase]);
|
|
7061
|
-
|
|
7464
|
+
React14.useEffect(() => {
|
|
7062
7465
|
if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
|
|
7063
7466
|
const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
|
|
7064
7467
|
return () => window.clearInterval(timer);
|
|
7065
7468
|
}, [provision?.attemptId, provision?.status]);
|
|
7066
|
-
const startProvisioning =
|
|
7469
|
+
const startProvisioning = React14.useCallback(async (replace = false) => {
|
|
7067
7470
|
setBusy(true);
|
|
7068
7471
|
try {
|
|
7069
7472
|
if (replace && provision?.attemptId) {
|
|
@@ -7081,7 +7484,7 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7081
7484
|
if (mounted.current) setBusy(false);
|
|
7082
7485
|
}
|
|
7083
7486
|
}, [invoke, provision?.attemptId]);
|
|
7084
|
-
const closeProvision =
|
|
7487
|
+
const closeProvision = React14.useCallback(async () => {
|
|
7085
7488
|
setBusy(true);
|
|
7086
7489
|
try {
|
|
7087
7490
|
if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
|
|
@@ -7092,7 +7495,7 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7092
7495
|
if (mounted.current) setBusy(false);
|
|
7093
7496
|
}
|
|
7094
7497
|
}, [invoke, provision?.attemptId, provision?.status]);
|
|
7095
|
-
|
|
7498
|
+
React14.useEffect(() => {
|
|
7096
7499
|
const attemptId = provision?.attemptId;
|
|
7097
7500
|
if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
|
|
7098
7501
|
const controller = new AbortController();
|
|
@@ -7133,21 +7536,24 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7133
7536
|
if (timer) window.clearTimeout(timer);
|
|
7134
7537
|
};
|
|
7135
7538
|
}, [invoke, loadStatus, provision?.attemptId, provision?.status]);
|
|
7136
|
-
const botAction =
|
|
7539
|
+
const botAction = React14.useCallback(async (account, operation, endpoint, payload) => {
|
|
7540
|
+
const snapshotVersion = workspaceFence.beginMutation();
|
|
7137
7541
|
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
|
7138
7542
|
try {
|
|
7139
7543
|
const snapshot = normalizeSnapshot8(await invoke(endpoint, payload));
|
|
7140
|
-
if (mounted.current) {
|
|
7544
|
+
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
|
7141
7545
|
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
|
7142
7546
|
}
|
|
7143
7547
|
} finally {
|
|
7548
|
+
const shouldRefresh = workspaceFence.endMutation();
|
|
7549
|
+
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
|
7144
7550
|
if (mounted.current) setBusyByBot((current) => {
|
|
7145
7551
|
const next = { ...current };
|
|
7146
7552
|
delete next[account.botId];
|
|
7147
7553
|
return next;
|
|
7148
7554
|
});
|
|
7149
7555
|
}
|
|
7150
|
-
}, [invoke]);
|
|
7556
|
+
}, [invoke, loadStatus, workspaceFence]);
|
|
7151
7557
|
const botList = model.bots.length > 0 ? h2(
|
|
7152
7558
|
"section",
|
|
7153
7559
|
{ className: "dim-listSection" },
|
|
@@ -7166,6 +7572,12 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7166
7572
|
WHATSAPP_ENDPOINTS.reconnectBot,
|
|
7167
7573
|
{ botId: account.botId }
|
|
7168
7574
|
),
|
|
7575
|
+
onWorkspaceSave: (workspace) => botAction(
|
|
7576
|
+
account,
|
|
7577
|
+
"workspace",
|
|
7578
|
+
WHATSAPP_ENDPOINTS.setWorkspace,
|
|
7579
|
+
{ botId: account.botId, workspace }
|
|
7580
|
+
),
|
|
7169
7581
|
onRequestRemove: () => setRemoveTarget(account.botId),
|
|
7170
7582
|
onCancelRemove: () => setRemoveTarget(null),
|
|
7171
7583
|
onConfirmRemove: async () => {
|
|
@@ -7200,7 +7612,7 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|
|
7200
7612
|
h2(Button13, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
|
|
7201
7613
|
)
|
|
7202
7614
|
) : h2(
|
|
7203
|
-
|
|
7615
|
+
React14.Fragment,
|
|
7204
7616
|
null,
|
|
7205
7617
|
provision?.status === "pending" ? h2(QrPanel5, {
|
|
7206
7618
|
provision,
|
|
@@ -7232,8 +7644,17 @@ var CSS10 = String.raw`
|
|
|
7232
7644
|
box-sizing: border-box;
|
|
7233
7645
|
}
|
|
7234
7646
|
.dim-page *, .dim-page *::before, .dim-page *::after { box-sizing: border-box; }
|
|
7235
|
-
.dim-title { margin: 0 0
|
|
7236
|
-
.dim-
|
|
7647
|
+
.dim-title { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 18px; }
|
|
7648
|
+
.dim-brand { min-width: 0; display: flex; align-items: center; gap: 12px; }
|
|
7649
|
+
.dim-brandLogo { width: 48px; height: 48px; flex: 0 0 48px; display: block; object-fit: contain; filter: grayscale(1) contrast(1.08); }
|
|
7650
|
+
.dim-title p { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 20px; font-weight: 600; white-space: nowrap; }
|
|
7651
|
+
.dim-githubAction { position: relative; display: inline-flex; flex: none; }
|
|
7652
|
+
.dim-githubLink { min-height: 30px; display: inline-flex; align-items: center; gap: 5px; flex: none; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 12px; line-height: normal; font-weight: 560; text-decoration: none; transition: border-color .15s ease, color .15s ease, background .15s ease; }
|
|
7653
|
+
.dim-githubLink:hover { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
7654
|
+
.dim-githubLink:focus-visible { outline: 2px solid color-mix(in srgb, var(--dim-blue) 70%, white); outline-offset: 2px; }
|
|
7655
|
+
.dim-githubArrow { font-size: 13px; line-height: 1; }
|
|
7656
|
+
.dim-githubTooltip { position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 20; width: max-content; max-width: min(220px, 80vw); padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateY(3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
7657
|
+
.dim-githubAction:hover .dim-githubTooltip, .dim-githubAction:focus-within .dim-githubTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
7237
7658
|
.dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
|
|
7238
7659
|
.dim-rail { max-height: 520px; display: grid; align-content: start; gap: 8px; overflow-y: auto; padding: 1px 4px 1px 1px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-border-l2, #dfe1e5) transparent; }
|
|
7239
7660
|
.dim-rail::-webkit-scrollbar { width: 4px; }
|
|
@@ -7278,7 +7699,7 @@ var CSS10 = String.raw`
|
|
|
7278
7699
|
.dim-panel .dim-credentialButton:hover:not(:disabled) { border-color: #4e5969; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
7279
7700
|
.dim-panel .dim-credentialButton[aria-pressed="true"] { border-color: #4e5969; background: var(--dsw-alias-bg-module-platform, #f2f3f5); box-shadow: inset 0 0 0 1px rgb(78 89 105 / 8%); }
|
|
7280
7701
|
.dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { min-height: 30px; display: inline-flex; align-items: center; justify-self: end; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
|
7281
|
-
.dim-panel .dim-channelPage { width: 100%; max-width: none; display: flex; flex-direction: column; gap:
|
|
7702
|
+
.dim-panel .dim-channelPage { width: 100%; max-width: none; display: flex; flex-direction: column; gap: 12px; padding: 0 0 24px; color: var(--dsw-alias-label-primary, #1f2329); box-sizing: border-box; }
|
|
7282
7703
|
.dim-panel .dim-surfaceCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
7283
7704
|
.dim-panel .dim-surfaceCard::before { display: none; }
|
|
7284
7705
|
.dim-panel .dim-surfaceBody { padding: 24px; }
|
|
@@ -7294,9 +7715,9 @@ var CSS10 = String.raw`
|
|
|
7294
7715
|
.dim-panel .dim-credentialError { margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.5; }
|
|
7295
7716
|
.dim-panel .dim-credentialActions { margin-top: 0; }
|
|
7296
7717
|
.dim-panel .dim-listSection { display: flex; flex-direction: column; gap: 0; }
|
|
7297
|
-
.dim-panel .dim-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin:
|
|
7718
|
+
.dim-panel .dim-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 6px; padding: 0; }
|
|
7298
7719
|
.dim-panel .dim-listHeading h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: normal; font-weight: 650; }
|
|
7299
|
-
.dim-panel .dim-botList { display: grid; gap:
|
|
7720
|
+
.dim-panel .dim-botList { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
|
|
7300
7721
|
.dim-panel .dim-loadingView { padding: 38px; color: var(--dsw-alias-label-secondary, #646a73); text-align: center; }
|
|
7301
7722
|
.dim-panel .dim-loadingView h3 { margin: 0 0 7px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: normal; font-weight: 650; }
|
|
7302
7723
|
.dim-panel .dim-loadingView p { margin: 0; line-height: 1.6; }
|
|
@@ -7344,7 +7765,20 @@ var CSS10 = String.raw`
|
|
|
7344
7765
|
.dim-panel .dim-confirm { padding: 18px 24px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
7345
7766
|
.dim-panel .dim-confirm strong, .dim-panel .dim-confirm h4 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 1.4; font-weight: 650; }
|
|
7346
7767
|
.dim-panel .dim-confirm p { margin: 7px 0 0; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.6; }
|
|
7347
|
-
.dim-panel .dim-cardFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top:
|
|
7768
|
+
.dim-panel .dim-cardFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
7769
|
+
.dim-panel .dim-workspace { min-width: 0; display: grid; gap: 7px; margin-top: 14px; padding: 11px 12px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
|
7770
|
+
.dim-panel .dim-workspaceHeader { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
|
|
7771
|
+
.dim-panel .dim-workspaceEdit { padding: 0; border: 0; color: #1677ff; background: transparent; font: inherit; font-weight: 560; cursor: pointer; }
|
|
7772
|
+
.dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
|
|
7773
|
+
.dim-panel .dim-workspacePath { min-width: 0; display: block; overflow: hidden; color: var(--dsw-alias-label-primary, #1f2329); font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
|
|
7774
|
+
.dim-panel .dim-workspaceForm { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 8px; }
|
|
7775
|
+
.dim-panel .dim-workspaceForm input { width: 100%; min-width: 0; height: 34px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
7776
|
+
.dim-panel .dim-workspaceForm input:focus { border-color: #1677ff; box-shadow: 0 0 0 2px rgb(22 119 255 / 10%); }
|
|
7777
|
+
.dim-panel .dim-workspaceActions { display: flex; gap: 6px; }
|
|
7778
|
+
.dim-panel .dim-workspaceActions button { min-height: 34px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; cursor: pointer; }
|
|
7779
|
+
.dim-panel .dim-workspaceActions button[type="submit"] { border-color: #1677ff; color: white; background: #1677ff; }
|
|
7780
|
+
.dim-panel .dim-workspaceActions button:disabled { cursor: not-allowed; opacity: .55; }
|
|
7781
|
+
.dim-panel .dim-workspaceError { grid-column: 1 / -1; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.5; }
|
|
7348
7782
|
.dim-panel .dim-cardSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; }
|
|
7349
7783
|
.dim-panel .dim-cardActions { flex: none; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; margin: 0 0 0 auto; }
|
|
7350
7784
|
.dim-panel .dim-cardActions .dim-cardAction { flex: none; min-height: 34px; padding: 0 13px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
|
|
@@ -7352,7 +7786,7 @@ var CSS10 = String.raw`
|
|
|
7352
7786
|
.dim-panel .dim-cardActions .dim-cardAction[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
7353
7787
|
.dim-panel .dim-botCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
7354
7788
|
.dim-panel .dim-botCard::before { display: none; }
|
|
7355
|
-
.dim-panel .dim-botCardBody { position: relative; padding:
|
|
7789
|
+
.dim-panel .dim-botCardBody { position: relative; padding: 14px; }
|
|
7356
7790
|
.dim-panel .dim-botCardTop { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
|
7357
7791
|
.dim-panel .dim-botIdentity { min-width: 0; display: flex; align-items: center; gap: 12px; }
|
|
7358
7792
|
.dim-panel .dim-botAvatar { flex: none; width: 42px; height: 42px; display: grid; place-items: center; overflow: hidden; border-radius: 12px; box-shadow: none; }
|
|
@@ -7365,8 +7799,8 @@ var CSS10 = String.raw`
|
|
|
7365
7799
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary, #20a162) 14%, transparent); }
|
|
7366
7800
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="warning"] { background: var(--dsw-alias-state-warn-primary, #d97706); }
|
|
7367
7801
|
.dim-panel .dim-botCard .dim-healthDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
|
|
7368
|
-
.dim-panel .dim-botMetrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin:
|
|
7369
|
-
.dim-panel .dim-botMetric { min-width: 0; padding:
|
|
7802
|
+
.dim-panel .dim-botMetrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 8px 0; }
|
|
7803
|
+
.dim-panel .dim-botMetric { min-width: 0; padding: 8px; border: 0; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
7370
7804
|
.dim-panel .dim-botMetric dt { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; font-weight: 400; line-height: normal; }
|
|
7371
7805
|
.dim-panel .dim-botMetric dd { overflow: hidden; margin: 5px 0 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; font-weight: 400; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
7372
7806
|
.dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
|
|
@@ -7403,6 +7837,7 @@ var CSS10 = String.raw`
|
|
|
7403
7837
|
@media (max-width: 560px) {
|
|
7404
7838
|
.dim-title { flex-direction: column; gap: 10px; }
|
|
7405
7839
|
.dim-title p { white-space: normal; }
|
|
7840
|
+
.dim-githubTooltip { right: auto; left: 0; }
|
|
7406
7841
|
.dim-rail { grid-template-columns: minmax(0, 1fr); }
|
|
7407
7842
|
}
|
|
7408
7843
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -7426,6 +7861,7 @@ function installImStyles() {
|
|
|
7426
7861
|
// plugin-src/client/index.js
|
|
7427
7862
|
var name = "im-settings";
|
|
7428
7863
|
var inject = ["slots", "connection", "locale"];
|
|
7864
|
+
var IM_PLUGIN_LOGO_URL = "data:image/png;base64,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";
|
|
7429
7865
|
var CHANNELS = Object.freeze([
|
|
7430
7866
|
{ id: "weixin", label: "\u5FAE\u4FE1" },
|
|
7431
7867
|
{ id: "feishu", label: "\u98DE\u4E66" },
|
|
@@ -7514,7 +7950,8 @@ function IMSettingsTab({
|
|
|
7514
7950
|
weixinRpcCall,
|
|
7515
7951
|
whatsappRpcCall
|
|
7516
7952
|
}) {
|
|
7517
|
-
const [selected, setSelected] =
|
|
7953
|
+
const [selected, setSelected] = React15.useState("weixin");
|
|
7954
|
+
const githubTooltipId = React15.useId();
|
|
7518
7955
|
const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
|
|
7519
7956
|
return h2(
|
|
7520
7957
|
"section",
|
|
@@ -7522,7 +7959,40 @@ function IMSettingsTab({
|
|
|
7522
7959
|
h2(
|
|
7523
7960
|
"header",
|
|
7524
7961
|
{ className: "dim-title" },
|
|
7525
|
-
h2(
|
|
7962
|
+
h2(
|
|
7963
|
+
"div",
|
|
7964
|
+
{ className: "dim-brand" },
|
|
7965
|
+
h2("img", {
|
|
7966
|
+
className: "dim-brandLogo",
|
|
7967
|
+
src: IM_PLUGIN_LOGO_URL,
|
|
7968
|
+
alt: "dsh-im",
|
|
7969
|
+
width: 48,
|
|
7970
|
+
height: 48
|
|
7971
|
+
}),
|
|
7972
|
+
h2("p", null, "\u8BA9\u804A\u5929\u673A\u5668\u4EBA\u8F7B\u677E\u63A5\u5165 DeepSeek Harness")
|
|
7973
|
+
),
|
|
7974
|
+
h2(
|
|
7975
|
+
"span",
|
|
7976
|
+
{ className: "dim-githubAction" },
|
|
7977
|
+
h2(
|
|
7978
|
+
"a",
|
|
7979
|
+
{
|
|
7980
|
+
className: "dim-githubLink",
|
|
7981
|
+
href: "https://github.com/xmanrui/dsh-im",
|
|
7982
|
+
target: "_blank",
|
|
7983
|
+
rel: "noopener noreferrer",
|
|
7984
|
+
"aria-label": "dsh-im GitHub",
|
|
7985
|
+
"aria-describedby": githubTooltipId
|
|
7986
|
+
},
|
|
7987
|
+
h2("span", null, "GitHub"),
|
|
7988
|
+
h2("span", { className: "dim-githubArrow", "aria-hidden": "true" }, "\u2197")
|
|
7989
|
+
),
|
|
7990
|
+
h2("span", {
|
|
7991
|
+
id: githubTooltipId,
|
|
7992
|
+
className: "dim-githubTooltip",
|
|
7993
|
+
role: "tooltip"
|
|
7994
|
+
}, "\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub")
|
|
7995
|
+
)
|
|
7526
7996
|
),
|
|
7527
7997
|
h2(
|
|
7528
7998
|
"div",
|