@awiki/dsh-plugin 0.2.5 → 0.3.0-rc.1
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 +63 -23
- package/README.zh.md +55 -20
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/cordis.patch.yml +0 -8
- package/lib/client.js +5452 -1849
- package/lib/client.js.map +1 -1
- package/lib/index.js +950 -38
- package/lib/model-proxy-contract.js +8 -1
- package/lib/provider.js +3 -1
- package/lib/{sdk-adapter-DKgkyApB.mjs → sdk-adapter-DLhLrLI6.mjs} +219 -2
- package/lib/typert.host.js +796 -49
- package/lib/typert.remote-client.d.ts +33 -1
- package/lib/typert.remote-client.d.ts.map +1 -1
- package/lib/typert.remote-client.js +796 -49
- package/lib/types/client/AwikiGroupDetails.d.ts +16 -0
- package/lib/types/client/AwikiGroupDetails.d.ts.map +1 -0
- package/lib/types/client/AwikiGroupDetails.js +108 -0
- package/lib/types/client/AwikiGroupDetails.js.map +1 -0
- package/lib/types/client/AwikiIdentityAccess.d.ts +21 -0
- package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -0
- package/lib/types/client/AwikiIdentityAccess.js +148 -0
- package/lib/types/client/AwikiIdentityAccess.js.map +1 -0
- package/lib/types/client/AwikiIdentityPage.d.ts +11 -0
- package/lib/types/client/AwikiIdentityPage.d.ts.map +1 -0
- package/lib/types/client/AwikiIdentityPage.js +8 -0
- package/lib/types/client/AwikiIdentityPage.js.map +1 -0
- package/lib/types/client/AwikiMail.d.ts +2 -1
- package/lib/types/client/AwikiMail.d.ts.map +1 -1
- package/lib/types/client/AwikiMail.js +153 -33
- package/lib/types/client/AwikiMail.js.map +1 -1
- package/lib/types/client/AwikiOnboarding.d.ts.map +1 -1
- package/lib/types/client/AwikiOnboarding.js +26 -9
- package/lib/types/client/AwikiOnboarding.js.map +1 -1
- package/lib/types/client/AwikiOverlay.d.ts +0 -5
- package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
- package/lib/types/client/AwikiOverlay.js +136 -124
- package/lib/types/client/AwikiOverlay.js.map +1 -1
- package/lib/types/client/AwikiProfileCard.d.ts +9 -0
- package/lib/types/client/AwikiProfileCard.d.ts.map +1 -0
- package/lib/types/client/AwikiProfileCard.js +90 -0
- package/lib/types/client/AwikiProfileCard.js.map +1 -0
- package/lib/types/client/AwikiRecoveryForm.d.ts +24 -0
- package/lib/types/client/AwikiRecoveryForm.d.ts.map +1 -0
- package/lib/types/client/AwikiRecoveryForm.js +148 -0
- package/lib/types/client/AwikiRecoveryForm.js.map +1 -0
- package/lib/types/client/AwikiSettingsSection.d.ts +0 -2
- package/lib/types/client/AwikiSettingsSection.d.ts.map +1 -1
- package/lib/types/client/AwikiSettingsSection.js +13 -15
- package/lib/types/client/AwikiSettingsSection.js.map +1 -1
- package/lib/types/client/MentionText.d.ts +7 -0
- package/lib/types/client/MentionText.d.ts.map +1 -0
- package/lib/types/client/MentionText.js +10 -0
- package/lib/types/client/MentionText.js.map +1 -0
- package/lib/types/client/controller.d.ts +94 -2
- package/lib/types/client/controller.d.ts.map +1 -1
- package/lib/types/client/controller.js +466 -13
- package/lib/types/client/controller.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +21 -4
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/mail-list-cache.d.ts +15 -0
- package/lib/types/client/mail-list-cache.d.ts.map +1 -0
- package/lib/types/client/mail-list-cache.js +212 -0
- package/lib/types/client/mail-list-cache.js.map +1 -0
- package/lib/types/client/mentions.d.ts +51 -0
- package/lib/types/client/mentions.d.ts.map +1 -0
- package/lib/types/client/mentions.js +165 -0
- package/lib/types/client/mentions.js.map +1 -0
- package/lib/types/client/model-proxy-controller.d.ts +4 -0
- package/lib/types/client/model-proxy-controller.d.ts.map +1 -1
- package/lib/types/client/model-proxy-controller.js +68 -3
- package/lib/types/client/model-proxy-controller.js.map +1 -1
- package/lib/types/client/settings-locales.js +8 -8
- package/lib/types/client/settings-locales.js.map +1 -1
- package/lib/types/client/slots.d.ts +21 -2
- package/lib/types/client/slots.d.ts.map +1 -1
- package/lib/types/index.d.ts +40 -6
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +523 -23
- package/lib/types/index.js.map +1 -1
- package/lib/types/model-proxy-contract.d.ts +6 -0
- package/lib/types/model-proxy-contract.d.ts.map +1 -1
- package/lib/types/model-proxy-contract.js +6 -0
- package/lib/types/model-proxy-contract.js.map +1 -1
- package/lib/types/provider-api.d.ts +29 -1
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/provider.d.ts.map +1 -1
- package/lib/types/provider.js +2 -0
- package/lib/types/provider.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts +18 -1
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +244 -2
- package/lib/types/sdk-adapter.js.map +1 -1
- package/lib/types/sent-mail-store.d.ts +22 -0
- package/lib/types/sent-mail-store.d.ts.map +1 -0
- package/lib/types/sent-mail-store.js +246 -0
- package/lib/types/sent-mail-store.js.map +1 -0
- package/lib/types/types.d.ts +152 -2
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/package.json +15 -26
- package/lib/model-proxy.js +0 -383
- package/lib/types/client/model-account-settings-availability.d.ts +0 -4
- package/lib/types/client/model-account-settings-availability.d.ts.map +0 -1
- package/lib/types/client/model-account-settings-availability.js +0 -4
- package/lib/types/client/model-account-settings-availability.js.map +0 -1
- package/lib/types/model-proxy.d.ts +0 -14
- package/lib/types/model-proxy.d.ts.map +0 -1
- package/lib/types/model-proxy.js +0 -371
- package/lib/types/model-proxy.js.map +0 -1
|
@@ -23,11 +23,6 @@ export declare function resolveAwikiDrawerPlacement(launcher: AwikiLauncherPosit
|
|
|
23
23
|
export declare function clampAwikiDrawerFrame(frame: AwikiDrawerFrame, viewportWidth: number, viewportHeight: number): AwikiDrawerFrame;
|
|
24
24
|
/** Resize one or two drawer boundaries while keeping the opposite boundaries fixed. */
|
|
25
25
|
export declare function resizeAwikiDrawerFrame(frame: AwikiDrawerFrame, direction: AwikiDrawerResizeDirection, deltaX: number, deltaY: number, viewportWidth: number, viewportHeight: number): AwikiDrawerFrame;
|
|
26
|
-
/** Render the identity registration form and its OTP challenge transition. */
|
|
27
|
-
export declare function AwikiRegistrationForm(props: Pick<AwikiOverlayProps, 'sendRegistrationOtp' | 'registerIdentity'> & {
|
|
28
|
-
pending: boolean;
|
|
29
|
-
autoFocusHandle?: boolean;
|
|
30
|
-
}): import("react").JSX.Element;
|
|
31
26
|
/**
|
|
32
27
|
* Render the frame-wide AWiki trigger and right-side drawer.
|
|
33
28
|
* @param props - slot-derived runtime, store, and injected AWiki operations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AwikiOverlay.d.ts","sourceRoot":"","sources":["../../../src/client/AwikiOverlay.tsx"],"names":[],"mappings":"AAAA,+EAA+E;
|
|
1
|
+
{"version":3,"file":"AwikiOverlay.d.ts","sourceRoot":"","sources":["../../../src/client/AwikiOverlay.tsx"],"names":[],"mappings":"AAAA,+EAA+E;AA+C/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGnD,eAAO,MAAM,2BAA2B,mCAAmC,CAAA;AAC3E,eAAO,MAAM,sBAAsB,8BAA8B,CAAA;AAmBjE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,aAAa,CAAA;AAE9F,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAA;CACzC;AAED,MAAM,WAAW,gBAAiB,SAAQ,qBAAqB;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,0BAA0B,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAA;AAI1F,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAKhI;AAeD,6FAA6F;AAC7F,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,qBAAqB,EAC/B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,kBAAkB,CAAC,EAAE,oBAAoB,GACxC,oBAAoB,CAqCtB;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,gBAAgB,EACvB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,gBAAgB,CAalB;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,0BAA0B,EACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,gBAAgB,CAelB;AAk5BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAmuBpD"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/** AWiki trigger, identity registration, and direct/group messaging drawer. */
|
|
3
3
|
import { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
-
import { IconChevronLeftOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconCloseOutline16, IconCopyOutline16, IconDataOutline16, IconDownloadOutline16,
|
|
4
|
+
import { IconChevronLeftOutline14, IconCheckOutline16, IconChecklistOutline14, IconChevronDownOutline14, IconCloseOutline16, IconCopyOutline16, IconDataOutline16, IconDownloadOutline16, IconGlobeOutline14, IconGoalOutline16, IconLoadingOutline16, IconPaperclipOutline16, IconPlusOutline16, IconRefreshOutline16, IconRefreshOutline14, IconSendOutline16, IconSettingsOutline16, IconSparkle16, Button, Menu, Modal, Tooltip, } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
5
5
|
import { AWIKI_ME_APP_ICON_DATA_URL } from "./assets.js";
|
|
6
6
|
import { createAttachmentObjectUrl, fileToBase64, saveDownloadedAttachment } from "./file.js";
|
|
7
7
|
import { AwikiMail } from "./AwikiMail.js";
|
|
8
|
+
import { AwikiGroupDetails } from "./AwikiGroupDetails.js";
|
|
9
|
+
import { AwikiIdentityAccess } from "./AwikiIdentityAccess.js";
|
|
10
|
+
import { AwikiProfileCard } from "./AwikiProfileCard.js";
|
|
11
|
+
import { MentionText } from "./MentionText.js";
|
|
12
|
+
import { activeMentionQuery, codePointIndexToUtf16Index, insertMention, mentionCandidates, protocolMentions, transformMentionDrafts, utf16IndexToCodePointIndex, } from "./mentions.js";
|
|
8
13
|
import css from './AwikiOverlay.module.css';
|
|
9
14
|
export const AWIKI_LAUNCHER_POSITION_KEY = 'dsh-awiki-launcher-position-v1';
|
|
10
15
|
export const AWIKI_DRAWER_FRAME_KEY = 'dsh-awiki-drawer-frame-v1';
|
|
@@ -180,113 +185,12 @@ function conversationTime(value, now = Date.now()) {
|
|
|
180
185
|
? new Intl.DateTimeFormat('zh-CN', { hour: '2-digit', minute: '2-digit', hour12: false }).format(value)
|
|
181
186
|
: new Intl.DateTimeFormat('zh-CN', { month: 'numeric', day: 'numeric' }).format(value);
|
|
182
187
|
}
|
|
183
|
-
/** Render the identity registration form and its OTP challenge transition. */
|
|
184
|
-
export function AwikiRegistrationForm(props) {
|
|
185
|
-
const [phone, setPhone] = useState('');
|
|
186
|
-
const [handle, setHandle] = useState('');
|
|
187
|
-
const [otp, setOtp] = useState('');
|
|
188
|
-
const [otpSent, setOtpSent] = useState(false);
|
|
189
|
-
const [notice, setNotice] = useState(null);
|
|
190
|
-
const [retryDeadline, setRetryDeadline] = useState(null);
|
|
191
|
-
const [retrySeconds, setRetrySeconds] = useState(0);
|
|
192
|
-
useEffect(() => {
|
|
193
|
-
if (retryDeadline === null)
|
|
194
|
-
return;
|
|
195
|
-
const update = () => {
|
|
196
|
-
const remaining = Math.max(0, Math.ceil((retryDeadline - Date.now()) / 1000));
|
|
197
|
-
setRetrySeconds(remaining);
|
|
198
|
-
if (remaining === 0)
|
|
199
|
-
setRetryDeadline(null);
|
|
200
|
-
};
|
|
201
|
-
update();
|
|
202
|
-
const timer = setInterval(update, 250);
|
|
203
|
-
return () => { clearInterval(timer); };
|
|
204
|
-
}, [retryDeadline]);
|
|
205
|
-
const requestOtp = async () => {
|
|
206
|
-
const result = await props.sendRegistrationOtp({ handle: handle.trim(), phone: phone.trim() });
|
|
207
|
-
if (!result.ok)
|
|
208
|
-
return;
|
|
209
|
-
const cooldownSeconds = Math.max(0, Math.ceil(result.value.retryAfterSeconds));
|
|
210
|
-
setOtpSent(true);
|
|
211
|
-
setRetryDeadline(Date.now() + cooldownSeconds * 1000);
|
|
212
|
-
setRetrySeconds(cooldownSeconds);
|
|
213
|
-
setNotice(`验证码已发送;${cooldownSeconds} 秒后可重新获取。`);
|
|
214
|
-
};
|
|
215
|
-
const register = async () => {
|
|
216
|
-
/* v8 ignore next -- the registration action is rendered only after an OTP challenge starts. */
|
|
217
|
-
if (!otpSent)
|
|
218
|
-
return;
|
|
219
|
-
const result = await props.registerIdentity({
|
|
220
|
-
phone: phone.trim(), handle: handle.trim(), otp: otp.trim(),
|
|
221
|
-
});
|
|
222
|
-
if (!result.ok)
|
|
223
|
-
return;
|
|
224
|
-
setNotice(null);
|
|
225
|
-
};
|
|
226
|
-
return (_jsxs("div", { className: css.registration, children: [_jsx("div", { className: css.registrationIcon, children: _jsx(IconUserOutline16, { size: 24 }) }), _jsx("h3", { children: "\u6CE8\u518C AWiki \u8EAB\u4EFD" }), _jsx("p", { children: "\u8BE5\u8EAB\u4EFD\u7531\u5F53\u524D Harness \u90E8\u7F72\u4E2D\u7684\u5168\u90E8 Agent \u5171\u540C\u4F7F\u7528\u3002" }), _jsxs("label", { children: ["Handle", _jsx("input", { value: handle, onChange: (event) => { setHandle(event.target.value); }, autoComplete: "username", placeholder: "\u4F8B\u5982 alice", autoFocus: props.autoFocusHandle })] }), _jsxs("label", { children: ["\u624B\u673A\u53F7", _jsx("input", { value: phone, onChange: (event) => { setPhone(event.target.value); }, autoComplete: "tel" })] }), !otpSent ? (_jsx("button", { type: "button", className: css.primary, disabled: props.pending || phone.trim() === '' || handle.trim() === '', onClick: () => { void requestOtp(); }, children: "\u83B7\u53D6\u9A8C\u8BC1\u7801" })) : (_jsxs(_Fragment, { children: [_jsxs("label", { children: ["\u9A8C\u8BC1\u7801", _jsx("input", { value: otp, onChange: (event) => { setOtp(event.target.value); }, inputMode: "numeric", autoComplete: "one-time-code" })] }), _jsx("button", { type: "button", className: css.primary, disabled: props.pending || handle.trim() === '' || otp.trim() === '', onClick: () => { void register(); }, children: "\u6CE8\u518C\u8EAB\u4EFD" }), _jsx("button", { type: "button", className: css.linkButton, disabled: props.pending || retrySeconds > 0, onClick: () => { void requestOtp(); }, children: retrySeconds > 0 ? `${retrySeconds} 秒后重新获取` : '重新获取验证码' })] })), notice !== null && _jsx("p", { className: css.notice, role: "status", children: notice })] }));
|
|
227
|
-
}
|
|
228
|
-
/** Let a signed-out installation resume its preserved local identity. */
|
|
229
|
-
function SignedOut(props) {
|
|
230
|
-
const [error, setError] = useState(null);
|
|
231
|
-
const login = async () => {
|
|
232
|
-
setError(null);
|
|
233
|
-
const result = await props.login();
|
|
234
|
-
if (!result.ok)
|
|
235
|
-
setError(result.error);
|
|
236
|
-
};
|
|
237
|
-
return (_jsxs("div", { className: css.centerState, children: [_jsx("div", { className: css.registrationIcon, children: _jsx(IconUserOutline16, { size: 24 }) }), _jsx("h3", { children: "\u5DF2\u9000\u51FA AWiki" }), _jsx("p", { children: "\u672C\u673A\u8EAB\u4EFD\u548C\u6D88\u606F\u6570\u636E\u4ECD\u5B89\u5168\u4FDD\u7559\u3002\u91CD\u65B0\u8FDB\u5165\u540E\u4F1A\u7EE7\u7EED\u4F7F\u7528\u539F\u6765\u7684 DID \u548C Handle\u3002" }), _jsx("button", { type: "button", className: css.primary, disabled: props.pending, onClick: () => { void login(); }, children: "\u91CD\u65B0\u8FDB\u5165" }), error !== null && _jsx("small", { className: css.inlineError, role: "alert", children: error })] }));
|
|
238
|
-
}
|
|
239
188
|
/** Prefer the peer WNS display name for a direct chat; groups keep their title. */
|
|
240
189
|
function conversationLabel(conversation) {
|
|
241
190
|
return conversation.kind === 'direct'
|
|
242
191
|
? (conversation.displayName ?? conversation.title)
|
|
243
192
|
: conversation.title;
|
|
244
193
|
}
|
|
245
|
-
/** Show only the deployment identity's WNS display name, never its routing Handle. */
|
|
246
|
-
function identityLabel(identity) {
|
|
247
|
-
return identity.displayName ?? '未设置昵称';
|
|
248
|
-
}
|
|
249
|
-
/** Editable deployment identity summary shown above the conversation roster. */
|
|
250
|
-
function IdentityCard(props) {
|
|
251
|
-
const [editing, setEditing] = useState(false);
|
|
252
|
-
const [draft, setDraft] = useState(props.identity.displayName ?? '');
|
|
253
|
-
const [error, setError] = useState(null);
|
|
254
|
-
useEffect(() => {
|
|
255
|
-
if (!editing)
|
|
256
|
-
setDraft(props.identity.displayName ?? '');
|
|
257
|
-
}, [editing, props.identity.displayName]);
|
|
258
|
-
const cancel = () => {
|
|
259
|
-
setDraft(props.identity.displayName ?? '');
|
|
260
|
-
setError(null);
|
|
261
|
-
setEditing(false);
|
|
262
|
-
};
|
|
263
|
-
const save = async () => {
|
|
264
|
-
const displayName = draft.trim();
|
|
265
|
-
const length = Array.from(displayName).length;
|
|
266
|
-
if (length === 0) {
|
|
267
|
-
setError('请输入昵称');
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (length > 50) {
|
|
271
|
-
setError('昵称不能超过 50 个字符');
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
setError(null);
|
|
275
|
-
const result = await props.updateDisplayName(displayName);
|
|
276
|
-
if (!result.ok) {
|
|
277
|
-
setError(result.error);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
setDraft(result.value.displayName ?? displayName);
|
|
281
|
-
setEditing(false);
|
|
282
|
-
};
|
|
283
|
-
return (_jsxs("div", { className: css.identityCard, children: [_jsx("div", { className: css.identityNameRow, children: editing ? (_jsxs("form", { className: css.identityEditor, onSubmit: (event) => { event.preventDefault(); void save(); }, children: [_jsx("input", { "aria-label": "\u6635\u79F0", autoFocus: true, disabled: props.pending, value: draft, onChange: (event) => { setDraft(event.target.value); }, onKeyDown: (event) => {
|
|
284
|
-
if (event.key === 'Escape') {
|
|
285
|
-
event.stopPropagation();
|
|
286
|
-
cancel();
|
|
287
|
-
}
|
|
288
|
-
} }), _jsx("button", { type: "submit", "aria-label": "\u4FDD\u5B58\u6635\u79F0", disabled: props.pending, children: _jsx(IconCheckOutline16, { size: 14 }) }), _jsx("button", { type: "button", "aria-label": "\u53D6\u6D88\u4FEE\u6539\u6635\u79F0", disabled: props.pending, onClick: cancel, children: _jsx(IconCloseOutline16, { size: 14 }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: props.identity.did, side: "bottom", children: _jsx("button", { type: "button", className: css.identityName, disabled: props.pending, onClick: () => { setError(null); setEditing(true); }, children: identityLabel(props.identity) }) }), _jsx(Tooltip, { label: "\u4FEE\u6539\u6635\u79F0", side: "right", children: _jsx("button", { type: "button", className: css.identityEdit, "aria-label": "\u4FEE\u6539\u6635\u79F0", disabled: props.pending, onClick: () => { setError(null); setEditing(true); }, children: _jsx(IconEditOutline16, { size: 14 }) }) })] })) }), _jsx("small", { className: css.identityHandle, children: props.identity.handle }), _jsxs("span", { className: css.identityStatus, children: [_jsx("i", {}), "\u5728\u7EBF"] }), error !== null && _jsx("small", { className: css.identityError, role: "alert", children: error })] }));
|
|
289
|
-
}
|
|
290
194
|
/** Switch the shared identity between messaging and on-demand mail. */
|
|
291
195
|
function ModeTabs(props) {
|
|
292
196
|
return (_jsxs("div", { className: css.modeTabs, role: "tablist", "aria-label": "AWiki \u529F\u80FD", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": props.mode === 'chat', onClick: () => { props.onChange('chat'); }, children: "\u4F1A\u8BDD" }), _jsxs("button", { type: "button", role: "tab", "aria-selected": props.mode === 'mail', onClick: () => { props.onChange('mail'); }, children: ["\u90AE\u4EF6", props.mailUnreadCount > 0 && _jsx("small", { children: props.mailUnreadCount > 99 ? '99+' : props.mailUnreadCount })] })] }));
|
|
@@ -353,11 +257,11 @@ function MessageRow(props) {
|
|
|
353
257
|
}
|
|
354
258
|
saveDownloadedAttachment(result.value);
|
|
355
259
|
};
|
|
356
|
-
return (_jsxs("div", { className: css.message, "data-message-id": props.message.id, "data-outgoing": props.message.outgoing || undefined, children: [_jsxs("div", { className: css.messageMeta, children: [_jsx("span", { children: senderLabel(props.message, props.peerLabel) }), _jsx("time", { children: time(props.message.sentAt) })] }), props.message.content.kind === 'text' ? (_jsx("p", { children: props.message.content.text })) : preview !== null ? (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: css.imageAttachment, "aria-label": `下载图片 ${props.message.content.attachment.fileName}`, onClick: () => { void download(); }, children: [_jsx("img", { src: preview.url, alt: props.message.content.attachment.fileName, onLoad: () => { props.onImageLoad?.(props.message.id); } }), _jsxs("span", { children: [_jsx("strong", { children: props.message.content.attachment.fileName }), _jsxs("small", { children: [props.message.content.attachment.size, " \u5B57\u8282"] }), _jsx(IconDownloadOutline16, { size: 16 })] })] }), props.message.content.caption !== undefined && _jsx("p", { className: css.caption, children: props.message.content.caption })] })) : (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: css.attachment, disabled: previewLoading, onClick: () => { void download(); }, children: [_jsxs("span", { children: [_jsx("strong", { children: props.message.content.attachment.fileName }), _jsx("small", { children: previewLoading ? '正在加载图片预览…' : `${props.message.content.attachment.size} 字节` })] }), _jsx(IconDownloadOutline16, { size: 16 })] }), props.message.content.caption !== undefined && _jsx("p", { className: css.caption, children: props.message.content.caption })] })), error !== null && _jsx("small", { className: css.inlineError, children: error })] }));
|
|
260
|
+
return (_jsxs("div", { className: css.message, "data-message-id": props.message.id, "data-outgoing": props.message.outgoing || undefined, children: [_jsxs("div", { className: css.messageMeta, children: [_jsx("span", { children: senderLabel(props.message, props.peerLabel) }), _jsx("time", { children: time(props.message.sentAt) })] }), props.message.content.kind === 'text' ? (_jsx("p", { children: _jsx(MentionText, { text: props.message.content.text, ...props.message.content.mentions === undefined ? {} : { mentions: props.message.content.mentions } }) })) : preview !== null ? (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: css.imageAttachment, "aria-label": `下载图片 ${props.message.content.attachment.fileName}`, onClick: () => { void download(); }, children: [_jsx("img", { src: preview.url, alt: props.message.content.attachment.fileName, onLoad: () => { props.onImageLoad?.(props.message.id); } }), _jsxs("span", { children: [_jsx("strong", { children: props.message.content.attachment.fileName }), _jsxs("small", { children: [props.message.content.attachment.size, " \u5B57\u8282"] }), _jsx(IconDownloadOutline16, { size: 16 })] })] }), props.message.content.caption !== undefined && _jsx("p", { className: css.caption, children: props.message.content.caption })] })) : (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: css.attachment, disabled: previewLoading, onClick: () => { void download(); }, children: [_jsxs("span", { children: [_jsx("strong", { children: props.message.content.attachment.fileName }), _jsx("small", { children: previewLoading ? '正在加载图片预览…' : `${props.message.content.attachment.size} 字节` })] }), _jsx(IconDownloadOutline16, { size: 16 })] }), props.message.content.caption !== undefined && _jsx("p", { className: css.caption, children: props.message.content.caption })] })), error !== null && _jsx("small", { className: css.inlineError, children: error })] }));
|
|
357
261
|
}
|
|
358
262
|
/** Render one optimistic outgoing bubble while the Host confirms delivery. */
|
|
359
263
|
function PendingMessageRow(props) {
|
|
360
|
-
return (_jsxs("div", { className: css.pendingMessage, role: "status", "aria-live": "polite", "aria-label": "\u6D88\u606F\u53D1\u9001\u4E2D", children: [_jsx(IconLoadingOutline16, { className: css.pendingMessageSpinner, size: 14 }), _jsxs("div", { className: css.pendingMessageContent, children: [_jsxs("div", { className: css.messageMeta, children: [_jsx("span", { children: "\u6211" }), _jsx("time", { children: time(props.draft.startedAt) })] }), props.draft.content.kind === 'text' ? (_jsx("p", { children: props.draft.content.text })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.pendingAttachment, children: [_jsx(IconPaperclipOutline16, { size: 16 }), _jsxs("span", { children: [_jsx("strong", { children: props.draft.content.fileName }), _jsxs("small", { children: [props.draft.content.size, " \u5B57\u8282"] })] })] }), props.draft.content.caption !== undefined && _jsx("p", { className: css.pendingCaption, children: props.draft.content.caption })] }))] })] }));
|
|
264
|
+
return (_jsxs("div", { className: css.pendingMessage, role: "status", "aria-live": "polite", "aria-label": "\u6D88\u606F\u53D1\u9001\u4E2D", children: [_jsx(IconLoadingOutline16, { className: css.pendingMessageSpinner, size: 14 }), _jsxs("div", { className: css.pendingMessageContent, children: [_jsxs("div", { className: css.messageMeta, children: [_jsx("span", { children: "\u6211" }), _jsx("time", { children: time(props.draft.startedAt) })] }), props.draft.content.kind === 'text' ? (_jsx("p", { children: _jsx(MentionText, { text: props.draft.content.text, mentions: protocolMentions(props.draft.content.text, props.draft.content.mentions) }) })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.pendingAttachment, children: [_jsx(IconPaperclipOutline16, { size: 16 }), _jsxs("span", { children: [_jsx("strong", { children: props.draft.content.fileName }), _jsxs("small", { children: [props.draft.content.size, " \u5B57\u8282"] })] })] }), props.draft.content.caption !== undefined && _jsx("p", { className: css.pendingCaption, children: props.draft.content.caption })] }))] })] }));
|
|
361
265
|
}
|
|
362
266
|
function summaryRangeLabel(summary) {
|
|
363
267
|
const scope = summary.range.kind === 'unread' ? '未读以来' : '最近消息';
|
|
@@ -405,11 +309,16 @@ function SummaryPanel(props) {
|
|
|
405
309
|
function Chat(props) {
|
|
406
310
|
const { view } = props;
|
|
407
311
|
const [text, setText] = useState('');
|
|
312
|
+
const [mentionDrafts, setMentionDrafts] = useState([]);
|
|
313
|
+
const [mentionQuery, setMentionQuery] = useState(null);
|
|
314
|
+
const [mentionCandidateIndex, setMentionCandidateIndex] = useState(0);
|
|
408
315
|
const [file, setFile] = useState(null);
|
|
409
316
|
const [sendingDraft, setSendingDraft] = useState(null);
|
|
317
|
+
const [groupDetailsOpen, setGroupDetailsOpen] = useState(false);
|
|
410
318
|
const [previewUrl, setPreviewUrl] = useState(null);
|
|
411
319
|
const [fileError, setFileError] = useState(null);
|
|
412
320
|
const input = useRef(null);
|
|
321
|
+
const composer = useRef(null);
|
|
413
322
|
const history = useRef(null);
|
|
414
323
|
const previousConversationId = useRef(null);
|
|
415
324
|
const previousMessageTail = useRef(null);
|
|
@@ -420,13 +329,55 @@ function Chat(props) {
|
|
|
420
329
|
const [historyAwayFromBottom, setHistoryAwayFromBottom] = useState(false);
|
|
421
330
|
const [unseenMessageCount, setUnseenMessageCount] = useState(0);
|
|
422
331
|
const selected = view.conversations.find(value => value.id === view.selectedConversationId);
|
|
332
|
+
const candidates = mentionQuery === null || selected?.kind !== 'group'
|
|
333
|
+
? []
|
|
334
|
+
: mentionCandidates(view.groupMembers, view.identity.did, mentionQuery.query);
|
|
423
335
|
const summary = selected === undefined ? undefined : view.summaries[selected.id];
|
|
424
336
|
const summaryPanelId = useId();
|
|
337
|
+
const mentionListId = useId();
|
|
425
338
|
selectedConversationId.current = view.selectedConversationId;
|
|
426
339
|
const visibleSendingDraft = sendingDraft?.conversationId === view.selectedConversationId
|
|
427
340
|
&& !view.messages.some(message => message.id === sendingDraft.messageId)
|
|
428
341
|
? sendingDraft
|
|
429
342
|
: null;
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
setMentionDrafts([]);
|
|
345
|
+
setMentionQuery(null);
|
|
346
|
+
setMentionCandidateIndex(0);
|
|
347
|
+
setGroupDetailsOpen(false);
|
|
348
|
+
}, [view.selectedConversationId]);
|
|
349
|
+
useEffect(() => {
|
|
350
|
+
if (mentionCandidateIndex >= candidates.length)
|
|
351
|
+
setMentionCandidateIndex(0);
|
|
352
|
+
}, [candidates.length, mentionCandidateIndex]);
|
|
353
|
+
const syncMentionQuery = (value, selectionStart) => {
|
|
354
|
+
if (selected?.kind !== 'group' || selectionStart === null) {
|
|
355
|
+
setMentionQuery(null);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
setMentionQuery(activeMentionQuery(value, utf16IndexToCodePointIndex(value, selectionStart)));
|
|
359
|
+
setMentionCandidateIndex(0);
|
|
360
|
+
};
|
|
361
|
+
const chooseMention = (index) => {
|
|
362
|
+
const query = mentionQuery;
|
|
363
|
+
const candidate = candidates[index];
|
|
364
|
+
if (query === null || candidate === undefined)
|
|
365
|
+
return;
|
|
366
|
+
const insertion = insertMention(text, query, candidate, `mention-${crypto.randomUUID()}`);
|
|
367
|
+
const retained = transformMentionDrafts(text, insertion.text, mentionDrafts);
|
|
368
|
+
setText(insertion.text);
|
|
369
|
+
setMentionDrafts([...retained, insertion.mention]);
|
|
370
|
+
setMentionQuery(null);
|
|
371
|
+
setMentionCandidateIndex(0);
|
|
372
|
+
requestAnimationFrame(() => {
|
|
373
|
+
const textarea = composer.current;
|
|
374
|
+
if (textarea === null)
|
|
375
|
+
return;
|
|
376
|
+
const caret = codePointIndexToUtf16Index(insertion.text, insertion.caret);
|
|
377
|
+
textarea.focus();
|
|
378
|
+
textarea.setSelectionRange(caret, caret);
|
|
379
|
+
});
|
|
380
|
+
};
|
|
430
381
|
const markSelectedConversationReadAtBottom = () => {
|
|
431
382
|
const node = history.current;
|
|
432
383
|
const newestRendered = view.messages.at(-1);
|
|
@@ -570,19 +521,24 @@ function Chat(props) {
|
|
|
570
521
|
const sendMessage = async () => {
|
|
571
522
|
if (sendingDraft !== null || view.selectedConversationId === null)
|
|
572
523
|
return;
|
|
573
|
-
const draft = text
|
|
524
|
+
const draft = text;
|
|
574
525
|
const conversationId = view.selectedConversationId;
|
|
575
526
|
if (file === null) {
|
|
576
527
|
/* v8 ignore next -- the only invocation control is disabled while both text and attachment are empty. */
|
|
577
|
-
if (draft === '')
|
|
528
|
+
if (draft.trim() === '')
|
|
578
529
|
return;
|
|
530
|
+
const mentions = selected?.kind === 'group' ? protocolMentions(draft, mentionDrafts) : [];
|
|
579
531
|
const messageId = `msg-${crypto.randomUUID()}`;
|
|
580
|
-
setSendingDraft({ conversationId, messageId, startedAt: Date.now(), content: { kind: 'text', text: draft } });
|
|
532
|
+
setSendingDraft({ conversationId, messageId, startedAt: Date.now(), content: { kind: 'text', text: draft, mentions: mentionDrafts } });
|
|
581
533
|
setText('');
|
|
582
|
-
|
|
534
|
+
setMentionDrafts([]);
|
|
535
|
+
setMentionQuery(null);
|
|
536
|
+
const result = await props.sendText(draft, messageId, mentions);
|
|
583
537
|
setSendingDraft(null);
|
|
584
|
-
if (!result.ok && selectedConversationId.current === conversationId)
|
|
538
|
+
if (!result.ok && selectedConversationId.current === conversationId) {
|
|
585
539
|
setText(draft);
|
|
540
|
+
setMentionDrafts(mentionDrafts);
|
|
541
|
+
}
|
|
586
542
|
return;
|
|
587
543
|
}
|
|
588
544
|
if (file.size > view.attachmentMaxBytes) {
|
|
@@ -601,25 +557,36 @@ function Chat(props) {
|
|
|
601
557
|
kind: 'attachment',
|
|
602
558
|
fileName: selectedFile.name,
|
|
603
559
|
size: selectedFile.size,
|
|
604
|
-
...(draft === '' ? {} : { caption: draft }),
|
|
560
|
+
...(draft.trim() === '' ? {} : { caption: draft.trim() }),
|
|
605
561
|
},
|
|
606
562
|
});
|
|
607
563
|
clearFile();
|
|
608
564
|
setText('');
|
|
565
|
+
setMentionDrafts([]);
|
|
566
|
+
setMentionQuery(null);
|
|
609
567
|
const result = await props.sendAttachment({
|
|
610
568
|
fileName: selectedFile.name,
|
|
611
569
|
mimeType: selectedFile.type || 'application/octet-stream',
|
|
612
570
|
bytesBase64,
|
|
613
|
-
...(draft === '' ? {} : { caption: draft }),
|
|
571
|
+
...(draft.trim() === '' ? {} : { caption: draft.trim() }),
|
|
614
572
|
clientMessageId: messageId,
|
|
615
573
|
});
|
|
616
574
|
setSendingDraft(null);
|
|
617
575
|
if (!result.ok && selectedConversationId.current === conversationId) {
|
|
618
576
|
setFile(selectedFile);
|
|
619
577
|
setText(draft);
|
|
578
|
+
setMentionDrafts(mentionDrafts);
|
|
620
579
|
}
|
|
621
580
|
};
|
|
622
|
-
return (_jsxs("div", { className: css.chat, children: [_jsxs("aside", { className: css.roster, "data-hidden": selected !== undefined || undefined, "aria-label": "\u4F1A\u8BDD", children: [_jsx(
|
|
581
|
+
return (_jsxs("div", { className: css.chat, children: [_jsxs("aside", { className: css.roster, "data-hidden": selected !== undefined || undefined, "aria-label": "\u4F1A\u8BDD", children: [_jsx(AwikiProfileCard, { identity: view.identity, profile: view.profile, pending: view.pending !== null, updateProfile: props.updateProfile }), props.modeTabs, _jsxs("div", { className: css.rosterHeader, children: [_jsx("div", { className: css.rosterTitle, children: "\u4F1A\u8BDD" }), props.composeMenu] }), view.groupRecovery !== null && (_jsxs("div", { className: css.groupRecoveryNotice, "data-status": view.groupRecovery.status, role: view.groupRecovery.status === 'blocked' ? 'alert' : 'status', children: [_jsxs("span", { children: [_jsx("strong", { children: view.groupRecovery.status === 'pending'
|
|
582
|
+
? '正在恢复旧群聊身份'
|
|
583
|
+
: view.groupRecovery.status === 'blocked'
|
|
584
|
+
? '部分旧群聊需要处理'
|
|
585
|
+
: '暂时无法检查旧群聊身份' }), _jsx("small", { children: view.groupRecovery.status === 'pending'
|
|
586
|
+
? `${view.groupRecovery.pending} 个群聊尚未完成,其他会话不受影响。`
|
|
587
|
+
: view.groupRecovery.status === 'blocked'
|
|
588
|
+
? `${view.groupRecovery.blocked} 个群聊未能自动恢复,其他会话不受影响。`
|
|
589
|
+
: '私聊和新群聊不受影响,可稍后重试。' })] }), _jsx(Tooltip, { label: "\u91CD\u8BD5\u7FA4\u804A\u8EAB\u4EFD\u6062\u590D", side: "right", children: _jsx("button", { type: "button", "aria-label": "\u91CD\u8BD5\u7FA4\u804A\u8EAB\u4EFD\u6062\u590D", disabled: view.pending !== null, onClick: () => { void props.retryGroupRebindRecovery(); }, children: _jsx(IconRefreshOutline14, { size: 14 }) }) })] })), _jsxs("div", { className: css.conversationList, children: [view.conversations.map(conversation => (_jsx(ConversationRow, { conversation: conversation, active: conversation.id === view.selectedConversationId, onSelect: () => { void props.selectConversation(conversation.id); } }, conversation.id))), view.conversations.length === 0 && _jsx("p", { className: css.empty, children: "\u8FD8\u6CA1\u6709\u53EF\u7528\u7684\u79C1\u804A\u6216\u7FA4\u804A\u3002" })] }), view.conversationsHasMore && _jsx("button", { type: "button", className: css.more, onClick: () => { void props.loadMoreConversations(); }, children: "\u52A0\u8F7D\u66F4\u591A\u4F1A\u8BDD" })] }), _jsx("section", { className: css.thread, "data-visible": selected !== undefined || undefined, children: selected === undefined ? (_jsxs("div", { className: css.threadEmpty, children: [_jsx(IconGlobeOutline14, { size: 28 }), _jsx("p", { children: "\u9009\u62E9\u4E00\u4E2A\u79C1\u804A\u6216\u7FA4\u804A\u67E5\u770B\u6D88\u606F\u3002" })] })) : (_jsxs(_Fragment, { children: [_jsxs("header", { className: css.threadHeader, children: [_jsx("button", { type: "button", className: css.back, "aria-label": "\u8FD4\u56DE\u4F1A\u8BDD\u5217\u8868", onClick: () => { void props.selectConversation(null); }, children: _jsx(IconChevronLeftOutline14, {}) }), _jsxs("div", { className: css.threadTitle, children: [_jsx("strong", { children: conversationLabel(selected) }), _jsx("small", { children: selected.kind === 'direct' ? '私聊' : '群聊' })] }), selected.kind === 'group' && (_jsx(Tooltip, { label: "\u7FA4\u804A\u8BE6\u60C5", side: "bottom", children: _jsx("button", { type: "button", className: css.threadAction, "aria-label": "\u6253\u5F00\u7FA4\u804A\u8BE6\u60C5", "aria-expanded": groupDetailsOpen, onClick: () => { setGroupDetailsOpen(value => !value); }, children: _jsx(IconSettingsOutline16, { size: 15 }) }) })), _jsxs("button", { type: "button", className: css.summaryTrigger, "aria-controls": summaryPanelId, "aria-expanded": summary === undefined ? undefined : !summary.collapsed, "aria-label": summary?.status === 'loading' ? '正在生成 AI 总结' : summary?.collapsed === true ? '展开 AI 总结' : '生成 AI 总结', disabled: summary?.status === 'loading', onClick: () => {
|
|
623
590
|
if (summary !== undefined && !summary.collapsed && summary.status !== 'error') {
|
|
624
591
|
props.setSummaryCollapsed(selected.id, true);
|
|
625
592
|
}
|
|
@@ -629,9 +596,36 @@ function Chat(props) {
|
|
|
629
596
|
else {
|
|
630
597
|
void props.summarizeConversation();
|
|
631
598
|
}
|
|
632
|
-
}, children: [summary?.status === 'loading' ? _jsx(IconLoadingOutline16, { size: 14 }) : _jsx(IconSparkle16, { size: 14 }), _jsx("span", { children: summary?.status === 'loading' ? '总结中' : 'AI 总结' }), summary !== undefined && _jsx(IconChevronDownOutline14, { size: 12 })] })] }), summary !== undefined && (_jsx(SummaryPanel, { id: summaryPanelId, summary: summary, regenerate: () => { void props.summarizeConversation(); }, collapse: collapsed => { props.setSummaryCollapsed(selected.id, collapsed); }, viewSource: viewSummarySource })), _jsxs("div", { className: css.historyShell, children: [_jsxs("div", { ref: history, className: css.history, role: "log", "aria-label": "\u6D88\u606F\u8BB0\u5F55", onScroll: syncHistoryPosition, children: [view.historyHasMore && _jsx("button", { type: "button", className: css.more, onClick: () => { void props.loadOlderHistory(); }, children: "\u52A0\u8F7D\u66F4\u65E9\u6D88\u606F" }), view.localPending && (_jsxs("div", { className: css.historyLoading, role: "status", "aria-live": "polite", "aria-label": "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u6D88\u606F", children: [_jsx(IconLoadingOutline16, { size: 18 }), _jsx("span", { children: "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u6D88\u606F\u2026" })] })), !view.localPending && view.refreshing && view.messages.length === 0 && (_jsxs("div", { className: css.historyLoading, role: "status", "aria-live": "polite", "aria-label": "\u6B63\u5728\u540C\u6B65\u6D88\u606F", children: [_jsx(IconLoadingOutline16, { size: 18 }), _jsx("span", { children: "\u6B63\u5728\u540C\u6B65\u6D88\u606F\u2026" })] })), view.messages.map(message => (_jsx(MessageRow, { message: message, peerLabel: selected.kind === 'direct' ? conversationLabel(selected) : undefined, download: props.downloadAttachment, onImageLoad: scrollAfterInitialImage }, message.id))), visibleSendingDraft !== null && _jsx(PendingMessageRow, { draft: visibleSendingDraft }), !view.localPending && !view.refreshing && view.messages.length === 0 && visibleSendingDraft === null && _jsx("p", { className: css.empty, children: "\u6682\u65E0\u6D88\u606F\u3002" })] }), historyAwayFromBottom && (_jsxs("button", { type: "button", className: css.latestMessages, "aria-label": unseenMessageCount === 0 ? '下滑到最新消息' : `有 ${unseenMessageCount} 条新消息,下滑到最新消息`, onClick: () => { scrollHistoryToLatest(true); }, children: [_jsx(IconChevronDownOutline14, { size: 14 }), unseenMessageCount > 0 && _jsxs("span", { children: ["\u65B0\u6D88\u606F\uFF08", unseenMessageCount, "\uFF09"] })] }))] }), _jsxs("div", { className: css.composer, children: [fileError !== null && _jsx("small", { className: css.inlineError, role: "alert", children: fileError }), _jsxs("div", { className: css.composeInput, children: [file !== null && (_jsxs("div", { className: css.filePreview, "data-image": previewUrl !== null || undefined, children: [previewUrl === null
|
|
599
|
+
}, children: [summary?.status === 'loading' ? _jsx(IconLoadingOutline16, { size: 14 }) : _jsx(IconSparkle16, { size: 14 }), _jsx("span", { children: summary?.status === 'loading' ? '总结中' : 'AI 总结' }), summary !== undefined && _jsx(IconChevronDownOutline14, { size: 12 })] })] }), selected.kind === 'group' && groupDetailsOpen && (_jsx(AwikiGroupDetails, { group: view.selectedGroup, members: view.groupMembers, hasMore: view.groupMembersHasMore, identity: view.identity, pending: view.pending !== null, refreshSelectedGroup: props.refreshSelectedGroup, loadMoreGroupMembers: props.loadMoreGroupMembers, addSelectedGroupMember: props.addSelectedGroupMember, removeSelectedGroupMember: props.removeSelectedGroupMember, leaveSelectedGroup: props.leaveSelectedGroup, onClose: () => { setGroupDetailsOpen(false); } })), summary !== undefined && (_jsx(SummaryPanel, { id: summaryPanelId, summary: summary, regenerate: () => { void props.summarizeConversation(); }, collapse: collapsed => { props.setSummaryCollapsed(selected.id, collapsed); }, viewSource: viewSummarySource })), _jsxs("div", { className: css.historyShell, children: [_jsxs("div", { ref: history, className: css.history, role: "log", "aria-label": "\u6D88\u606F\u8BB0\u5F55", onScroll: syncHistoryPosition, children: [view.historyHasMore && _jsx("button", { type: "button", className: css.more, onClick: () => { void props.loadOlderHistory(); }, children: "\u52A0\u8F7D\u66F4\u65E9\u6D88\u606F" }), view.localPending && (_jsxs("div", { className: css.historyLoading, role: "status", "aria-live": "polite", "aria-label": "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u6D88\u606F", children: [_jsx(IconLoadingOutline16, { size: 18 }), _jsx("span", { children: "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u6D88\u606F\u2026" })] })), !view.localPending && view.refreshing && view.messages.length === 0 && (_jsxs("div", { className: css.historyLoading, role: "status", "aria-live": "polite", "aria-label": "\u6B63\u5728\u540C\u6B65\u6D88\u606F", children: [_jsx(IconLoadingOutline16, { size: 18 }), _jsx("span", { children: "\u6B63\u5728\u540C\u6B65\u6D88\u606F\u2026" })] })), view.messages.map(message => (_jsx(MessageRow, { message: message, peerLabel: selected.kind === 'direct' ? conversationLabel(selected) : undefined, download: props.downloadAttachment, onImageLoad: scrollAfterInitialImage }, message.id))), visibleSendingDraft !== null && _jsx(PendingMessageRow, { draft: visibleSendingDraft }), !view.localPending && !view.refreshing && view.messages.length === 0 && visibleSendingDraft === null && _jsx("p", { className: css.empty, children: "\u6682\u65E0\u6D88\u606F\u3002" })] }), historyAwayFromBottom && (_jsxs("button", { type: "button", className: css.latestMessages, "aria-label": unseenMessageCount === 0 ? '下滑到最新消息' : `有 ${unseenMessageCount} 条新消息,下滑到最新消息`, onClick: () => { scrollHistoryToLatest(true); }, children: [_jsx(IconChevronDownOutline14, { size: 14 }), unseenMessageCount > 0 && _jsxs("span", { children: ["\u65B0\u6D88\u606F\uFF08", unseenMessageCount, "\uFF09"] })] }))] }), _jsxs("div", { className: css.composer, children: [fileError !== null && _jsx("small", { className: css.inlineError, role: "alert", children: fileError }), _jsxs("div", { className: css.composeInput, children: [file !== null && (_jsxs("div", { className: css.filePreview, "data-image": previewUrl !== null || undefined, children: [previewUrl === null
|
|
633
600
|
? _jsx("span", { className: css.filePreviewIcon, children: _jsx(IconPaperclipOutline16, {}) })
|
|
634
|
-
: _jsx("img", { src: previewUrl, alt: file.name }), previewUrl === null && _jsx("span", { className: css.filePreviewName, children: file.name }), _jsx("button", { type: "button", className: css.removeFile, "aria-label": `移除附件 ${file.name}`, onClick: clearFile, children: _jsx(IconCloseOutline16, { size: 12 }) })] })), _jsx("
|
|
601
|
+
: _jsx("img", { src: previewUrl, alt: file.name }), previewUrl === null && _jsx("span", { className: css.filePreviewName, children: file.name }), _jsx("button", { type: "button", className: css.removeFile, "aria-label": `移除附件 ${file.name}`, onClick: clearFile, children: _jsx(IconCloseOutline16, { size: 12 }) })] })), mentionQuery !== null && candidates.length > 0 && (_jsx("div", { id: mentionListId, className: css.mentionCandidates, role: "listbox", "aria-label": "\u53EF\u63D0\u53CA\u7684\u7FA4\u6210\u5458", children: candidates.map((candidate, index) => (_jsxs("button", { type: "button", role: "option", "aria-selected": index === mentionCandidateIndex, onMouseDown: (event) => { event.preventDefault(); chooseMention(index); }, children: [_jsx("span", { children: candidate.label.slice(0, 1).toLocaleUpperCase() }), _jsx("strong", { children: candidate.label }), _jsx("small", { children: candidate.member.handle ?? candidate.member.did })] }, candidate.member.did))) })), _jsx("textarea", { ref: composer, value: text, "aria-controls": mentionQuery !== null && candidates.length > 0 ? mentionListId : undefined, "aria-expanded": mentionQuery !== null && candidates.length > 0, "aria-autocomplete": "list", onChange: (event) => {
|
|
602
|
+
const next = event.target.value;
|
|
603
|
+
setMentionDrafts(current => transformMentionDrafts(text, next, current));
|
|
604
|
+
setText(next);
|
|
605
|
+
syncMentionQuery(next, event.target.selectionStart);
|
|
606
|
+
}, onSelect: (event) => { syncMentionQuery(text, event.currentTarget.selectionStart); }, onKeyDown: (event) => {
|
|
607
|
+
if (mentionQuery !== null && candidates.length > 0) {
|
|
608
|
+
if (event.key === 'ArrowDown') {
|
|
609
|
+
event.preventDefault();
|
|
610
|
+
setMentionCandidateIndex(index => (index + 1) % candidates.length);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (event.key === 'ArrowUp') {
|
|
614
|
+
event.preventDefault();
|
|
615
|
+
setMentionCandidateIndex(index => (index - 1 + candidates.length) % candidates.length);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if ((event.key === 'Enter' || event.key === 'Tab') && !event.nativeEvent.isComposing) {
|
|
619
|
+
event.preventDefault();
|
|
620
|
+
chooseMention(mentionCandidateIndex);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (event.key === 'Escape') {
|
|
624
|
+
event.preventDefault();
|
|
625
|
+
setMentionQuery(null);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
635
629
|
if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing)
|
|
636
630
|
return;
|
|
637
631
|
event.preventDefault();
|
|
@@ -650,6 +644,7 @@ export function AwikiOverlay(props) {
|
|
|
650
644
|
const titleId = useId();
|
|
651
645
|
const composeTitleId = useId();
|
|
652
646
|
const groupComposeTitleId = useId();
|
|
647
|
+
const groupJoinTitleId = useId();
|
|
653
648
|
const [accountMenuOpen, setAccountMenuOpen] = useState(false);
|
|
654
649
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
655
650
|
const [composeDirect, setComposeDirect] = useState(false);
|
|
@@ -659,12 +654,14 @@ export function AwikiOverlay(props) {
|
|
|
659
654
|
const [groupName, setGroupName] = useState('');
|
|
660
655
|
const [groupMembers, setGroupMembers] = useState('');
|
|
661
656
|
const [groupComposeError, setGroupComposeError] = useState(null);
|
|
657
|
+
const [joinGroupOpen, setJoinGroupOpen] = useState(false);
|
|
658
|
+
const [joinGroupDid, setJoinGroupDid] = useState('');
|
|
659
|
+
const [joinGroupError, setJoinGroupError] = useState(null);
|
|
662
660
|
const [logoutOpen, setLogoutOpen] = useState(false);
|
|
663
661
|
const [logoutPending, setLogoutPending] = useState(false);
|
|
664
662
|
const [logoutError, setLogoutError] = useState(null);
|
|
665
663
|
const [mode, setMode] = useState('chat');
|
|
666
664
|
const [mailUnreadCount, setMailUnreadCount] = useState(0);
|
|
667
|
-
const [mailRefreshRevision, setMailRefreshRevision] = useState(0);
|
|
668
665
|
const [launcherPosition, setLauncherPosition] = useState(readLauncherPosition);
|
|
669
666
|
const [drawerFrame, setDrawerFrame] = useState(readDrawerFrame);
|
|
670
667
|
const [launcherDragging, setLauncherDragging] = useState(false);
|
|
@@ -728,6 +725,9 @@ export function AwikiOverlay(props) {
|
|
|
728
725
|
setGroupName('');
|
|
729
726
|
setGroupMembers('');
|
|
730
727
|
setGroupComposeError(null);
|
|
728
|
+
setJoinGroupOpen(false);
|
|
729
|
+
setJoinGroupDid('');
|
|
730
|
+
setJoinGroupError(null);
|
|
731
731
|
setMode('chat');
|
|
732
732
|
const drag = drawerDrag.current;
|
|
733
733
|
if (drag !== null)
|
|
@@ -765,6 +765,10 @@ export function AwikiOverlay(props) {
|
|
|
765
765
|
setComposeGroup(false);
|
|
766
766
|
return;
|
|
767
767
|
}
|
|
768
|
+
if (joinGroupOpen) {
|
|
769
|
+
setJoinGroupOpen(false);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
768
772
|
if (menuOpen) {
|
|
769
773
|
setMenuOpen(false);
|
|
770
774
|
return;
|
|
@@ -778,7 +782,7 @@ export function AwikiOverlay(props) {
|
|
|
778
782
|
};
|
|
779
783
|
document.addEventListener('keydown', onKeyDown);
|
|
780
784
|
return () => { document.removeEventListener('keydown', onKeyDown); };
|
|
781
|
-
}, [open, accountMenuOpen, composeDirect, composeGroup, logoutOpen, logoutPending, menuOpen, props.actions]);
|
|
785
|
+
}, [open, accountMenuOpen, composeDirect, composeGroup, joinGroupOpen, logoutOpen, logoutPending, menuOpen, props.actions]);
|
|
782
786
|
useEffect(() => {
|
|
783
787
|
const onResize = () => {
|
|
784
788
|
setLauncherPosition((current) => {
|
|
@@ -1015,6 +1019,16 @@ export function AwikiOverlay(props) {
|
|
|
1015
1019
|
setGroupName('');
|
|
1016
1020
|
setGroupMembers('');
|
|
1017
1021
|
};
|
|
1022
|
+
const joinGroup = async () => {
|
|
1023
|
+
setJoinGroupError(null);
|
|
1024
|
+
const result = await props.joinGroup(joinGroupDid);
|
|
1025
|
+
if (!result.ok) {
|
|
1026
|
+
setJoinGroupError(result.error);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
setJoinGroupOpen(false);
|
|
1030
|
+
setJoinGroupDid('');
|
|
1031
|
+
};
|
|
1018
1032
|
const logout = async () => {
|
|
1019
1033
|
setLogoutPending(true);
|
|
1020
1034
|
setLogoutError(null);
|
|
@@ -1038,21 +1052,19 @@ export function AwikiOverlay(props) {
|
|
|
1038
1052
|
setAccountMenuOpen(false);
|
|
1039
1053
|
setLogoutError(null);
|
|
1040
1054
|
setLogoutOpen(true);
|
|
1041
|
-
}, anchor: (_jsx("button", { type: "button", "aria-label": "AWiki \u8D26\u6237\u83DC\u5355", "aria-expanded": accountMenuOpen, "aria-haspopup": "menu", onClick: () => { setAccountMenuOpen(value => !value); }, children: _jsx(IconGlobeOutline14, { size: 18 }) })) })) : _jsx(IconGlobeOutline14, { size: 18 }), _jsx("h2", { id: titleId, children: "AWiki" })] }), _jsx("button", { type: "button", "aria-label":
|
|
1042
|
-
if (mode === 'mail')
|
|
1043
|
-
setMailRefreshRevision(value => value + 1);
|
|
1044
|
-
else
|
|
1045
|
-
void props.open();
|
|
1046
|
-
}, children: _jsx(IconRefreshOutline16, {}) }), _jsx("button", { type: "button", "aria-label": "\u5173\u95ED AWiki", onClick: props.actions.close, children: _jsx(IconCloseOutline16, {}) })] }), view.status === 'loading' && _jsx("div", { className: css.centerState, role: "status", children: "\u6B63\u5728\u8FDE\u63A5 AWiki\u2026" }), view.status === 'error' && _jsxs("div", { className: css.centerState, children: [_jsx("p", { children: view.error }), _jsx("button", { type: "button", className: css.primary, onClick: () => { void props.open(); }, children: "\u91CD\u8BD5" })] }), view.status === 'ready' && view.sessionStatus === 'unregistered' && _jsx(AwikiRegistrationForm, { ...props, pending: view.pending !== null }), view.status === 'ready' && view.sessionStatus === 'signed-out' && _jsx(SignedOut, { login: props.login, pending: view.pending !== null }), view.status === 'ready' && view.sessionStatus === 'active' && view.identity !== null && (_jsxs(_Fragment, { children: [_jsx("div", { className: css.modePanel, "data-active": mode === 'chat' || undefined, hidden: mode !== 'chat', children: _jsx(Chat, { ...props, selectConversation: selectConversation, view: { ...view, identity: view.identity }, modeTabs: _jsx(ModeTabs, { mode: mode, mailUnreadCount: mailUnreadCount, onChange: setMode }), composeMenu: (_jsx(Menu, { open: menuOpen, onClose: () => { setMenuOpen(false); }, align: "end", portal: true, compact: true, items: [
|
|
1055
|
+
}, anchor: (_jsx("button", { type: "button", "aria-label": "AWiki \u8D26\u6237\u83DC\u5355", "aria-expanded": accountMenuOpen, "aria-haspopup": "menu", onClick: () => { setAccountMenuOpen(value => !value); }, children: _jsx(IconGlobeOutline14, { size: 18 }) })) })) : _jsx(IconGlobeOutline14, { size: 18 }), _jsx("h2", { id: titleId, children: "AWiki" })] }), mode === 'chat' && (_jsx("button", { type: "button", "aria-label": "\u5237\u65B0 AWiki", disabled: view.pending !== null, onClick: () => { void props.open(); }, children: _jsx(IconRefreshOutline16, {}) })), _jsx("button", { type: "button", "aria-label": "\u5173\u95ED AWiki", onClick: props.actions.close, children: _jsx(IconCloseOutline16, {}) })] }), view.status === 'loading' && _jsx("div", { className: css.centerState, role: "status", children: "\u6B63\u5728\u8FDE\u63A5 AWiki\u2026" }), view.status === 'error' && _jsxs("div", { className: css.centerState, children: [_jsx("p", { children: view.error }), _jsx("button", { type: "button", className: css.primary, onClick: () => { void props.open(); }, children: "\u91CD\u8BD5" })] }), view.status === 'ready' && (view.sessionStatus === 'unregistered' || view.sessionStatus === 'signed-out') && (_jsx("div", { className: css.identityAccess, children: _jsx(AwikiIdentityAccess, { ...props, sessionStatus: view.sessionStatus, recoveryOperationId: view.recoveryOperationId, recoveryProgress: view.recoveryProgress, pending: view.pending !== null }) })), view.status === 'ready' && view.sessionStatus === 'active' && view.identity !== null && (_jsxs(_Fragment, { children: [_jsx("div", { className: css.modePanel, "data-active": mode === 'chat' || undefined, hidden: mode !== 'chat', children: _jsx(Chat, { ...props, selectConversation: selectConversation, view: { ...view, identity: view.identity }, modeTabs: _jsx(ModeTabs, { mode: mode, mailUnreadCount: mailUnreadCount, onChange: setMode }), composeMenu: (_jsx(Menu, { open: menuOpen, onClose: () => { setMenuOpen(false); }, align: "end", portal: true, compact: true, items: [
|
|
1047
1056
|
{ id: 'direct', label: '发起私聊' },
|
|
1048
1057
|
{ id: 'group', label: '发起群聊' },
|
|
1058
|
+
{ id: 'join-group', label: '加入群聊' },
|
|
1049
1059
|
], onSelect: (id) => {
|
|
1050
1060
|
setMenuOpen(false);
|
|
1051
1061
|
if (id === 'direct')
|
|
1052
1062
|
setComposeDirect(true);
|
|
1053
1063
|
if (id === 'group')
|
|
1054
1064
|
setComposeGroup(true);
|
|
1055
|
-
|
|
1056
|
-
|
|
1065
|
+
if (id === 'join-group')
|
|
1066
|
+
setJoinGroupOpen(true);
|
|
1067
|
+
}, anchor: (_jsx("button", { type: "button", className: css.rosterAction, "aria-label": "\u53D1\u8D77\u4F1A\u8BDD", "aria-expanded": menuOpen, "aria-haspopup": "menu", onClick: () => { setMenuOpen(value => !value); }, children: _jsx(IconPlusOutline16, {}) })) })) }) }), _jsx("div", { className: css.modePanel, "data-active": mode === 'mail' || undefined, hidden: mode !== 'mail', children: _jsx(AwikiMail, { active: mode === 'mail', cacheOwner: view.identity.did, identityCard: mode === 'mail' ? _jsx(AwikiProfileCard, { identity: view.identity, profile: view.profile, pending: view.pending !== null, updateProfile: props.updateProfile }) : null, modeTabs: _jsx(ModeTabs, { mode: mode, mailUnreadCount: mailUnreadCount, onChange: setMode }), onUnreadCountChange: setMailUnreadCount, getMailAccount: props.getMailAccount, listMailInbox: props.listMailInbox, readMail: props.readMail, markMailRead: props.markMailRead, sendMail: props.sendMail }, view.identity.did) })] })), composeDirect && (_jsx("div", { className: css.composeBackdrop, children: _jsxs("form", { className: css.composeCard, role: "dialog", "aria-modal": "true", "aria-labelledby": composeTitleId, onSubmit: (event) => { event.preventDefault(); void startDirect(); }, children: [_jsx("h3", { id: composeTitleId, children: "\u53D1\u8D77\u79C1\u804A" }), _jsx("p", { children: "\u8F93\u5165\u5BF9\u65B9 Handle\u3002\u6253\u5F00\u4F1A\u8BDD\u524D\u4F1A\u5148\u786E\u8BA4\u8BE5\u7528\u6237\u5B58\u5728\u3002" }), _jsxs("label", { children: ["Handle", _jsx("input", { value: peerHandle, onChange: (event) => { setPeerHandle(event.target.value); setComposeError(null); }, autoComplete: "off", placeholder: "\u4F8B\u5982 alice", autoFocus: true })] }), view.pending === '查找用户' && _jsx("p", { role: "status", children: "\u6B63\u5728\u67E5\u627E\u7528\u6237\u2026" }), composeError !== null && _jsx("p", { className: css.inlineError, role: "alert", children: composeError }), _jsxs("div", { className: css.composeActions, children: [_jsx("button", { type: "button", className: css.secondary, onClick: () => { setComposeDirect(false); setComposeError(null); }, children: "\u53D6\u6D88" }), _jsx("button", { type: "submit", className: css.primary, disabled: view.pending !== null || peerHandle.trim() === '', children: "\u6253\u5F00\u4F1A\u8BDD" })] })] }) })), composeGroup && (_jsx("div", { className: css.composeBackdrop, children: _jsxs("form", { className: css.composeCard, role: "dialog", "aria-modal": "true", "aria-labelledby": groupComposeTitleId, onSubmit: (event) => { event.preventDefault(); void createGroup(); }, children: [_jsx("h3", { id: groupComposeTitleId, children: "\u53D1\u8D77\u7FA4\u804A" }), _jsx("p", { children: "\u586B\u5199\u7FA4\u540D\u5373\u53EF\u521B\u5EFA\u3002\u4E5F\u53EF\u4EE5\u73B0\u5728\u9080\u8BF7\u9996\u6279\u6210\u5458\uFF0C\u652F\u6301 Handle \u6216 DID\u3002" }), _jsxs("label", { children: ["\u7FA4\u804A\u540D\u79F0", _jsx("input", { value: groupName, onChange: (event) => { setGroupName(event.target.value); setGroupComposeError(null); }, autoComplete: "off", placeholder: "\u4F8B\u5982 \u53D1\u5E03\u534F\u4F5C\u7FA4", autoFocus: true })] }), _jsxs("label", { children: ["\u9996\u6279\u7FA4\u6210\u5458\uFF08\u53EF\u9009\uFF09", _jsx("textarea", { "aria-label": "\u7FA4\u6210\u5458", value: groupMembers, onChange: (event) => { setGroupMembers(event.target.value); setGroupComposeError(null); }, rows: 4, placeholder: '例如 alice.awiki.ai\nbob.awiki.ai' })] }), view.pending === '创建群聊' && _jsx("p", { role: "status", children: "\u6B63\u5728\u521B\u5EFA\u7FA4\u804A\u5E76\u9080\u8BF7\u6210\u5458\u2026" }), groupComposeError !== null && _jsx("p", { className: css.inlineError, role: "alert", children: groupComposeError }), _jsxs("div", { className: css.composeActions, children: [_jsx("button", { type: "button", className: css.secondary, onClick: () => { setComposeGroup(false); setGroupComposeError(null); }, children: "\u53D6\u6D88" }), _jsx("button", { type: "submit", className: css.primary, disabled: view.pending !== null || groupName.trim() === '', children: "\u521B\u5EFA\u7FA4\u804A" })] })] }) })), joinGroupOpen && (_jsx("div", { className: css.composeBackdrop, children: _jsxs("form", { className: css.composeCard, role: "dialog", "aria-modal": "true", "aria-labelledby": groupJoinTitleId, onSubmit: (event) => { event.preventDefault(); void joinGroup(); }, children: [_jsx("h3", { id: groupJoinTitleId, children: "\u52A0\u5165\u7FA4\u804A" }), _jsx("p", { children: "\u8F93\u5165\u7FA4\u804A\u7684\u5B8C\u6574 DID\u3002\u52A0\u5165\u6210\u529F\u540E\u4F1A\u6253\u5F00\u8BE5\u7FA4\u804A\u5E76\u8BFB\u53D6\u6700\u65B0\u6210\u5458\u5217\u8868\u3002" }), _jsxs("label", { children: ["\u7FA4 DID", _jsx("input", { value: joinGroupDid, onChange: (event) => { setJoinGroupDid(event.target.value); setJoinGroupError(null); }, autoComplete: "off", placeholder: "did:wba:...", autoFocus: true })] }), joinGroupError !== null && _jsx("p", { className: css.inlineError, role: "alert", children: joinGroupError }), _jsxs("div", { className: css.composeActions, children: [_jsx("button", { type: "button", className: css.secondary, onClick: () => { setJoinGroupOpen(false); setJoinGroupError(null); }, children: "\u53D6\u6D88" }), _jsx("button", { type: "submit", className: css.primary, disabled: view.pending !== null || joinGroupDid.trim() === '', children: "\u52A0\u5165\u7FA4\u804A" })] })] }) })), _jsx(Modal, { open: logoutOpen, onClose: () => { if (!logoutPending)
|
|
1068
|
+
setLogoutOpen(false); }, title: "\u9000\u51FA\u767B\u5F55", closeLabel: "\u53D6\u6D88", className: css.compactModal ?? '', contentClassName: css.compactModalContent ?? '', description: "\u9000\u51FA\u540E\uFF0C\u672C\u673A\u5C06\u6682\u505C\u4F7F\u7528 AWiki\uFF1B\u8EAB\u4EFD\u548C\u672C\u5730\u6570\u636E\u90FD\u4F1A\u4FDD\u7559\u3002", footer: (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: logoutPending, onClick: () => { setLogoutOpen(false); }, children: "\u53D6\u6D88" }), _jsx(Button, { type: "button", variant: "outline", className: css.logoutConfirm, disabled: logoutPending, onClick: () => { void logout(); }, children: logoutPending ? '正在退出…' : '确认退出' })] })), children: _jsxs("div", { className: css.logoutWarning, children: [_jsx("p", { children: "\u9000\u51FA\u671F\u95F4\uFF0CWeb UI \u548C Agent \u90FD\u4E0D\u80FD\u8BFB\u53D6\u4F1A\u8BDD\u6216\u4F7F\u7528\u8BE5\u8EAB\u4EFD\u53D1\u9001\u6D88\u606F\u3002" }), _jsx("p", { children: "\u7A0D\u540E\u70B9\u51FB\u201C\u91CD\u65B0\u8FDB\u5165\u672C\u673A\u8EAB\u4EFD\u201D\u5373\u53EF\u7531\u672C\u673A Rust SDK \u6062\u590D\u540C\u4E00\u4E2A DID\u3001Handle \u548C\u6D88\u606F\u6570\u636E\u5E93\u3002" }), logoutError !== null && _jsx("p", { className: css.inlineError, role: "alert", children: logoutError })] }) }), view.error !== null && view.status !== 'error' && _jsx("div", { className: css.error, role: "alert", children: view.error }), view.pending !== null && view.pending !== '发送消息' && view.pending !== '发送附件' && view.pending !== '加载消息' && _jsxs("div", { className: css.pending, role: "status", children: [view.pending, "\u2026"] }), DRAWER_RESIZE_DIRECTIONS.map(direction => (_jsx("div", { className: css.resizeHandle, "data-resize-handle": direction, "aria-hidden": "true", onPointerDown: (event) => { onDrawerResizePointerDown(event, direction); }, onPointerMove: onDrawerResizePointerMove, onPointerUp: finishDrawerResize, onPointerCancel: finishDrawerResize }, direction)))] }))] }));
|
|
1057
1069
|
}
|
|
1058
1070
|
//# sourceMappingURL=AwikiOverlay.js.map
|