@awiki/dsh-plugin 0.3.16 → 0.3.18

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.
Files changed (87) hide show
  1. package/README.md +4 -0
  2. package/THIRD_PARTY_NOTICES.md +6 -0
  3. package/lib/client.js +3543 -746
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +218 -41
  6. package/lib/{sdk-adapter-DGcTiEZz.mjs → package-version.generated-D0MSDRZn.mjs} +83 -10
  7. package/lib/provider.js +5 -3
  8. package/lib/typert.host.js +481 -127
  9. package/lib/typert.remote-client.d.ts +9 -3
  10. package/lib/typert.remote-client.d.ts.map +1 -1
  11. package/lib/typert.remote-client.js +481 -127
  12. package/lib/types/client/AwikiAvatar.d.ts +40 -0
  13. package/lib/types/client/AwikiAvatar.d.ts.map +1 -0
  14. package/lib/types/client/AwikiAvatar.js +201 -0
  15. package/lib/types/client/AwikiAvatar.js.map +1 -0
  16. package/lib/types/client/AwikiAvatarEditor.d.ts +11 -0
  17. package/lib/types/client/AwikiAvatarEditor.d.ts.map +1 -0
  18. package/lib/types/client/AwikiAvatarEditor.js +113 -0
  19. package/lib/types/client/AwikiAvatarEditor.js.map +1 -0
  20. package/lib/types/client/AwikiAvatarPreview.d.ts +8 -0
  21. package/lib/types/client/AwikiAvatarPreview.d.ts.map +1 -0
  22. package/lib/types/client/AwikiAvatarPreview.js +68 -0
  23. package/lib/types/client/AwikiAvatarPreview.js.map +1 -0
  24. package/lib/types/client/AwikiGroupDetails.js +2 -2
  25. package/lib/types/client/AwikiGroupDetails.js.map +1 -1
  26. package/lib/types/client/AwikiIdentityAccess.d.ts +1 -0
  27. package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -1
  28. package/lib/types/client/AwikiIdentityAccess.js +10 -2
  29. package/lib/types/client/AwikiIdentityAccess.js.map +1 -1
  30. package/lib/types/client/AwikiOverlay.d.ts.map +1 -1
  31. package/lib/types/client/AwikiOverlay.js +92 -27
  32. package/lib/types/client/AwikiOverlay.js.map +1 -1
  33. package/lib/types/client/AwikiProfileCard.d.ts +1 -1
  34. package/lib/types/client/AwikiProfileCard.d.ts.map +1 -1
  35. package/lib/types/client/AwikiProfileCard.js +6 -2
  36. package/lib/types/client/AwikiProfileCard.js.map +1 -1
  37. package/lib/types/client/avatar-cache.d.ts +24 -0
  38. package/lib/types/client/avatar-cache.d.ts.map +1 -0
  39. package/lib/types/client/avatar-cache.js +336 -0
  40. package/lib/types/client/avatar-cache.js.map +1 -0
  41. package/lib/types/client/avatar-dialog.d.ts +3 -0
  42. package/lib/types/client/avatar-dialog.d.ts.map +1 -0
  43. package/lib/types/client/avatar-dialog.js +18 -0
  44. package/lib/types/client/avatar-dialog.js.map +1 -0
  45. package/lib/types/client/avatar-image.d.ts +26 -0
  46. package/lib/types/client/avatar-image.d.ts.map +1 -0
  47. package/lib/types/client/avatar-image.js +193 -0
  48. package/lib/types/client/avatar-image.js.map +1 -0
  49. package/lib/types/client/controller.d.ts +30 -4
  50. package/lib/types/client/controller.d.ts.map +1 -1
  51. package/lib/types/client/controller.js +267 -56
  52. package/lib/types/client/controller.js.map +1 -1
  53. package/lib/types/client/index.d.ts.map +1 -1
  54. package/lib/types/client/index.js +10 -2
  55. package/lib/types/client/index.js.map +1 -1
  56. package/lib/types/client/slots.d.ts +12 -3
  57. package/lib/types/client/slots.d.ts.map +1 -1
  58. package/lib/types/conversation-preferences.d.ts.map +1 -1
  59. package/lib/types/conversation-preferences.js +27 -0
  60. package/lib/types/conversation-preferences.js.map +1 -1
  61. package/lib/types/index.d.ts +8 -2
  62. package/lib/types/index.d.ts.map +1 -1
  63. package/lib/types/index.js +76 -11
  64. package/lib/types/index.js.map +1 -1
  65. package/lib/types/listener.d.ts.map +1 -1
  66. package/lib/types/listener.js +9 -8
  67. package/lib/types/listener.js.map +1 -1
  68. package/lib/types/package-version.generated.d.ts +2 -2
  69. package/lib/types/package-version.generated.js +2 -2
  70. package/lib/types/provider-api.d.ts +9 -3
  71. package/lib/types/provider-api.d.ts.map +1 -1
  72. package/lib/types/provider.d.ts.map +1 -1
  73. package/lib/types/provider.js +5 -2
  74. package/lib/types/provider.js.map +1 -1
  75. package/lib/types/sdk-adapter.d.ts +6 -3
  76. package/lib/types/sdk-adapter.d.ts.map +1 -1
  77. package/lib/types/sdk-adapter.js +58 -7
  78. package/lib/types/sdk-adapter.js.map +1 -1
  79. package/lib/types/tenant-registry.d.ts +2 -0
  80. package/lib/types/tenant-registry.d.ts.map +1 -1
  81. package/lib/types/tenant-registry.js +83 -16
  82. package/lib/types/tenant-registry.js.map +1 -1
  83. package/lib/types/types.d.ts +63 -7
  84. package/lib/types/types.d.ts.map +1 -1
  85. package/lib/types/types.js.map +1 -1
  86. package/lib/types/update-policy.d.ts +2 -2
  87. package/package.json +33 -34
@@ -1,4 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { AwikiAvatarPreview } from "./AwikiAvatarPreview.js";
3
+ import { AwikiAvatar, AwikiAvatarProvider } from "./AwikiAvatar.js";
2
4
  import { AwikiDraftProvider, useDraftState } from "./drafts.js";
3
5
  /** AWiki trigger, identity registration, and direct/group messaging drawer. */
4
6
  import { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';
@@ -212,7 +214,7 @@ function ConversationRow(props) {
212
214
  const unreadCount = props.conversation.unreadCount ?? 0;
213
215
  const unreadLabel = unreadCount > 99 ? '99+' : String(unreadCount);
214
216
  const preview = props.conversation.lastMessagePreview ?? '暂无消息';
215
- return (_jsxs("div", { className: css.conversationRow, "data-active": props.active || undefined, children: [_jsxs("button", { type: "button", className: css.conversationSelect, "aria-label": unreadCount > 0 ? `${label},${unreadCount} 条未读消息` : undefined, onClick: props.onSelect, children: [_jsxs("span", { className: css.avatar, children: [props.conversation.kind === 'direct' ? '私' : '群', unreadCount > 0 && _jsx("span", { className: css.conversationUnreadBadge, "aria-hidden": "true", children: unreadLabel })] }), _jsxs("span", { className: css.conversationText, children: [_jsxs("span", { className: css.conversationHeader, children: [_jsx("strong", { id: labelId, children: label }), props.conversation.lastMessageAt !== undefined && (_jsx("time", { className: css.conversationTime, children: conversationTime(props.conversation.lastMessageAt) }))] }), _jsx("small", { children: preview })] })] }), _jsx(Menu, { open: menuOpen, onClose: () => { setMenuOpen(false); }, align: "end", portal: true, compact: true, items: [{
217
+ return (_jsxs("div", { className: css.conversationRow, "data-active": props.active || undefined, children: [_jsxs("button", { type: "button", className: css.conversationSelect, "aria-label": unreadCount > 0 ? `${label},${unreadCount} 条未读消息` : undefined, onClick: props.onSelect, children: [_jsxs("span", { className: css.avatar, children: [_jsx(AwikiAvatar, { name: label, ...props.conversation.kind === 'direct' ? { did: props.conversation.peerDid } : { groupDid: props.conversation.groupDid }, uri: props.conversation.avatarUri, thumbnail: props.conversation.avatarThumbnailUri }), unreadCount > 0 && _jsx("span", { className: css.conversationUnreadBadge, "aria-hidden": "true", children: unreadLabel })] }), _jsxs("span", { className: css.conversationText, children: [_jsxs("span", { className: css.conversationHeader, children: [_jsx("strong", { id: labelId, children: label }), props.conversation.lastMessageAt !== undefined && (_jsx("time", { className: css.conversationTime, children: conversationTime(props.conversation.lastMessageAt) }))] }), _jsx("small", { children: preview })] })] }), _jsx(Menu, { open: menuOpen, onClose: () => { setMenuOpen(false); }, align: "end", portal: true, compact: true, items: [{
216
218
  id: 'remove',
217
219
  label: '从会话列表移除',
218
220
  icon: _jsx(IconArchiveOutline20, { size: 14 }),
@@ -221,6 +223,12 @@ function ConversationRow(props) {
221
223
  props.onRemove();
222
224
  }, anchor: (_jsx("button", { type: "button", className: css.conversationMenu, "aria-label": "\u66F4\u591A\u4F1A\u8BDD\u64CD\u4F5C", "aria-describedby": labelId, title: `管理会话:${label}`, "aria-expanded": menuOpen, "aria-haspopup": "menu", disabled: props.pending, onClick: (event) => { event.stopPropagation(); setMenuOpen(value => !value); }, children: _jsx(IconEllipsisOutline16, { size: 15 }) })) })] }));
223
225
  }
226
+ function SecurityLock(props) {
227
+ return _jsxs("svg", { className: props.className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [_jsx("rect", { x: "5", y: "10", width: "14", height: "11", rx: "2" }), _jsx("path", { d: props.open ? 'M8 10V7a4 4 0 0 1 8 0' : 'M8 10V7a4 4 0 0 1 8 0v3' })] });
228
+ }
229
+ function MessageSecurityMark({ pending = false }) {
230
+ return _jsx("span", { className: css.messageLock, role: "img", "aria-label": pending ? "将以端到端加密发送" : "端到端加密", title: pending ? "将以端到端加密发送" : "端到端加密", children: _jsx(SecurityLock, {}) });
231
+ }
224
232
  /** Render one AWiki message, including an attachment download action. */
225
233
  function MessageRow(props) {
226
234
  const [error, setError] = useState(null);
@@ -269,11 +277,17 @@ function MessageRow(props) {
269
277
  }
270
278
  saveDownloadedAttachment(result.value);
271
279
  };
272
- 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 })] }));
280
+ 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) })] }), _jsxs("div", { className: css.messageBody, children: [props.showSecurity && (props.message.encrypted || (props.message.outgoing && props.message.encryptionRequired && ["queued", "sending", "failed", "retrying", "pending"].includes(props.message.sendState ?? ""))) && _jsx(MessageSecurityMark, { pending: !props.message.encrypted }), 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 })] }));
273
281
  }
274
282
  /** Render one optimistic outgoing bubble while the Host confirms delivery. */
275
283
  function PendingMessageRow(props) {
276
- 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 })] }))] })] }));
284
+ const [showSpinner, setShowSpinner] = useState(false);
285
+ useEffect(() => {
286
+ setShowSpinner(false);
287
+ const timer = setTimeout(() => setShowSpinner(true), Math.max(0, props.draft.startedAt + 3000 - Date.now()));
288
+ return () => clearTimeout(timer);
289
+ }, [props.draft.messageId, props.draft.startedAt]);
290
+ return (_jsxs("div", { className: css.pendingMessage, role: "status", "aria-live": "polite", "aria-label": "\u6D88\u606F\u53D1\u9001\u4E2D", children: [showSpinner && _jsx(IconLoadingOutline16, { className: css.pendingMessageSpinner, size: 14 }), _jsxs("div", { className: css.pendingMessageContent, children: [props.draft.encrypted && _jsx(MessageSecurityMark, { pending: true }), _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 })] }))] })] }));
277
291
  }
278
292
  function summaryRangeLabel(summary) {
279
293
  const scope = summary.range.kind === 'unread' ? '未读以来' : '最近消息';
@@ -325,6 +339,7 @@ function Chat(props) {
325
339
  const [mentionQuery, setMentionQuery] = useState(null);
326
340
  const [mentionCandidateIndex, setMentionCandidateIndex] = useState(0);
327
341
  const [file, setFile] = useDraftState(`chat:${view.selectedConversationId}:file`, null);
342
+ const [failedSend, setFailedSend] = useDraftState('chat:failed-security', null, false);
328
343
  const [sendingDraft, setSendingDraft] = useDraftState('chat:sending', null, false);
329
344
  const [groupDetailsOpen, setGroupDetailsOpen] = useState(false);
330
345
  const [threadMenuOpen, setThreadMenuOpen] = useState(false);
@@ -352,7 +367,7 @@ function Chat(props) {
352
367
  ? view.groupAccess
353
368
  : { groupDid: selected.groupDid, status: 'loading' }
354
369
  : null;
355
- const groupWritable = selectedGroupAccess === null || selectedGroupAccess.status === 'available';
370
+ const groupWritable = (selectedGroupAccess === null || selectedGroupAccess.status === 'available') && (!view.selectedGroup?.encrypted || view.selectedGroup.canSendSecure === true);
356
371
  const candidates = mentionQuery === null || selected?.kind !== 'group'
357
372
  ? []
358
373
  : mentionCandidates(view.groupMembers, view.identity.did, mentionQuery.query);
@@ -547,8 +562,12 @@ function Chat(props) {
547
562
  input.current.value = '';
548
563
  };
549
564
  const sendMessage = async () => {
550
- if (sendingDraft !== null || view.selectedConversationId === null)
565
+ if (sendingDraft !== null || view.selectedConversationId === null || (selected?.kind === 'direct' && view.messageSecurityReady !== true) || !groupWritable)
551
566
  return;
567
+ const retry = failedSend?.conversationId === view.selectedConversationId && failedSend.text === text && failedSend.file === file ? failedSend : null;
568
+ const security = retry?.security ?? (selected?.kind === 'direct' && view.encryptedConversationIds?.includes(selected.id) ? 'e2ee-required' : 'plain');
569
+ const messageId = retry?.messageId ?? `msg-${crypto.randomUUID()}`;
570
+ const intent = { conversationId: view.selectedConversationId, text, file, messageId, security };
552
571
  const draft = text;
553
572
  const conversationId = view.selectedConversationId;
554
573
  if (file === null) {
@@ -556,13 +575,15 @@ function Chat(props) {
556
575
  if (draft.trim() === '')
557
576
  return;
558
577
  const mentions = selected?.kind === 'group' ? protocolMentions(draft, mentionDrafts) : [];
559
- const messageId = `msg-${crypto.randomUUID()}`;
560
- setSendingDraft({ conversationId, messageId, startedAt: Date.now(), content: { kind: 'text', text: draft, mentions: mentionDrafts } });
578
+ setSendingDraft({ conversationId, messageId, encrypted: selected?.kind === 'direct' && security === 'e2ee-required', startedAt: Date.now(), content: { kind: 'text', text: draft, mentions: mentionDrafts } });
561
579
  setText('');
562
580
  setMentionDrafts([]);
563
581
  setMentionQuery(null);
564
- const result = await props.sendText(draft, messageId, mentions);
582
+ const result = await props.sendText(draft, messageId, mentions, security);
565
583
  setSendingDraft(null);
584
+ setFailedSend(result.ok ? null : intent);
585
+ if (!mounted.current || selectedConversationId.current !== conversationId)
586
+ return;
566
587
  if (!result.ok) {
567
588
  setText(draft);
568
589
  setMentionDrafts(mentionDrafts);
@@ -575,11 +596,8 @@ function Chat(props) {
575
596
  }
576
597
  setFileError(null);
577
598
  const selectedFile = file;
578
- const bytesBase64 = await fileToBase64(selectedFile);
579
- if (!mounted.current || selectedConversationId.current !== conversationId)
580
- return;
581
- const messageId = `msg-${crypto.randomUUID()}`;
582
599
  setSendingDraft({
600
+ encrypted: selected?.kind === 'direct' && security === 'e2ee-required',
583
601
  conversationId,
584
602
  messageId,
585
603
  startedAt: Date.now(),
@@ -590,11 +608,25 @@ function Chat(props) {
590
608
  ...(draft.trim() === '' ? {} : { caption: draft.trim() }),
591
609
  },
592
610
  });
611
+ let bytesBase64;
612
+ try {
613
+ bytesBase64 = await fileToBase64(selectedFile);
614
+ }
615
+ catch {
616
+ setSendingDraft(null);
617
+ setFileError('附件读取失败,文件和消息模式已保留,请重试');
618
+ return;
619
+ }
620
+ if (!mounted.current || selectedConversationId.current !== conversationId) {
621
+ setSendingDraft(null);
622
+ return;
623
+ }
593
624
  clearFile();
594
625
  setText('');
595
626
  setMentionDrafts([]);
596
627
  setMentionQuery(null);
597
628
  const result = await props.sendAttachment({
629
+ security,
598
630
  fileName: selectedFile.name,
599
631
  mimeType: selectedFile.type || 'application/octet-stream',
600
632
  bytesBase64,
@@ -602,6 +634,9 @@ function Chat(props) {
602
634
  clientMessageId: messageId,
603
635
  });
604
636
  setSendingDraft(null);
637
+ setFailedSend(result.ok ? null : intent);
638
+ if (!mounted.current || selectedConversationId.current !== conversationId)
639
+ return;
605
640
  if (!result.ok) {
606
641
  setFile(selectedFile);
607
642
  setText(draft);
@@ -626,7 +661,7 @@ function Chat(props) {
626
661
  if (!result.ok)
627
662
  setConversationPreferenceError(result.error);
628
663
  };
629
- 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" }), _jsxs("div", { className: css.rosterActions, children: [view.hiddenConversations.length > 0 && (_jsx(Tooltip, { label: "\u67E5\u770B\u5DF2\u9690\u85CF\u4F1A\u8BDD", side: "right", children: _jsxs("button", { type: "button", className: css.rosterAction, "aria-label": `查看已隐藏会话,${view.hiddenConversations.length} 个`, onClick: () => { setConversationPreferenceError(null); setHiddenConversationsOpen(true); }, children: [_jsx(IconArchiveOutline20, { size: 15 }), _jsx("small", { children: view.hiddenConversations.length > 99 ? '99+' : view.hiddenConversations.length })] }) })), props.composeMenu] })] }), _jsxs("div", { className: css.conversationList, children: [view.conversations.map(conversation => (_jsx(ConversationRow, { conversation: conversation, active: conversation.id === view.selectedConversationId, pending: view.pending !== null, onSelect: () => { void props.selectConversation(conversation.id); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(conversation); } }, 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 }) }) })), _jsx(Menu, { open: threadMenuOpen, onClose: () => { setThreadMenuOpen(false); }, align: "end", portal: true, compact: true, items: [{
664
+ 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, setAvatar: props.setAvatar, clearAvatar: props.clearAvatar, refreshAvatarProfile: props.refreshAvatarProfile }), props.modeTabs, _jsxs("div", { className: css.rosterHeader, children: [_jsx("div", { className: css.rosterTitle, children: "\u4F1A\u8BDD" }), _jsxs("div", { className: css.rosterActions, children: [view.hiddenConversations.length > 0 && (_jsx(Tooltip, { label: "\u67E5\u770B\u5DF2\u9690\u85CF\u4F1A\u8BDD", side: "right", children: _jsxs("button", { type: "button", className: css.rosterAction, "aria-label": `查看已隐藏会话,${view.hiddenConversations.length} 个`, onClick: () => { setConversationPreferenceError(null); setHiddenConversationsOpen(true); }, children: [_jsx(IconArchiveOutline20, { size: 15 }), _jsx("small", { children: view.hiddenConversations.length > 99 ? '99+' : view.hiddenConversations.length })] }) })), props.composeMenu] })] }), _jsxs("div", { className: css.conversationList, children: [view.conversations.map(conversation => (_jsx(ConversationRow, { conversation: conversation, active: conversation.id === view.selectedConversationId, pending: view.pending !== null, onSelect: () => { void props.selectConversation(conversation.id); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(conversation); } }, 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, {}) }), _jsx(_Fragment, { children: selected.kind === 'direct' && _jsx(AwikiAvatarPreview, { did: selected.peerDid, name: conversationLabel(selected), uri: selected.avatarUri, thumbnail: selected.avatarThumbnailUri }, `${view.identity?.did ?? ""}:${selected.id}`) }), _jsxs("div", { className: css.threadTitle, children: [_jsxs("strong", { children: [conversationLabel(selected), view.selectedGroup?.encrypted && _jsx("span", { className: css.groupLock, role: "img", "aria-label": "\u52A0\u5BC6\u7FA4", title: "\u7AEF\u5230\u7AEF\u52A0\u5BC6\u7FA4", children: _jsx(SecurityLock, {}) })] }), _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 }) }) })), _jsx(Menu, { open: threadMenuOpen, onClose: () => { setThreadMenuOpen(false); }, align: "end", portal: true, compact: true, items: [{
630
665
  id: 'remove',
631
666
  label: '从会话列表移除',
632
667
  icon: _jsx(IconArchiveOutline20, { size: 14 }),
@@ -644,9 +679,12 @@ function Chat(props) {
644
679
  else {
645
680
  void props.summarizeConversation();
646
681
  }
647
- }, 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 })] })] }), selectedGroupAccess !== null && selectedGroupAccess.status !== 'available' && (_jsx(AwikiGroupAccessNotice, { access: selectedGroupAccess, pending: view.pending !== null, onRetry: () => { void props.refreshSelectedGroup(); }, onRejoin: () => { void props.joinGroup(selectedGroupAccess.groupDid); }, onBack: () => { void props.selectConversation(null); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(selected); } })), selected.kind === 'group' && groupDetailsOpen && (_jsx(AwikiGroupDetails, { group: view.selectedGroup, fallback: { groupDid: selected.groupDid, title: conversationLabel(selected) }, access: selectedGroupAccess ?? { groupDid: selected.groupDid, status: 'loading' }, 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, joinGroup: props.joinGroup, onClose: () => { setGroupDetailsOpen(false); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(selected); } })), 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
682
+ }, 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 })] })] }), selectedGroupAccess !== null && selectedGroupAccess.status !== 'available' && (_jsx(AwikiGroupAccessNotice, { access: selectedGroupAccess, pending: view.pending !== null, onRetry: () => { void props.refreshSelectedGroup(); }, onRejoin: () => { void props.joinGroup(selectedGroupAccess.groupDid); }, onBack: () => { void props.selectConversation(null); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(selected); } })), selected.kind === 'group' && groupDetailsOpen && (_jsx(AwikiGroupDetails, { group: view.selectedGroup, fallback: { groupDid: selected.groupDid, title: conversationLabel(selected) }, access: selectedGroupAccess ?? { groupDid: selected.groupDid, status: 'loading' }, 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, joinGroup: props.joinGroup, onClose: () => { setGroupDetailsOpen(false); }, onRemove: () => { setConversationPreferenceError(null); setRemoveCandidate(selected); } })), 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, { showSecurity: selected.kind === 'direct', message: sendingDraft?.messageId === message.id && sendingDraft.encrypted
683
+ ? { ...message, encryptionRequired: true, sendState: message.sendState ?? 'pending' }
684
+ : failedSend?.messageId === message.id && failedSend.security === 'e2ee-required'
685
+ ? { ...message, encryptionRequired: true, sendState: message.sendState ?? 'failed' } : 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: [failedSend?.conversationId === view.selectedConversationId && failedSend.text === text && failedSend.file === file && _jsxs("small", { role: "status", children: ["\u672C\u6B21\u91CD\u8BD5\u4FDD\u6301", failedSend.security === 'e2ee-required' ? '加密消息' : '普通消息', "\uFF0C\u4E0D\u4F1A\u8DDF\u968F\u4E0A\u65B9\u6A21\u5F0F\u5207\u6362\u3002"] }), 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
648
686
  ? _jsx("span", { className: css.filePreviewIcon, children: _jsx(IconPaperclipOutline16, {}) })
649
- : _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}`, disabled: !groupWritable, 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, disabled: !groupWritable, "aria-controls": mentionQuery !== null && candidates.length > 0 ? mentionListId : undefined, "aria-expanded": mentionQuery !== null && candidates.length > 0, "aria-autocomplete": "list", onChange: (event) => {
687
+ : _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}`, disabled: !groupWritable, 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))) })), view.selectedGroup?.encrypted && !view.selectedGroup.canSendSecure && _jsxs("div", { className: css.groupSecurityStatus, role: "status", children: [_jsx(SecurityLock, {}), _jsx("span", { children: view.pending !== null ? '正在准备加密聊天…' : view.selectedGroup.secureProblem ? view.selectedGroup.secureProblem : view.selectedGroup.secureState === 'WaitingForMembershipUpdate' ? '等待成员完成加密准备' : view.selectedGroup.myRole === 'owner' ? '正在准备加密聊天…' : '等待群主设备完成加密准备' }), view.selectedGroup.myRole === 'owner' && view.selectedGroup.secureRetryable && _jsx("button", { className: css.secondary, type: "button", disabled: view.pending !== null, onClick: () => { void props.repairSelectedGroupSecurity?.(); }, children: "\u91CD\u8BD5" })] }), _jsx("textarea", { ref: composer, value: text, disabled: !groupWritable, "aria-controls": mentionQuery !== null && candidates.length > 0 ? mentionListId : undefined, "aria-expanded": mentionQuery !== null && candidates.length > 0, "aria-autocomplete": "list", onChange: (event) => {
650
688
  const next = event.target.value;
651
689
  setMentionDrafts(current => transformMentionDrafts(text, next, current));
652
690
  setText(next);
@@ -679,7 +717,7 @@ function Chat(props) {
679
717
  event.preventDefault();
680
718
  if (view.pending === null && sendingDraft === null && (file !== null || text.trim() !== ''))
681
719
  void sendMessage();
682
- }, placeholder: groupWritable ? '输入消息' : '当前群聊暂不可发送消息', rows: 2 }), _jsxs("div", { className: css.composeActions, children: [_jsx(Tooltip, { label: "\u6DFB\u52A0\u9644\u4EF6", side: "top", children: _jsx("button", { type: "button", className: css.filePicker, "aria-label": "\u6DFB\u52A0\u9644\u4EF6", disabled: !groupWritable || view.pending !== null || sendingDraft !== null, onClick: () => { input.current?.click(); }, children: _jsx(IconPaperclipOutline16, {}) }) }), _jsx("input", { ref: input, type: "file", className: css.fileInput, "aria-label": "\u9009\u62E9\u4E00\u4E2A\u9644\u4EF6", disabled: !groupWritable, onChange: (event) => { setFile(event.target.files?.[0] ?? null); setFileError(null); } }), _jsx("button", { type: "button", className: css.send, "aria-label": "\u53D1\u9001\u6D88\u606F", disabled: !groupWritable || view.pending !== null || sendingDraft !== null || (file === null && text.trim() === ''), onClick: () => { void sendMessage(); }, children: _jsx(IconSendOutline16, {}) })] })] })] })] })) }), _jsx(Modal, { open: removeCandidate !== null, onClose: () => { if (view.pending === null) {
720
+ }, placeholder: groupWritable ? '输入消息' : '当前群聊暂不可发送消息', rows: 2 }), _jsxs("div", { className: css.composeActions, children: [_jsx(Tooltip, { label: "\u6DFB\u52A0\u9644\u4EF6", side: "top", children: _jsx("button", { type: "button", className: css.filePicker, "aria-label": "\u6DFB\u52A0\u9644\u4EF6", disabled: !groupWritable || view.pending !== null || sendingDraft !== null, onClick: () => { input.current?.click(); }, children: _jsx(IconPaperclipOutline16, {}) }) }), selected.kind === 'direct' && _jsx(Tooltip, { label: view.messageSecurityReady !== true ? '正在读取消息模式' : view.encryptedConversationIds?.includes(selected.id) ? '端到端加密,点击关闭' : '普通消息,点击开启端到端加密', side: "top", children: _jsx("button", { type: "button", className: css.filePicker, "aria-label": "\u6D88\u606F\u5B89\u5168\u7C7B\u578B", "aria-pressed": view.encryptedConversationIds?.includes(selected.id) ?? false, disabled: view.messageSecurityReady !== true || view.pending === '保存消息模式' || props.setMessageSecurity === undefined, onClick: () => { void props.setMessageSecurity?.(selected.id, !view.encryptedConversationIds?.includes(selected.id)); }, children: view.messageSecurityReady !== true ? _jsx(IconLoadingOutline16, {}) : _jsx(SecurityLock, { open: !view.encryptedConversationIds?.includes(selected.id) }) }) }), view.messageSecurityError && _jsx("button", { type: "button", className: css.secondary, onClick: () => { void props.retryMessageSecurity?.(); }, children: "\u91CD\u8BD5\u8BFB\u53D6\u6D88\u606F\u6A21\u5F0F" }), _jsx("input", { ref: input, type: "file", className: css.fileInput, "aria-label": "\u9009\u62E9\u4E00\u4E2A\u9644\u4EF6", disabled: !groupWritable, onChange: (event) => { setFile(event.target.files?.[0] ?? null); setFileError(null); } }), _jsx("button", { type: "button", className: css.send, "aria-label": "\u53D1\u9001\u6D88\u606F", disabled: !groupWritable || (selected?.kind === 'direct' && view.messageSecurityReady !== true) || view.pending !== null || sendingDraft !== null || (file === null && text.trim() === ''), onClick: () => { void sendMessage(); }, children: _jsx(IconSendOutline16, {}) })] })] })] })] })) }), _jsx(Modal, { open: removeCandidate !== null, onClose: () => { if (view.pending === null) {
683
721
  setRemoveCandidate(null);
684
722
  setConversationPreferenceError(null);
685
723
  } }, title: "\u4ECE\u4F1A\u8BDD\u5217\u8868\u79FB\u9664", closeLabel: "\u53D6\u6D88", className: css.compactModal ?? '', description: removeCandidate === null
@@ -687,7 +725,7 @@ function Chat(props) {
687
725
  : `“${conversationLabel(removeCandidate)}”只会从本机最近会话中移除,不会${removeCandidate.kind === 'group' ? '退出群聊或' : ''}清除已有消息。收到新消息后可能重新出现。`, footer: _jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: view.pending !== null, onClick: () => { setRemoveCandidate(null); setConversationPreferenceError(null); }, children: "\u53D6\u6D88" }), _jsx(Button, { type: "button", variant: "outline", disabled: view.pending !== null, onClick: () => { void removeConversation(); }, children: "\u786E\u8BA4\u79FB\u9664" })] }), children: conversationPreferenceError !== null && _jsx("div", { className: css.conversationPreferenceError, role: "alert", children: conversationPreferenceError }) }), _jsxs(Modal, { open: hiddenConversationsOpen, onClose: () => { if (view.pending === null) {
688
726
  setHiddenConversationsOpen(false);
689
727
  setConversationPreferenceError(null);
690
- } }, title: "\u5DF2\u9690\u85CF\u4F1A\u8BDD", closeLabel: "\u5173\u95ED", className: css.compactModal ?? '', description: "\u8FD9\u4E9B\u4F1A\u8BDD\u53EA\u5728\u672C\u673A\u5217\u8868\u4E2D\u9690\u85CF\uFF0C\u6D88\u606F\u548C\u7FA4\u6210\u5458\u5173\u7CFB\u6CA1\u6709\u6539\u53D8\u3002", footer: _jsx(Button, { type: "button", variant: "outline", disabled: view.pending !== null, onClick: () => { setHiddenConversationsOpen(false); setConversationPreferenceError(null); }, children: "\u5B8C\u6210" }), children: [_jsx("div", { className: css.hiddenConversationList, children: view.hiddenConversations.map(conversation => (_jsxs("div", { className: css.hiddenConversationRow, children: [_jsx("span", { className: css.avatar, children: conversation.kind === 'direct' ? '私' : '群' }), _jsxs("span", { children: [_jsx("strong", { children: conversationLabel(conversation) }), _jsx("small", { children: conversation.lastMessagePreview ?? '暂无消息' })] }), _jsx("button", { type: "button", disabled: view.pending !== null, onClick: () => { void restoreConversation(conversation.id); }, children: "\u6062\u590D" })] }, conversation.id))) }), conversationPreferenceError !== null && _jsx("div", { className: css.conversationPreferenceError, role: "alert", children: conversationPreferenceError })] })] }));
728
+ } }, title: "\u5DF2\u9690\u85CF\u4F1A\u8BDD", closeLabel: "\u5173\u95ED", className: css.compactModal ?? '', description: "\u8FD9\u4E9B\u4F1A\u8BDD\u53EA\u5728\u672C\u673A\u5217\u8868\u4E2D\u9690\u85CF\uFF0C\u6D88\u606F\u548C\u7FA4\u6210\u5458\u5173\u7CFB\u6CA1\u6709\u6539\u53D8\u3002", footer: _jsx(Button, { type: "button", variant: "outline", disabled: view.pending !== null, onClick: () => { setHiddenConversationsOpen(false); setConversationPreferenceError(null); }, children: "\u5B8C\u6210" }), children: [_jsx("div", { className: css.hiddenConversationList, children: view.hiddenConversations.map(conversation => (_jsxs("div", { className: css.hiddenConversationRow, children: [_jsx(AwikiAvatar, { name: conversationLabel(conversation), ...conversation.kind === 'direct' ? { did: conversation.peerDid } : { groupDid: conversation.groupDid }, uri: conversation.avatarUri, thumbnail: conversation.avatarThumbnailUri }), _jsxs("span", { children: [_jsx("strong", { children: conversationLabel(conversation) }), _jsx("small", { children: conversation.lastMessagePreview ?? '暂无消息' })] }), _jsx("button", { type: "button", disabled: view.pending !== null, onClick: () => { void restoreConversation(conversation.id); }, children: "\u6062\u590D" })] }, conversation.id))) }), conversationPreferenceError !== null && _jsx("div", { className: css.conversationPreferenceError, role: "alert", children: conversationPreferenceError })] })] }));
691
729
  }
692
730
  /**
693
731
  * Render the frame-wide AWiki trigger and right-side drawer.
@@ -695,7 +733,8 @@ function Chat(props) {
695
733
  * @returns the persistent trigger and the conditionally mounted drawer.
696
734
  */
697
735
  export function AwikiOverlay(props) {
698
- return _jsx(AwikiDraftProvider, { ...props.drafts === undefined ? {} : { store: props.drafts }, children: _jsx(AwikiOverlayContent, { ...props }) });
736
+ const view = props.useAwiki(state => state);
737
+ return _jsx(AwikiAvatarProvider, { owner: view.identity?.did ?? '', profile: view.profile, avatarDisplayProfiles: props.avatarDisplayProfiles, avatarGroup: props.avatarGroup, children: _jsx(AwikiDraftProvider, { ...props.drafts === undefined ? {} : { store: props.drafts }, children: _jsx(AwikiOverlayContent, { ...props }) }) });
699
738
  }
700
739
  function AwikiOverlayContent(props) {
701
740
  const open = props.useStore(state => state.open);
@@ -710,9 +749,29 @@ function AwikiOverlayContent(props) {
710
749
  const [peerHandle, setPeerHandle] = useDraftState('compose:peerHandle', '');
711
750
  const [composeError, setComposeError] = useState(null);
712
751
  const [composeGroup, setComposeGroup] = useState(false);
713
- const [groupName, setGroupName] = useDraftState('compose:groupName', '');
714
- const [groupMembers, setGroupMembers] = useDraftState('compose:groupMembers', '');
752
+ const [groupName, setGroupName] = useState('');
753
+ const [encryptedGroup, setEncryptedGroup] = useState(false);
754
+ const [groupFrozen, setGroupFrozen] = useState(false);
755
+ const groupDialogGeneration = useRef(0);
756
+ const groupOperationId = useRef(undefined);
757
+ const groupAccount = useRef(view.identity?.did);
758
+ if (groupAccount.current !== view.identity?.did) {
759
+ groupDialogGeneration.current += 1;
760
+ groupAccount.current = view.identity?.did;
761
+ }
762
+ const [groupMembers, setGroupMembers] = useState('');
715
763
  const [groupComposeError, setGroupComposeError] = useState(null);
764
+ const closeGroupDialog = () => {
765
+ groupDialogGeneration.current += 1;
766
+ groupOperationId.current = undefined;
767
+ setComposeGroup(false);
768
+ setGroupName('');
769
+ setGroupMembers('');
770
+ setEncryptedGroup(false);
771
+ setGroupFrozen(false);
772
+ setGroupComposeError(null);
773
+ };
774
+ useEffect(() => { closeGroupDialog(); }, [view.identity?.did]);
716
775
  const [joinGroupOpen, setJoinGroupOpen] = useState(false);
717
776
  const [joinGroupDid, setJoinGroupDid] = useDraftState('compose:joinGroupDid', '');
718
777
  const [joinGroupError, setJoinGroupError] = useState(null);
@@ -779,7 +838,7 @@ function AwikiOverlayContent(props) {
779
838
  setMenuOpen(false);
780
839
  setComposeDirect(false);
781
840
  setComposeError(null);
782
- setComposeGroup(false);
841
+ closeGroupDialog();
783
842
  setGroupComposeError(null);
784
843
  setJoinGroupOpen(false);
785
844
  setJoinGroupError(null);
@@ -817,7 +876,7 @@ function AwikiOverlayContent(props) {
817
876
  return;
818
877
  }
819
878
  if (composeGroup) {
820
- setComposeGroup(false);
879
+ closeGroupDialog();
821
880
  return;
822
881
  }
823
882
  if (joinGroupOpen) {
@@ -1065,14 +1124,19 @@ function AwikiOverlayContent(props) {
1065
1124
  .split(/[\n,,]+/u)
1066
1125
  .map(member => member.trim())
1067
1126
  .filter(member => member !== '');
1068
- const result = await props.createGroup(groupName, members);
1127
+ const generation = groupDialogGeneration.current;
1128
+ const account = groupAccount.current;
1129
+ groupOperationId.current ??= `group-${crypto.randomUUID()}`;
1130
+ setGroupFrozen(true);
1131
+ const result = await props.createGroup(groupName, members, encryptedGroup, groupOperationId.current);
1132
+ if (generation !== groupDialogGeneration.current || account !== groupAccount.current)
1133
+ return;
1069
1134
  if (!result.ok) {
1070
1135
  setGroupComposeError(result.error);
1071
1136
  return;
1072
1137
  }
1073
- setComposeGroup(false);
1074
- setGroupName('');
1075
- setGroupMembers('');
1138
+ closeGroupDialog();
1139
+ await props.selectConversation(result.value.conversation.id);
1076
1140
  };
1077
1141
  const joinGroup = async () => {
1078
1142
  setJoinGroupError(null);
@@ -1123,7 +1187,8 @@ function AwikiOverlayContent(props) {
1123
1187
  setComposeGroup(true);
1124
1188
  if (id === 'join-group')
1125
1189
  setJoinGroupOpen(true);
1126
- }, 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, getConfig: props.getConfig, getMailAccount: props.getMailAccount, listMailInbox: props.listMailInbox, readMail: props.readMail, markMailRead: props.markMailRead, sendMail: props.sendMail, downloadMailAttachment: props.downloadMailAttachment }, `${props.drafts?.getScope('mail:attachments') ?? ''}:${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.tenant.example\nbob.tenant.example' })] }), 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)
1190
+ }, 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, setAvatar: props.setAvatar, clearAvatar: props.clearAvatar, refreshAvatarProfile: props.refreshAvatarProfile }) : null, modeTabs: _jsx(ModeTabs, { mode: mode, mailUnreadCount: mailUnreadCount, onChange: setMode }), onUnreadCountChange: setMailUnreadCount, getConfig: props.getConfig, getMailAccount: props.getMailAccount, listMailInbox: props.listMailInbox, readMail: props.readMail, markMailRead: props.markMailRead, sendMail: props.sendMail, downloadMailAttachment: props.downloadMailAttachment }, `${props.drafts?.getScope('mail:attachments') ?? ''}:${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, onMouseDown: event => { if (event.target === event.currentTarget)
1191
+ closeGroupDialog(); }, 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" }), _jsxs("div", { className: css.groupTypeControl, role: "group", "aria-label": "\u7FA4\u7C7B\u578B", children: [_jsx("button", { type: "button", "aria-pressed": !encryptedGroup, disabled: groupFrozen, onClick: () => setEncryptedGroup(false), children: "\u666E\u901A\u7FA4" }), _jsx("button", { type: "button", "aria-pressed": encryptedGroup, disabled: groupFrozen, onClick: () => setEncryptedGroup(true), children: "\u52A0\u5BC6\u7FA4" })] }), _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, disabled: groupFrozen, 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, disabled: groupFrozen, onChange: (event) => { setGroupMembers(event.target.value); setGroupComposeError(null); }, rows: 4, placeholder: '例如 alice.tenant.example\nbob.tenant.example' })] }), groupFrozen && _jsx("p", { children: "\u6B63\u5728\u6838\u5B9E\u5EFA\u7FA4\u7ED3\u679C\uFF1B\u5173\u95ED\u540E\u4ECD\u4F1A\u7EE7\u7EED\u5904\u7406\u3002" }), 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: () => { closeGroupDialog(); 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)
1127
1192
  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)))] }))] }));
1128
1193
  }
1129
1194
  //# sourceMappingURL=AwikiOverlay.js.map