botmux 3.12.1 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.runtime-build-id +1 -1
- package/dist/bot-registry.d.ts +7 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +46 -13
- package/dist/bot-registry.js.map +1 -1
- package/dist/core/config-dir.d.ts +167 -0
- package/dist/core/config-dir.d.ts.map +1 -0
- package/dist/core/config-dir.js +152 -0
- package/dist/core/config-dir.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +48 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/per-bot-env.d.ts.map +1 -1
- package/dist/core/per-bot-env.js +9 -0
- package/dist/core/per-bot-env.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +19 -2
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +44 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +499 -63
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +26 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +64 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +301 -10
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +13 -6
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +8 -0
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +16 -11
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/web/bot-multi-select.d.ts +33 -0
- package/dist/dashboard/web/bot-multi-select.d.ts.map +1 -0
- package/dist/dashboard/web/bot-multi-select.js +34 -0
- package/dist/dashboard/web/bot-multi-select.js.map +1 -0
- package/dist/dashboard/web/groups-page.d.ts +13 -0
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +25 -7
- package/dist/dashboard/web/groups-page.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +8 -4
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +15 -23
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +13 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +25 -0
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-HL655CNR.js → bot-defaults-page-NQJFJ45Z.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-BFUTTYVK.js → chunk-5QJB24I2.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-ELL53D62.js → chunk-6JTZJRQJ.js} +2 -2
- package/dist/dashboard-web/chunks/{chunk-5ZL7NLR5.js → chunk-CU4SUKK3.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-5PAYQ3KT.js → chunk-KEEYQ3WF.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-WLIVBPJC.js +1 -0
- package/dist/dashboard-web/chunks/{connectors-page-3QQACF23.js → connectors-page-HADHZXXZ.js} +1 -1
- package/dist/dashboard-web/chunks/groups-page-5TYF46RS.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-ZZ3JEJ6L.js → insights-page-EVGGJI5L.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-ROJ5GZ4N.js → monitor-room-HRGR2ENP.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-IATV7KJF.js → monitoring-page-LBU7TTTW.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-TYO5OIIT.js → office-page-DF6RRBMN.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-VJUULZF2.js → overview-page-ZB6QH2EX.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-7RJJKZGK.js → roles-page-ZBZGHIIF.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-JTEJ5IXW.js → schedules-page-NQVQC3QY.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-DXBCDWCS.js +8 -0
- package/dist/dashboard-web/chunks/{settings-page-7CTTFBRS.js → settings-page-E7NN4NVQ.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-57NZECJ3.js → skills-page-2QAVB2SA.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-6YYIMJG3.js → team-federation-page-VGWL53AE.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-ZGKBSBK3.js → v3-page-AY35LKCX.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-AACJF6DB.js → whiteboards-page-BNWUU4KG.js} +1 -1
- package/dist/dashboard-web/style.css +61 -53
- package/dist/features/codex-notifier/internal-turn.d.ts +1 -2
- package/dist/features/codex-notifier/internal-turn.d.ts.map +1 -1
- package/dist/features/codex-notifier/internal-turn.js +1 -13
- package/dist/features/codex-notifier/internal-turn.js.map +1 -1
- package/dist/i18n/en.js +2 -2
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.js +2 -2
- package/dist/i18n/zh.js.map +1 -1
- package/dist/platform/secure-host-file.d.ts +63 -0
- package/dist/platform/secure-host-file.d.ts.map +1 -1
- package/dist/platform/secure-host-file.js +90 -25
- package/dist/platform/secure-host-file.js.map +1 -1
- package/dist/services/async-trigger-store.d.ts +24 -4
- package/dist/services/async-trigger-store.d.ts.map +1 -1
- package/dist/services/async-trigger-store.js +81 -19
- package/dist/services/async-trigger-store.js.map +1 -1
- package/dist/services/codex-session-meta.d.ts +4 -0
- package/dist/services/codex-session-meta.d.ts.map +1 -0
- package/dist/services/codex-session-meta.js +15 -0
- package/dist/services/codex-session-meta.js.map +1 -0
- package/dist/services/idempotency-store.d.ts +18 -3
- package/dist/services/idempotency-store.d.ts.map +1 -1
- package/dist/services/idempotency-store.js +30 -12
- package/dist/services/idempotency-store.js.map +1 -1
- package/dist/services/traex-transcript.d.ts +7 -5
- package/dist/services/traex-transcript.d.ts.map +1 -1
- package/dist/services/traex-transcript.js +139 -33
- package/dist/services/traex-transcript.js.map +1 -1
- package/dist/services/trigger-types.d.ts +14 -1
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +35 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +1 -1
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +18 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/worker.js +35 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/chunk-6ALR252E.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-SDZMJ7UV.js +0 -8
- package/dist/dashboard-web/chunks/sessions-page-KD7HDT3U.js +0 -8
package/dist/dashboard/auth.d.ts
CHANGED
|
@@ -61,6 +61,14 @@ export declare function persistToken(tokenPath: string, token: string): void;
|
|
|
61
61
|
* Load the active token, creating and persisting the first one when absent.
|
|
62
62
|
* The file lock makes get-or-create linearizable across dashboard processes:
|
|
63
63
|
* every concurrent caller returns the same durable token.
|
|
64
|
+
*
|
|
65
|
+
* The credential directory is pinned once (Linux: via an open directory
|
|
66
|
+
* descriptor) and the lock, the read, and the write all resolve through that
|
|
67
|
+
* same anchor. This keeps the whole critical section on one directory inode —
|
|
68
|
+
* so a symlinked HOME whose target sits under a shared-drive / 0777 ancestor
|
|
69
|
+
* still succeeds, while an ancestor rename mid-section cannot redirect the lock
|
|
70
|
+
* or the token write into a substituted directory. `~/.botmux` itself must
|
|
71
|
+
* still be 0700 and owned by the current user; a leaf symlink is still refused.
|
|
64
72
|
*/
|
|
65
73
|
export declare function loadOrCreatePersistedToken(tokenPath: string): string;
|
|
66
74
|
/** Generate and durably replace the token while serialized with first creation. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;CAAE;AAExE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEvF;AAED;;gFAEgF;AAChF,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAM5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CA4BlC;AAED,qEAAqE;AACrE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIrE;AAED,gFAAgF;AAChF,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA+CtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUpE;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO1E;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAsBxB,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB;;;;uDAImD;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,YAAY,CAkDf"}
|
package/dist/dashboard/auth.js
CHANGED
|
@@ -2,8 +2,7 @@ import { randomBytes, createHmac, timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { readFileSync, existsSync, mkdirSync, chmodSync, linkSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import { atomicWriteFileSync } from '../utils/atomic-write.js';
|
|
4
4
|
import { dirname } from 'node:path';
|
|
5
|
-
import { readSecureHostFileSync,
|
|
6
|
-
import { withFileLockSync } from '../utils/file-lock.js';
|
|
5
|
+
import { readSecureHostFileSync, withSecureHostParentSync, writeSecureHostFileSync, } from '../platform/secure-host-file.js';
|
|
7
6
|
const NONCE_TTL_MS = 60_000;
|
|
8
7
|
const TS_WINDOW_S = 30;
|
|
9
8
|
const seenNonces = new Map(); // nonce → expiresAt
|
|
@@ -170,26 +169,32 @@ export function persistToken(tokenPath, token) {
|
|
|
170
169
|
* Load the active token, creating and persisting the first one when absent.
|
|
171
170
|
* The file lock makes get-or-create linearizable across dashboard processes:
|
|
172
171
|
* every concurrent caller returns the same durable token.
|
|
172
|
+
*
|
|
173
|
+
* The credential directory is pinned once (Linux: via an open directory
|
|
174
|
+
* descriptor) and the lock, the read, and the write all resolve through that
|
|
175
|
+
* same anchor. This keeps the whole critical section on one directory inode —
|
|
176
|
+
* so a symlinked HOME whose target sits under a shared-drive / 0777 ancestor
|
|
177
|
+
* still succeeds, while an ancestor rename mid-section cannot redirect the lock
|
|
178
|
+
* or the token write into a substituted directory. `~/.botmux` itself must
|
|
179
|
+
* still be 0700 and owned by the current user; a leaf symlink is still refused.
|
|
173
180
|
*/
|
|
174
181
|
export function loadOrCreatePersistedToken(tokenPath) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const existing = loadPersistedToken(securePath);
|
|
182
|
+
return withSecureHostParentSync(tokenPath, (parent) => parent.withLeafLock(() => {
|
|
183
|
+
const existing = parent.readLeaf(256)?.trim() || null;
|
|
178
184
|
if (existing)
|
|
179
185
|
return existing;
|
|
180
186
|
const token = generateToken();
|
|
181
|
-
|
|
187
|
+
parent.writeLeaf(token);
|
|
182
188
|
return token;
|
|
183
|
-
});
|
|
189
|
+
}));
|
|
184
190
|
}
|
|
185
191
|
/** Generate and durably replace the token while serialized with first creation. */
|
|
186
192
|
export function rotatePersistedToken(tokenPath) {
|
|
187
|
-
|
|
188
|
-
return withFileLockSync(securePath, () => {
|
|
193
|
+
return withSecureHostParentSync(tokenPath, (parent) => parent.withLeafLock(() => {
|
|
189
194
|
const token = generateToken();
|
|
190
|
-
|
|
195
|
+
parent.writeLeaf(token);
|
|
191
196
|
return token;
|
|
192
|
-
});
|
|
197
|
+
}));
|
|
193
198
|
}
|
|
194
199
|
/** Extract `botmux_dashboard_token` value from a Cookie header. */
|
|
195
200
|
export function parseCookie(header) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,GACpF,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/dashboard/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/** Minimal bot shape shared by the group picker (`GroupBot`) and the
|
|
3
|
+
* create-session picker (`PickerBot`): both carry `larkAppId` + a display name. */
|
|
4
|
+
export interface BotMultiSelectOption {
|
|
5
|
+
larkAppId: string;
|
|
6
|
+
botName?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Searchable, scrollable multi-select for bots. Fully controlled: the caller
|
|
10
|
+
* owns the `selected` Set and toggles via `onToggle`, and MUST read the final
|
|
11
|
+
* selection from that Set — never from the DOM.
|
|
12
|
+
*
|
|
13
|
+
* Why the caller state is the only source of truth: the list renders only the
|
|
14
|
+
* rows matching the current search query; rows filtered out are unmounted, so
|
|
15
|
+
* their checkboxes leave the DOM entirely. A caller that harvested the checked
|
|
16
|
+
* ids via `FormData.getAll(...)` at submit time would silently drop any bot
|
|
17
|
+
* that was selected earlier but is not matching the search box at that moment.
|
|
18
|
+
* `selected` survives filtering; the DOM does not.
|
|
19
|
+
*
|
|
20
|
+
* The list area has a fixed max-height and scrolls internally, so a long roster
|
|
21
|
+
* (dozens of bots) never grows the surrounding dialog past the viewport — the
|
|
22
|
+
* dialog's own action buttons stay reachable.
|
|
23
|
+
*/
|
|
24
|
+
export declare function BotMultiSelect(props: {
|
|
25
|
+
bots: BotMultiSelectOption[];
|
|
26
|
+
selected: Set<string>;
|
|
27
|
+
onToggle(larkAppId: string, checked: boolean): void;
|
|
28
|
+
searchPlaceholder: string;
|
|
29
|
+
noMatchLabel: string;
|
|
30
|
+
emptyLabel: string;
|
|
31
|
+
selectedCountLabel(n: number): string;
|
|
32
|
+
}): React.JSX.Element;
|
|
33
|
+
//# sourceMappingURL=bot-multi-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-multi-select.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/bot-multi-select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B;oFACoF;AACpF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,IAAI,EAAE,oBAAoB,EAAE,CAAC;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvC,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAkDpB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Searchable, scrollable multi-select for bots. Fully controlled: the caller
|
|
5
|
+
* owns the `selected` Set and toggles via `onToggle`, and MUST read the final
|
|
6
|
+
* selection from that Set — never from the DOM.
|
|
7
|
+
*
|
|
8
|
+
* Why the caller state is the only source of truth: the list renders only the
|
|
9
|
+
* rows matching the current search query; rows filtered out are unmounted, so
|
|
10
|
+
* their checkboxes leave the DOM entirely. A caller that harvested the checked
|
|
11
|
+
* ids via `FormData.getAll(...)` at submit time would silently drop any bot
|
|
12
|
+
* that was selected earlier but is not matching the search box at that moment.
|
|
13
|
+
* `selected` survives filtering; the DOM does not.
|
|
14
|
+
*
|
|
15
|
+
* The list area has a fixed max-height and scrolls internally, so a long roster
|
|
16
|
+
* (dozens of bots) never grows the surrounding dialog past the viewport — the
|
|
17
|
+
* dialog's own action buttons stay reachable.
|
|
18
|
+
*/
|
|
19
|
+
export function BotMultiSelect(props) {
|
|
20
|
+
const [query, setQuery] = useState('');
|
|
21
|
+
const queryNorm = query.trim().toLowerCase();
|
|
22
|
+
const visibleBots = useMemo(() => {
|
|
23
|
+
if (!queryNorm)
|
|
24
|
+
return props.bots;
|
|
25
|
+
return props.bots.filter(bot => (bot.botName ?? bot.larkAppId).toLowerCase().includes(queryNorm) ||
|
|
26
|
+
bot.larkAppId.toLowerCase().includes(queryNorm));
|
|
27
|
+
}, [props.bots, queryNorm]);
|
|
28
|
+
const selectedCount = props.selected.size;
|
|
29
|
+
if (!props.bots.length) {
|
|
30
|
+
return _jsx("p", { className: "bot-multi-select-empty", children: props.emptyLabel });
|
|
31
|
+
}
|
|
32
|
+
return (_jsxs("div", { className: "bot-multi-select", children: [_jsx("input", { className: "bot-multi-select-search", type: "search", placeholder: props.searchPlaceholder, "aria-label": props.searchPlaceholder, value: query, onChange: event => setQuery(event.currentTarget.value) }), visibleBots.length ? (_jsx("div", { className: "bot-multi-select-list", role: "group", children: visibleBots.map(bot => (_jsxs("label", { className: "bot-multi-select-row", children: [_jsx("input", { type: "checkbox", value: bot.larkAppId, checked: props.selected.has(bot.larkAppId), onChange: event => props.onToggle(bot.larkAppId, event.currentTarget.checked) }), _jsxs("span", { className: "bot-multi-select-main", children: [_jsx("strong", { children: bot.botName ?? bot.larkAppId }), _jsxs("small", { children: ["(", bot.larkAppId, ")"] })] })] }, bot.larkAppId))) })) : (_jsx("p", { className: "bot-multi-select-empty", children: props.noMatchLabel })), selectedCount ? (_jsx("small", { className: "bot-multi-select-count", children: props.selectedCountLabel(selectedCount) })) : null] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=bot-multi-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-multi-select.js","sourceRoot":"","sources":["../../../src/dashboard/web/bot-multi-select.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAS1C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,KAQ9B;IACC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/B,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAClC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7B,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;YAChE,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;IAE1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,YAAG,SAAS,EAAC,wBAAwB,YAAE,KAAK,CAAC,UAAU,GAAK,CAAC;IACtE,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,kBAAkB,aAC/B,gBACE,SAAS,EAAC,yBAAyB,EACnC,IAAI,EAAC,QAAQ,EACb,WAAW,EAAE,KAAK,CAAC,iBAAiB,gBACxB,KAAK,CAAC,iBAAiB,EACnC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,GACtD,EACD,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CACpB,cAAK,SAAS,EAAC,uBAAuB,EAAC,IAAI,EAAC,OAAO,YAChD,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CACtB,iBAA2B,SAAS,EAAC,sBAAsB,aACzD,gBACE,IAAI,EAAC,UAAU,EACf,KAAK,EAAE,GAAG,CAAC,SAAS,EACpB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,EAC1C,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,GAC7E,EACF,gBAAM,SAAS,EAAC,uBAAuB,aACrC,2BAAS,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,GAAU,EAC/C,iCAAS,GAAG,CAAC,SAAS,SAAU,IAC3B,KAVG,GAAG,CAAC,SAAS,CAWjB,CACT,CAAC,GACE,CACP,CAAC,CAAC,CAAC,CACF,YAAG,SAAS,EAAC,wBAAwB,YAAE,KAAK,CAAC,YAAY,GAAK,CAC/D,EACA,aAAa,CAAC,CAAC,CAAC,CACf,gBAAO,SAAS,EAAC,wBAAwB,YAAE,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAAC,GAAS,CAC5F,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
import { type PageDisposer } from './react-mount.js';
|
|
2
|
+
import { useT } from './react-hooks.js';
|
|
3
|
+
import { type GroupBot, type GroupChat, type GroupsSnapshot } from './groups.js';
|
|
4
|
+
type Translator = ReturnType<typeof useT>;
|
|
5
|
+
export declare function AddBotsDialog(props: {
|
|
6
|
+
chat: GroupChat;
|
|
7
|
+
bots: GroupBot[];
|
|
8
|
+
tr: Translator;
|
|
9
|
+
onClose(): void;
|
|
10
|
+
onReloadGroups(options?: {
|
|
11
|
+
force?: boolean;
|
|
12
|
+
}): Promise<GroupsSnapshot>;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
2
14
|
export declare function renderGroupsPage(root: HTMLElement): PageDisposer;
|
|
15
|
+
export {};
|
|
3
16
|
//# sourceMappingURL=groups-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups-page.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"groups-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups-page.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAqBxC,OAAO,EAiBL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,cAAc,EAGpB,MAAM,aAAa,CAAC;AAErB,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AA6kB1C,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACxE,+BAuFA;AA21BD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAEhE"}
|
|
@@ -5,6 +5,7 @@ import { useT } from './react-hooks.js';
|
|
|
5
5
|
import { botOrbStyle, chatAvatarUrlFor } from './ui.js';
|
|
6
6
|
import { copyText } from './clipboard.js';
|
|
7
7
|
import { FeedGroupPicker } from './feed-group-picker.js';
|
|
8
|
+
import { BotMultiSelect } from './bot-multi-select.js';
|
|
8
9
|
import { CreateActionButton, DropdownMenu, LoadingState, OverviewList, OverviewListItem, OverviewListMain, OverviewListTail, RefreshIconButton, } from './dashboard-components.js';
|
|
9
10
|
import { hasExplicitChatRole, summarizeGroupProfileMatches, } from './role-profile-match.js';
|
|
10
11
|
import { allExpectedInChat, availableBotsForPicker, collectGroupProfileEntries, emptyGroupsSnapshot, fetchGroupsSnapshot, fetchRoleProfileSummaries, filterGroupChats, injectOptimisticChat, isValidProfileId, loadGroupRoleProfileContext, paginateGroupRows, roleKey, roleProfileBootstrapStatus, summarizeAddBotsResult, suggestRoleProfileIdFromChat, } from './groups.js';
|
|
@@ -41,7 +42,9 @@ function DialogError(props) {
|
|
|
41
42
|
return (_jsxs("p", { className: "hint-warn", children: [_jsx("strong", { children: props.title }), _jsx("br", {}), _jsx("small", { children: String(props.reason ?? 'unknown') })] }));
|
|
42
43
|
}
|
|
43
44
|
function BotCheckboxes(props) {
|
|
44
|
-
|
|
45
|
+
const options = availableBotsForPicker(props.bots, props.excludeIds);
|
|
46
|
+
const tr = props.tr;
|
|
47
|
+
return (_jsx(BotMultiSelect, { bots: options, selected: props.selected, onToggle: props.onToggle, searchPlaceholder: tr('botPicker.searchPlaceholder'), noMatchLabel: tr('botPicker.noMatch'), emptyLabel: tr('botPicker.empty'), selectedCountLabel: n => tr('botPicker.selectedCount', { n: String(n) }) }));
|
|
45
48
|
}
|
|
46
49
|
function AddBotsResult(props) {
|
|
47
50
|
const summary = props.summary;
|
|
@@ -117,6 +120,7 @@ function CreateDialog(props) {
|
|
|
117
120
|
const [error, setError] = useState(null);
|
|
118
121
|
const [success, setSuccess] = useState(null);
|
|
119
122
|
const [copied, setCopied] = useState(false);
|
|
123
|
+
const [selectedBots, setSelectedBots] = useState(new Set());
|
|
120
124
|
const [roleProfileId, setRoleProfileId] = useState('');
|
|
121
125
|
const [feedGroups, setFeedGroups] = useState([]);
|
|
122
126
|
const [feedGroupAppId, setFeedGroupAppId] = useState('');
|
|
@@ -210,7 +214,7 @@ function CreateDialog(props) {
|
|
|
210
214
|
const name = String(fd.get('name') ?? '').trim();
|
|
211
215
|
const bindWorkingDir = String(fd.get('bindWorkingDir') ?? '').trim();
|
|
212
216
|
const roleProfileId = String(fd.get('roleProfileId') ?? '').trim();
|
|
213
|
-
const ids =
|
|
217
|
+
const ids = [...selectedBots];
|
|
214
218
|
if (ids.length === 0) {
|
|
215
219
|
setError({ title: '请选择 bot', reason: '至少选择一个 bot 后再创建群聊。' });
|
|
216
220
|
return;
|
|
@@ -266,7 +270,14 @@ function CreateDialog(props) {
|
|
|
266
270
|
});
|
|
267
271
|
}, children: copied ? tr('sessions.copied') : tr('sessions.copy') })] }), _jsxs("p", { children: [_jsx("b", { children: "\u521B\u5EFA\u8005:" }), " ", _jsx("code", { children: String(success.creator ?? '?') })] }), success.feedGroupId ? _jsxs("p", { className: "hint-ok", children: ["\u5DF2\u52A0\u5165\u98DE\u4E66\u6807\u7B7E", success.feedGroupName ? `「${String(success.feedGroupName)}」` : '', "\u3002"] }) : null, success.feedGroupError ? _jsxs("p", { className: "hint-warn", children: ["\u7FA4\u804A\u5DF2\u521B\u5EFA\uFF0C\u4F46\u6807\u7B7E\u8BBE\u7F6E\u5931\u8D25\uFF1A", String(success.feedGroupError)] }) : null, _jsx(CreateInviteNote, { resp: success }), binds.length > 0 ? (bindFailed.length === 0 ? (_jsxs("p", { className: "hint-ok", children: ["\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A", _jsx("code", { children: String(success.bindResolvedPath ?? '') }), "\uFF08", bindOk, "/", binds.length, " bots\uFF09"] })) : (_jsxs("p", { className: "hint-warn", children: ["\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ", bindOk, "/", binds.length, "\u3002", bindFailed.map((b, index) => (_jsxs("span", { children: [_jsx("br", {}), _jsx("code", { children: String(b?.larkAppId ?? '?') }), ": ", String(b?.error ?? 'unknown')] }, `${b?.larkAppId ?? '?'}-${index}`)))] }))) : null, profileStatus ? _jsx("p", { className: profileStatus.kind === 'ok' ? 'hint-ok' : 'hint-warn', children: profileStatus.text }) : null, invalidBots.length || invalidUsers.length ? (_jsxs("ul", { children: [invalidBots.length ? _jsxs("li", { children: ["\u65E0\u6548 bot id: ", _jsx("code", { children: invalidBots.join(', ') })] }) : null, invalidUsers.length ? _jsxs("li", { children: ["\u65E0\u6548\u7528\u6237 open_id: ", _jsx("code", { children: invalidUsers.join(', ') })] }) : null] })) : null, _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", id: "g-create-close", onClick: props.onClose, children: tr('sessions.dismiss') }), _jsx("a", { className: "btn-link primary", href: appLink, target: "_blank", rel: "noopener", children: tr('groups.openGroup') })] })] }));
|
|
268
272
|
}
|
|
269
|
-
return (_jsxs("article", { className: "g-create-dialog", children: [_jsxs("header", { className: "g-create-head", children: [_jsx("h3", { children: tr('groups.createTitle') }), _jsx("p", { children: tr('groups.createHelp') })] }), _jsxs("form", { id: "g-createform", className: "g-create-form", onSubmit: ev => void submit(ev), children: [_jsxs("fieldset", { className: "g-modal-field g-create-bots", children: [_jsx("legend", { children: tr('groups.botPicker') }), _jsx("div", { className: "g-bot-picker", children: _jsx(BotCheckboxes, { bots: props.bots
|
|
273
|
+
return (_jsxs("article", { className: "g-create-dialog", children: [_jsxs("header", { className: "g-create-head", children: [_jsx("h3", { children: tr('groups.createTitle') }), _jsx("p", { children: tr('groups.createHelp') })] }), _jsxs("form", { id: "g-createform", className: "g-create-form", onSubmit: ev => void submit(ev), children: [_jsxs("fieldset", { className: "g-modal-field g-create-bots", children: [_jsx("legend", { children: tr('groups.botPicker') }), _jsx("div", { className: "g-bot-picker", children: _jsx(BotCheckboxes, { bots: props.bots, tr: tr, selected: selectedBots, onToggle: (id, checked) => setSelectedBots(prev => {
|
|
274
|
+
const next = new Set(prev);
|
|
275
|
+
if (checked)
|
|
276
|
+
next.add(id);
|
|
277
|
+
else
|
|
278
|
+
next.delete(id);
|
|
279
|
+
return next;
|
|
280
|
+
}) }) })] }), _jsxs("div", { className: "g-create-fields", children: [_jsxs("fieldset", { className: "g-modal-field", children: [_jsx("legend", { children: tr('groups.name') }), _jsx("input", { type: "text", name: "name", placeholder: tr('groups.namePlaceholder'), maxLength: 60 })] }), _jsxs("fieldset", { className: "g-modal-field", children: [_jsx("legend", { children: tr('groups.bindDir') }), _jsx("input", { type: "text", name: "bindWorkingDir", placeholder: "e.g. ~/projects/botmux" }), _jsx("small", { children: tr('groups.bindDirHelp') })] }), _jsxs("fieldset", { className: "g-modal-field g-profile-field", children: [_jsx("legend", { children: tr('groups.roleProfile') }), _jsx("input", { type: "hidden", name: "roleProfileId", value: roleProfileId }), _jsx(DropdownMenu, { className: "g-profile-menu", ariaLabel: tr('groups.roleProfile'), label: roleProfileId || tr('groups.roleProfileNone'), value: roleProfileId, options: [
|
|
270
281
|
{ value: '', label: tr('groups.roleProfileNone') },
|
|
271
282
|
...props.roleProfiles.map(profile => ({ value: profile.profileId, label: profile.profileId })),
|
|
272
283
|
], onChange: setRoleProfileId }), _jsx("small", { children: tr('groups.roleProfileHelp') })] }), _jsxs("fieldset", { className: "g-modal-field g-feed-group-field", children: [_jsx("legend", { children: "\u98DE\u4E66\u6807\u7B7E\uFF08\u53EF\u9009\uFF09" }), _jsx("input", { type: "hidden", name: "feedGroupId", value: feedGroupId }), _jsx(FeedGroupPicker, { groups: feedGroups, selectedId: feedGroupId, newName: newFeedGroupName, disabled: feedGroupsLoading || !!feedGroupsError, onChange: (selectedId, newName) => { setFeedGroupId(selectedId); setNewFeedGroupName(newName); } }), _jsx("small", { children: "\u5C55\u5F00\u540E\u53EF\u5728\u7B2C\u4E00\u884C\u8F93\u5165\u65B0\u6807\u7B7E\u540D\u79F0\uFF0C\u6216\u9009\u62E9\u4E0B\u65B9\u5DF2\u6709\u6807\u7B7E\u3002" }), feedGroupsLoading ? _jsx("small", { children: "\u6B63\u5728\u8BFB\u53D6\u98DE\u4E66\u6807\u7B7E\u2026" }) : null, feedGroupsError ? (_jsxs("div", { className: "hint-warn-inline", children: [_jsx("small", { children: feedGroupsError }), ' ', _jsx("button", { type: "button", disabled: feedGroupAuthSubmitting, onClick: () => void openFeedGroupLogin(), children: feedGroupAuthSubmitting ? '正在完成授权…' : '立即授权' }), _jsx("small", { children: " \u6388\u6743\u540E\u4F1A\u5F39\u7A97\u63D0\u793A\u4F60\u7C98\u8D34\u56DE\u8C03\u5730\u5740\u3002" })] })) : null, !feedGroupsLoading && !feedGroupsError && feedGroups.length === 0 ? _jsx("small", { children: "\u5F53\u524D\u6CA1\u6709\u6807\u7B7E\uFF0C\u53EF\u76F4\u63A5\u521B\u5EFA\u4E00\u4E2A\u3002" }) : null] })] }), feedGroupAuthUrl ? (_jsx("div", { className: "feed-group-auth-overlay", children: _jsxs("section", { className: "feed-group-auth-card", role: "dialog", "aria-modal": "true", "aria-labelledby": "feed-group-auth-title", children: [_jsx("h3", { id: "feed-group-auth-title", children: "\u6388\u6743\u98DE\u4E66\u6807\u7B7E" }), _jsxs("p", { children: ["\u70B9\u51FB\u4E0B\u9762\u7684\u6309\u94AE\uFF0C\u5728\u98DE\u4E66\u9875\u9762\u786E\u8BA4\u6388\u6743\u3002\u5982\u679C BotMux \u4E0E\u6D4F\u89C8\u5668\u5728\u540C\u4E00\u53F0\u7535\u8111\uFF0C\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u5B8C\u6210\u6388\u6743\u3002\u5982\u679C BotMux \u8FD0\u884C\u5728\u8FDC\u7A0B\u865A\u62DF\u673A\u4E0A\uFF0C\u6D4F\u89C8\u5668\u4F1A\u56E0\u65E0\u6CD5\u8BBF\u95EE\u672C\u673A\u5730\u5740 ", _jsx("code", { children: "127.0.0.1:9768" }), " \u800C\u663E\u793A\u201C\u65E0\u6CD5\u8BBF\u95EE\u201D\uFF1B\u6B64\u65F6\u8BF7\u590D\u5236\u5730\u5740\u680F\u4E2D\u7684\u5B8C\u6574\u94FE\u63A5\u5E76\u7C98\u8D34\u5230\u4E0B\u65B9\u3002"] }), _jsx("button", { type: "button", className: "primary feed-group-auth-open", onClick: () => window.open(feedGroupAuthUrl, '_blank', 'noopener'), children: "\u8DF3\u8F6C\u98DE\u4E66\u6388\u6743" }), _jsxs("label", { children: [_jsx("span", { children: "\u8BF7\u628A\u70B9\u51FB\u6388\u6743\u540E\u7684\u5B8C\u6574\u94FE\u63A5\u7C98\u8D34\u5728\u8FD9\u91CC" }), _jsx("input", { type: "url", value: feedGroupCallbackUrl, placeholder: "http://127.0.0.1:9768/callback?code=\u2026&state=\u2026", onChange: event => setFeedGroupCallbackUrl(event.currentTarget.value) })] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", onClick: () => { setFeedGroupAuthUrl(''); setFeedGroupCallbackUrl(''); }, children: "\u53D6\u6D88" }), _jsx("button", { type: "button", className: "primary", disabled: !feedGroupCallbackUrl.trim() || feedGroupAuthSubmitting, onClick: () => void completeFeedGroupLogin(feedGroupCallbackUrl), children: feedGroupAuthSubmitting ? '正在完成授权…' : '完成授权' })] })] }) })) : null, _jsx("div", { className: "g-create-status", "data-create-status": true, "aria-live": "polite", children: error ? _jsx(DialogError, { ...error }) : null }), _jsxs("div", { className: "actions g-create-actions", children: [_jsx("button", { type: "button", id: "g-create-cancel", onClick: props.onClose, children: tr('groups.cancel') }), _jsx("button", { type: "submit", className: "primary", disabled: submitting, children: submitting ? _jsxs(_Fragment, { children: [_jsx("i", { className: "button-spinner", "aria-hidden": "true" }), tr('groups.createSubmitting')] }) : tr('groups.createSubmit') })] })] })] }));
|
|
@@ -287,16 +298,16 @@ function CreateInviteNote(props) {
|
|
|
287
298
|
}
|
|
288
299
|
return (_jsxs("p", { className: "hint-warn", children: ["\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C", _jsx("strong", { children: "\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60" }), "\u3002\u70B9\u5F00\u4E0B\u9762\u94FE\u63A5\u524D\uFF0C\u5148\u8BA9\u7FA4\u91CC\u4EFB\u4E00\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u53BB\u3002"] }));
|
|
289
300
|
}
|
|
290
|
-
function AddBotsDialog(props) {
|
|
301
|
+
export function AddBotsDialog(props) {
|
|
291
302
|
const { chat, tr } = props;
|
|
292
303
|
const [submitting, setSubmitting] = useState(false);
|
|
293
304
|
const [error, setError] = useState(null);
|
|
294
305
|
const [summary, setSummary] = useState(null);
|
|
306
|
+
const [selectedBots, setSelectedBots] = useState(new Set());
|
|
295
307
|
const inChatSet = useMemo(() => new Set((chat.memberBots ?? []).filter(member => member.inChat).map(member => member.larkAppId)), [chat]);
|
|
296
308
|
async function submit(ev) {
|
|
297
309
|
ev.preventDefault();
|
|
298
|
-
const
|
|
299
|
-
const ids = fd.getAll('bot');
|
|
310
|
+
const ids = [...selectedBots];
|
|
300
311
|
if (ids.length === 0) {
|
|
301
312
|
setError({ title: '请选择 bot', reason: '至少选择一个 bot 后再添加。' });
|
|
302
313
|
setSummary(null);
|
|
@@ -339,7 +350,14 @@ function AddBotsDialog(props) {
|
|
|
339
350
|
setSubmitting(false);
|
|
340
351
|
}
|
|
341
352
|
}
|
|
342
|
-
return (_jsxs("article", { className: "g-add-bots-dialog", children: [_jsx("header", { children: _jsxs("h3", { children: [tr('groups.addBots'), " \u00B7 ", chat.name ?? chat.chatId] }) }), _jsx("p", { children: tr('groups.createHelp') }), _jsxs("form", { id: "g-addform", onSubmit: ev => void submit(ev), children: [_jsx(BotCheckboxes, { bots: props.bots, excludeIds: inChatSet
|
|
353
|
+
return (_jsxs("article", { className: "g-add-bots-dialog", children: [_jsx("header", { children: _jsxs("h3", { children: [tr('groups.addBots'), " \u00B7 ", chat.name ?? chat.chatId] }) }), _jsx("p", { children: tr('groups.createHelp') }), _jsxs("form", { id: "g-addform", onSubmit: ev => void submit(ev), children: [_jsx(BotCheckboxes, { bots: props.bots, excludeIds: inChatSet, tr: tr, selected: selectedBots, onToggle: (id, checked) => setSelectedBots(prev => {
|
|
354
|
+
const next = new Set(prev);
|
|
355
|
+
if (checked)
|
|
356
|
+
next.add(id);
|
|
357
|
+
else
|
|
358
|
+
next.delete(id);
|
|
359
|
+
return next;
|
|
360
|
+
}) }), _jsxs("div", { "data-add-status": true, "aria-live": "polite", children: [error ? _jsx(DialogError, { ...error }) : null, summary ? (_jsxs(_Fragment, { children: [_jsx(AddBotsResult, { summary: summary.result }), summary.refreshError ? _jsx(DialogError, { title: "\u5237\u65B0\u5931\u8D25", reason: summary.refreshError }) : null] })) : null] }), _jsxs("div", { className: "actions", children: [_jsx("button", { type: "button", id: "g-cancel", onClick: props.onClose, children: tr('groups.cancel') }), _jsx("button", { type: "submit", className: "primary", disabled: submitting, children: submitting ? 'Adding...' : tr('groups.addBots') })] })] })] }));
|
|
343
361
|
}
|
|
344
362
|
function SaveProfileDialog(props) {
|
|
345
363
|
const { tr } = props;
|