Skip to content

Chat Hub 附件/媒体查看器、DAG 联邦修复与 Termux 兼容 - #297

Open
steve02081504 wants to merge 44 commits into
masterfrom
fix-termux
Open

Chat Hub 附件/媒体查看器、DAG 联邦修复与 Termux 兼容#297
steve02081504 wants to merge 44 commits into
masterfrom
fix-termux

Conversation

@steve02081504

@steve02081504 steve02081504 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Chat Hub 附件与媒体:新增全屏 mediaViewer(图片/视频:ESC 关闭、左右切换、缩放拖拽、下载);消息附件区重写为画廊/懒加载占位/文件卡;composer 附件预览条与发送队列改进;formatBytes 工具函数。
  • DAG 联邦与归档:新增 knownLocalEventIds.mjs,合并 events.jsonl、归档 manifest、event_meta 与 checkpoint 锚点,避免已折叠/已验收的 tip 父事件被永久当成缺失;branchMessageFilter 分支消息过滤。
  • API 整理:删除 public/src/api/groupChannel.mjs,频道 API 统一收敛到 endpoints/groupChannel.mjs;群设置权限面板与频道权限 UI 调整。
  • Termux / 移动端兼容:WHIP SDP 原生可选(whip_native_optional 测试);channelStreaming 流式路由调整;fount-p2p 升至 ^0.0.23
  • 其他修复memoizePromise 缓存命中返回 Promise.resolve;icon_anime start() 支持 dismiss 后续播 hold;远程 profile 拉取超时测试;locale watch 脚本与 zh-CN 大批量同步。

Test plan

  • fount test --no-parallel(chat shell 相关 manifest)
  • Hub 发送图片/视频/大文件附件,验证画廊、懒加载占位与全屏查看器
  • 联邦 catchup / tip_merge 场景下已归档消息父链不再卡住
  • Termux 环境 WHIP 流式(无原生 SDP 时降级路径)
  • Composer 纸夹选文件后预览条正常、发送后附件出现在消息气泡

新增 Chat Hub 附件工作流和全屏媒体查看器,统一使用 files[],移除旧内联图片标记与频道 API。修复 DAG 联邦归档、分支过滤和 tip 合并逻辑。频道权限改为继承与覆盖模型,好友私聊支持仅用 charname 创建绑定。

通过延迟加载 WHIP 原生依赖提升 Termux 兼容性。同步升级 fount-p2p,延长远程 profile 超时,并修复 memoizePromise、动画状态和 locale 检查问题。测试覆盖附件、联邦、权限、WHIP 和好友群流程。

主要风险是附件状态分布在草稿、pending 行和 IndexedDB 队列中,状态合并复杂。媒体查看器、附件渲染和拖放逻辑集中扩张,增加维护与视觉一致性风险。频道 API 大规模迁移后,旧调用路径若未清理会产生运行时断链。国际化新增 switch 叶节点和多语言校验规则,类型兼容边界更复杂。

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

本次变更更新聊天附件与媒体、好友绑定、频道权限、DAG 联邦、国际化运行时、模块路径和测试调度。消息附件改用 content.files[],并移除历史 [image:...] 标记。

Changes

聊天平台整合

Layer / File(s) Summary
动画生命周期
imgs/icon_anime/*, src/log_viewer/index.mjs
start 会识别已有动画状态。dismiss 后只恢复 hold
聊天附件与媒体
src/public/parts/shells/chat/public/hub/*, src/public/parts/shells/chat/public/src/*, src/public/parts/shells/chat/src/chat/channel/*, src/public/parts/shells/chat/test/*
附件草稿、IndexedDB 离线队列、附件编辑、媒体查看器、消息渲染和测试改用 content.files[]。历史 [image:...] 标记不再生成。
好友绑定与权限
src/public/parts/shells/chat/public/shared/*, src/public/parts/shells/chat/public/hub/*, src/public/parts/shells/chat/public/src/groupSettings/*, src/public/parts/shells/chat/src/group/*
好友绑定支持 charnameentityHash。频道权限面板展示全部角色,并使用覆盖状态和清除覆盖操作。@everyone 默认角色获得 UPLOAD_FILES
DAG 与联邦同步
src/public/parts/shells/chat/src/chat/dag/*, src/public/parts/shells/chat/src/chat/federation/*, src/public/parts/shells/chat/src/group/queries.mjs, src/public/parts/shells/chat/public/shared/branchMessageFilter.mjs
DAG tip 消息不再被折叠删除。归档、元数据和 checkpoint 中的已知事件参与联邦补洞与授权校验。多 tip 频道消息按目标 tip 的祖先闭包过滤。
国际化与运行时支持
src/decl/locale_data.ts, src/public/locales/*, src/public/pages/scripts/i18n/*, .esh/commands/update-locales.py, src/scripts/checks/*
新增本地化字段、switch 叶节点解析、aria-label 语种检查、JSON LF 检查、媒体查看器和 formatBytes
路径迁移与测试规则
deno.json, src/public/parts/shells/cabinet/*, src/public/parts/shells/social/*, src/scripts/test/*, src/server/p2p_server/inbound_handlers.mjs
更新 fount-p2p 版本约束和模块路径。WHIP 改为运行时加载。测试 watchdog 增加 30 分钟下限,并忽略部分文件运行对 suite 基线的更新。

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
审美与简洁 ⚠️ Warning 未通过:src/scripts/i18n/switch_value.mjs:4-8 仅重导出,channelContent.mjs:191-220 保留历史 [image:] 兼容分支,dragAndDrop.mjs:63-78 手写本地化且有冗余防御。 删除后端重导出并直接使用唯一实现;移除历史标记投影;将拖放提示改为带 data-i18n 的模板,并去掉 overlay?.instanceof
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了媒体附件、DAG 联邦修复和 Termux 兼容这三项主要变更,内容具体且未使用空泛表述。
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix-termux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Aug 10, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 7 medium · 15 minor

Alerts:
⚠ 22 issues (≤ 0 issues of at least minor severity)

Results:
22 new issues

Category Results
Documentation 5 minor
CodeStyle 10 minor
Complexity 7 medium

View in Codacy

🟢 Metrics 616 complexity · 0 duplication

Metric Results
Complexity 616
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Comment thread src/public/parts/shells/chat/public/src/composerAttachments.mjs Fixed
Comment thread src/public/parts/shells/chat/public/src/composerAttachments.mjs Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 82

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/public/parts/shells/chat/src/chat/channel/postMessage.mjs (1)

155-201: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

描述符构造重复两遍,抽出去顺便消掉复杂度告警。

160-167 与 177-183 是逐字段相同的描述符构造,sanitizeAlt(file.description) 在 166 行还算了两次。抽一个辅助函数后,函数体和 Codacy 的 51 行 / 复杂度 23 告警一起解决。

♻️ 精简
+/**
+ * 归一化附件描述符。
+ * `@param` {object} file 附件元数据
+ * `@returns` {object} 描述符
+ */
+function fileDescriptor(file) {
+	const description = sanitizeAlt(file.description)
+	return {
+		fileId: String(file.fileId || '').trim(),
+		name: String(file.name || 'file').slice(0, 255),
+		mime_type: String(file.mime_type || 'application/octet-stream'),
+		size: Math.max(0, Number(file.size) || 0),
+		...description ? { description } : {},
+	}
+}
 	const fileDescriptors = mergeExistingFiles && Array.isArray(content?.files)
-		? content.files.map(file => ({
-			fileId: String(file.fileId || '').trim(),
-			name: String(file.name || 'file').slice(0, 255),
-			mime_type: String(file.mime_type || 'application/octet-stream'),
-			size: Math.max(0, Number(file.size) || 0),
-			...sanitizeAlt(file.description) ? { description: sanitizeAlt(file.description) } : {},
-		})).filter(file => file.fileId)
+		? content.files.map(fileDescriptor).filter(file => file.fileId)
 		: []
 		if (existingId && !file.buffer) {
 			if (fileDescriptors.some(d => d.fileId === existingId)) continue
-			const description = sanitizeAlt(file.description)
-			fileDescriptors.push({
-				fileId: existingId,
-				name: String(file.name || 'file').slice(0, 255),
-				mime_type: String(file.mime_type || 'application/octet-stream'),
-				size: Math.max(0, Number(file.size) || 0),
-				...description ? { description } : {},
-			})
+			fileDescriptors.push(fileDescriptor(file))
 			fileIds.push(existingId)
 			continue
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/chat/channel/postMessage.mjs` around lines
155 - 201, 在 attachFilesToContent 中抽取一个复用的文件描述符构造辅助函数,统一处理
fileId、name、mime_type、size 和经 sanitizeAlt 处理的 description;替换现有的 content.files
映射及新文件上传后的重复字段构造,并确保 description 每个文件只计算一次,同时保持现有过滤和字段默认值行为不变。

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/public/locales/emoji.json`:
- Line 1458: 更新 emoji locale 中本次新增的所有文字值,包括 rolesHint
及相关条目;移除中文、英文和描述性文案,仅保留必要的占位符与稳定标识,并将其余内容改为表达相同含义的纯 emoji。

In `@src/public/locales/en-UK.json`:
- Around line 5867-5871: Update the aria-label values in the prev and next
navigation entries from “Previous image” and “Next image” to “Previous media”
and “Next media” so labels correctly cover both images and videos.

In `@src/public/locales/es-ES.json`:
- Line 2786: Update the unread-count aria-label translation near the
"aria-label" entry to use the project's existing pluralization mechanism,
returning “1 elemento no leído” for count 1 and retaining the plural “elementos
no leídos” form for other counts.
- Around line 5867-5872: Update the prev and next aria-label values in the
locale navigation labels to use Spanish wording for “previous media” and “next
media” rather than image-specific or ambiguous text.

In `@src/public/locales/fr-FR.json`:
- Around line 2778-2780: Update the trustedAuthorBadge.title translation in
src/public/locales/fr-FR.json:2778-2780 and
src/public/locales/hi-IN.json:2778-2780 to explicitly mention the risks of
dangerous Markdown/HTML, preserving each locale’s language and existing badge
structure.
- Line 1458: Update the French rolesHint text and the existing
settings.perm.UPLOAD_FILES label to translate UPLOAD_FILES as “Téléverser des
fichiers” instead of “Télécharger des fichiers”, keeping the surrounding
permission guidance unchanged.
- Around line 5867-5871: Update the media viewer navigation labels in
src/public/locales/fr-FR.json lines 5867-5871 to “Média précédent” and “Média
suivant”, and update the corresponding labels in src/public/locales/hi-IN.json
lines 5867-5871 to “पिछला मीडिया” and “अगला मीडिया”.
- Around line 2917-2919: Update the recognize entries in
src/public/locales/fr-FR.json lines 2917-2919 and src/public/locales/hi-IN.json
lines 2917-2919 so both title and aria-label use accurate target-language
wording for converting voice to text: French “Convertir la voix en texte” or
“Transcrire la voix”, and Hindi “आवाज़ को टेक्स्ट में बदलें” or “आवाज़ से
टेक्स्ट”.

In `@src/public/locales/hi-IN.json`:
- Around line 2782-2784: Update the dropToUpload translation in the attachment
locale entry to explicitly preserve the drag-and-drop instruction, using the
requested wording equivalent to “拖放到 #${channel} 以上传” while retaining the
${channel} interpolation.

In `@src/public/locales/is-IS.json`:
- Around line 5867-5872: Update the prev and next aria-label values in the
locale entry to use Icelandic wording for “previous media” and “next media”
instead of “previous image” and “next image,” while leaving the surrounding
media viewer translations unchanged.

In `@src/public/locales/it-IT.json`:
- Around line 2463-2465: Update the breadcrumb aria-label value in the locale
entry to use the clear chat terminology “Discussione” or “Sottothread”,
replacing the ambiguous “filo” while keeping the surrounding localization
structure unchanged.
- Around line 5867-5869: Update the "prev" aria-label in the locale entry from
"Immagine precedente" to "Elemento multimediale precedente" so the media viewer
correctly labels both image and video items.
- Line 1495: Update the clearOverride translation value in the Italian locale so
it reads “Ripristina tutto alle autorizzazioni del ruolo,” clearly expressing
restoration to role permissions.
- Around line 1930-1931: Update the "textContent" value in the locale entry to
"Fidati di questo autore" so the button explicitly identifies the author as the
trusted subject, while leaving the matching "title" unchanged.

In `@src/public/locales/ko-KR.json`:
- Line 2776: Update the “pins” translation in the chat.hub Korean locale to “아직
고정된 메시지가 없습니다.”, aligning the empty-state wording with the existing “상단에 고정” and
“고정된 메시지” terminology.
- Around line 5867-5872: Update the media viewer navigation labels in the prev
and next entries to use consistent Korean terminology meaning “previous media”
and “next media” instead of photo-specific or ambiguous wording. Preserve the
existing aria-label structure and make both labels explicit and symmetric.
- Around line 1492-1496: Update the localized “hint” value in the channel
permission override translations to remove the final sentence referring to the
nonexistent “재정의 추가” button, while preserving the inheritance, allow/deny, and
Everyone file-upload guidance.
- Around line 2778-2780: 更新 trustedAuthorBadge.title,使可信作者徽章完整说明信任 Markdown 和
HTML(包括潜在危险内容)的范围;参考同一语言包中的 chat.hub.trustAuthor.title 文案,保持 textContent 不变。

In `@src/public/locales/lzh.json`:
- Around line 2783-2786: Update the affected lzh locale entries, including
dropToUpload, unreadBadge, and the additional ranges, to use consistent
traditional Chinese/classical lzh wording instead of simplified characters in
labels such as upload, download, image editing, speech-to-text, media viewer,
and unread counts. Preserve the existing translation keys and placeholders while
replacing only the mixed-script text.
- Around line 5867-5871: Update the media viewer navigation labels in
src/public/locales/lzh.json lines 5867-5871 to use generic lzh “previous item”
and “next item” wording instead of image-specific text; also update
src/public/locales/nl-NL.json lines 5867-5871 from “Vorige foto” and “Volgende”
to “Vorig item” and “Volgend item”.

In `@src/public/locales/nl-NL.json`:
- Around line 2778-2780: Update the trustedAuthorBadge.title translation in the
nl-NL locale to explicitly state that both Markdown and HTML rendering are
permitted, replacing the current Markdown-only wording while preserving the
existing trusted-author meaning.

In `@src/public/locales/uk-UA.json`:
- Line 1458: Update the rolesHint strings at both referenced locations to use
the existing UPLOAD_FILES label text "Завантажувати файли" instead of
"Завантаження файлів", keeping the rest of each hint unchanged.
- Around line 5867-5871: Update the prev and next aria-label values in the media
navigation translations from image-specific wording to the Ukrainian equivalents
of “Previous media” and “Next media,” while preserving the existing locale
structure.
- Around line 1930-1931: 同步 chat.group.trustAuthor 与 chat.hub.trustAuthor
的乌克兰语文案:将操作文本统一为“Довіряти”,并在标题中明确说明允许不安全的 Markdown/HTML;仅更新这两个 trustAuthor
入口的对应翻译键,保持其余本地化内容不变。

In `@src/public/pages/scripts/components/mediaViewer.mjs`:
- Around line 25-46: Replace the custom applyI18nObject logic with the shared
setElementI18n API, preserving the existing media viewer translation keys and
target elements in localizeMediaViewer. Remove the manual geti18n_nowarn lookup
and aria-label/textContent handling, and update any setLocalizeLogic replay path
to reuse the shared localization flow instead of duplicating it.
- Around line 252-260: 移除 openMediaViewer 中监听整个 document.body 的 MutationObserver
及其并行清理逻辑;在 closeMediaViewer 中统一调用 document.removeEventListener('keydown', onKey)
并清理 activeViewer。若需支持外部移除节点,复用 i18n 模块的 onElementRemoved;同时拆分或抽取 openMediaViewer
内部逻辑以解决函数过长问题。
- Around line 268-358: 调整媒体查看器的样式定义,使 .media-viewer 使用 DaisyUI
modal/modal-backdrop 语义及现有主题变量,移除硬编码的 rgba 背景色和 `#fff`。将 .media-viewer-btn 与
.media-viewer-nav 改为复用 DaisyUI btn/btn-circle 类,仅保留 stage、transform 等无法由 DaisyUI
覆盖的布局与交互规则。

In `@src/public/pages/scripts/lib/formatBytes.mjs`:
- Around line 7-14: 在 formatBytes 中移除对已声明为 number 的 bytes 的 Number(bytes) 转换和
Number.isFinite 防御性检查,直接使用 bytes 进行计算;同时将局部变量 n、k、dm、i
重命名为符合命名规范的完整名称,并保持现有非正数返回及字节单位格式化行为不变。

In `@src/public/parts/shells/chat/public/hub/AGENTS.md`:
- Line 48: Rewrite the guidance line in AGENTS.md in English while preserving
all identifiers, API names, endpoint references, and the existing behavior
distinctions for char searches, entity hits, friendBinding, and network handle
searches.

In `@src/public/parts/shells/chat/public/hub/composerDraft.mjs`:
- Around line 91-132: Refactor loadDraft into focused helpers: capture and
validate the message, content-warning, and sensitive-media elements once, then
use an applyDraft helper for reset and text/settings restoration and a
restoreDraftFiles helper for attachment recovery and visibility. Move the
renderAttachmentPreview dynamic import into restoreDraftFiles so it occurs only
when peekDraftFiles returns attachments, while preserving current draft and
preview behavior.
- Around line 123-130: Update loadDraft’s use of peekDraftFiles so it receives a
copied array with cloned file objects rather than the internal draft references;
preserve the existing selectedFiles population and preview rendering while
preventing edits to restored files from mutating the stored draft snapshot.

In `@src/public/parts/shells/chat/public/hub/core/domUtils.mjs`:
- Line 36: 统一更新三处角色名处理逻辑:定义仅接收字符串的 normalizeCharPartName,并在相关缓存键生成路径中复用它,统一执行
trim().toLowerCase()。替换当前 String(... || '') 的静默兜底,确保上游非字符串值直接暴露错误而不是转换为空字符串。

In `@src/public/parts/shells/chat/public/hub/entityProfile.mjs`:
- Around line 255-261: Reuse a single isSelf value computed before the dmButton
handling, removing the duplicate isViewerEntityHash(entityHash) declarations
near the existing checks and in the trust-button block. In the trust-button
canTrust calculation, rely on the truthiness contract of
resolveTrustAuthorPubKeyHash and remove the redundant isHex64(authorPubKeyHash)
validation.
- Around line 137-148: Refactor resolveTrustAuthorPubKeyHash by introducing a
local normHex helper for String(value || '').trim().toLowerCase() normalization,
then reuse it for entity.pubKeyHash, entity.entityHash, member.entityHash,
member.pubKeyHash/memberKey, and profile.activePubKeyHex without changing the
existing fallback order or validation behavior.

In `@src/public/parts/shells/chat/public/hub/hubContact.mjs`:
- Line 7: 在 hubContact.mjs 的联系人绑定流程中,改用 charFriendBindingInput,并传入
friend.charname 与 friend.displayName,替代会移除 charname 的
entityFriendBindingInput;同时更新对应的导入,确保 enterFriendChat() 收到 resolved.charname 并触发
ensureCharOnGroup()。

In `@src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs`:
- Around line 154-181: Use retainLocalAttachmentBuffers in
channelMessageStore.mjs as the sole attachment-buffer restoration
implementation. In
src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs lines
154-181, export retainLocalAttachmentBuffers unchanged as the shared helper. In
src/public/parts/shells/chat/public/hub/messages/messageSend.mjs lines 31-39,
remove the duplicate backfill logic from channelRowFromPostedEvent and restore
its single-argument signature; in confirmPendingRow, call
retainLocalAttachmentBuffers(pendingRow, realRow) when confirming the server
row.
- Around line 154-181: 精简 retainLocalAttachmentBuffers,移除对 file.buffer 类型、fileId
字符串转换及数组内容的防御性盘问,直接使用本地数据。删除 byId/byNameMime 同时写入后无意义的空索引判断,并保留现有按 fileId、名称和
MIME 类型恢复 buffer 的行为。
- Around line 197-208: Update the batch merge logic around pendingRow and
retainLocalAttachmentBuffers so pendingRow is used only when the current eventId
equals composerPendingId; never use it as the fallback for unrelated batch rows.
Preserve existing byId lookup behavior for other messages, and ensure pending
attachment buffers are retained only for the confirmed composer message.

In `@src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs`:
- Around line 78-84: Update the catch block in renderMessageRowElement to stop
exposing error.message or raw exception text through fallback.textContent. Keep
the detailed console.error logging, and set the fallback element’s data-i18n
attribute plus a stable user-facing error message key/value instead.

In `@src/public/parts/shells/chat/public/hub/messages/render/file.mjs`:
- Around line 300-309: 在遍历 visible 的循环中,将循环变量 i 重命名为
visibleIndex,并同步更新该循环体内所有引用,保持画廊可见项索引及溢出判断行为不变。
- Around line 180-201: 调整附件渲染流程:在调用 loadGroupFileBlobUrl 前先计算 file.size,并依据
LAZY_MEDIA_BYTES、id 和 local 判断大媒体是否应返回 media_placeholder;仅在不满足懒加载条件时读取 Blob
URL。保持图片渲染及已有 local 文件路径行为不变。

In `@src/public/parts/shells/chat/public/hub/messages/render/index.mjs`:
- Around line 190-195: 在计算 alreadyTrusted 的消息渲染逻辑中处理 isTrustedAuthor 的
reject,将信任存储查询失败视为 false;确保 renderMessageRowElement 仍能渲染远程消息,仅不生成
trustedAuthorBadge。

In `@src/public/parts/shells/chat/public/hub/messages/render/text.mjs`:
- Line 79: Remove the unused unescapeAttrUrl utility from the text-rendering
module, along with any associated inline-image rendering logic that exists
solely to support it, while preserving the remaining message HTML conversion
behavior.

In `@src/public/parts/shells/chat/public/hub/sendQueue.mjs`:
- Around line 29-53: Use full descriptive names in the IndexedDB flows: rename
req to request and tx to transaction in openDb and withStore, updating all
references. In sendGroupMessage(groupId, channelId, content, files), remove the
redundant files?.length ? files : undefined expression and rely on the existing
files = [] default plus the length check.
- Around line 27-55: Cache the IndexedDB connection promise at module scope and
update openDb to reuse it instead of calling indexedDB.open for every withStore
invocation. Ensure concurrent callers share the same promise, while preserving
the existing upgrade, success, and error handling behavior.

In `@src/public/parts/shells/chat/public/hub/sidebar/selectChannel.mjs`:
- Around line 34-48: 在 composerDraft.mjs 中将 saveDraft 的实际落盘逻辑抽出并导出即时写入函数,保留
saveDraft 的 debounce 行为;在 selectChannel
中保存上一频道草稿时改用该即时函数,确保文本、内容警告和敏感媒体状态同步持久化。与此同时,将局部变量 cw 和 sm 重命名为可读的完整名称。

In `@src/public/parts/shells/chat/public/hub/wiring/fileEvents.mjs`:
- Around line 12-13: Update the event handler to use event.currentTarget as the
input element, removing the instanceof HTMLInputElement check and its
early-return branch because the listener is bound directly to
image-upload-input.
- Around line 14-17: 在文件选择处理逻辑中,完成 files 快照后立即执行 input.value =
'',再进行群组和频道上下文检查;保留现有无文件及上下文不完整时的提前返回行为,确保任何早退都不会保留已选文件。

In `@src/public/parts/shells/chat/public/shared/branchMessageFilter.mjs`:
- Around line 16-21: 统一 `branchMessageFilter` 中的 `Map` 输入处理:不要直接复用调用方的键,而应从
`Map` 的 values 重建以 `String(event.id).trim().toLowerCase()` 为键的索引,使其与
`ancestorClosureFromTip` 及其他路径一致;同时移除数组输入处理中不属于 JSDoc 契约的 `|| []` 回退。

In `@src/public/parts/shells/chat/public/shared/channelContent.mjs`:
- Around line 199-207: 更新 stripInlineImageMarkers,避免对替换后的全文统一折叠连续换行;仅清理由
INLINE_IMAGE_MARKER_RE 移除后紧邻产生的空行,同时保留用户原有空行。改用直接 replace 或确保每次 test 前可靠重置全局正则的
lastIndex,避免模块级带 g 正则状态泄漏。

In `@src/public/parts/shells/chat/public/shared/friendBinding.mjs`:
- Around line 51-56: Rename the local variable `eh` in
`entityFriendBindingInput` to `normalizedEntityHash`, and update its validation
and returned `entityHash` references accordingly without changing the behavior.
- Around line 35-56: 为导出的 charFriendBindingInput 和 entityFriendBindingInput
JSDoc 各添加一行真实的中文摘要,明确说明它们分别用于构造基于 charname 或 entityHash
的建群好友绑定输入;保留现有参数、返回值及互斥关系说明不变。

In `@src/public/parts/shells/chat/public/src/composerAttachments.mjs`:
- Around line 159-175: 在附件渲染流程中移除只使用一次的临时绑定:将
showDownloadButton、showDeleteButton 及 buttonCount 的计算分别内联到
renderTemplate('attachment_preview') 的对应参数中,并在模板参数中直接使用 !composing、composing
及按钮条件表达式;保留 showSpeechRecognitionButton 和 showEditButton 作为现有复用绑定。
- Around line 186-188: 统一复用已由 String(file.mime_type || '') 得到的 mime 值及其 isImage
判定,更新 composerAttachments 中图片、视频和音频分支,避免直接调用可能缺失的
file.mime_type.startsWith;确保对应逻辑分别使用 isImage、mime.startsWith('video/') 和
mime.startsWith('audio/'),在缺少 mime_type 时仍能安全渲染。
- Around line 44-57: The shared composer attachment module must not depend on
Hub-level state or UI. In composerAttachments.mjs lines 44-57, remove
syncComposerExtrasVisibility and leave composer extras visibility to Hub’s
setComposerExtrasVisible; in lines 227-252, remove the hub/core/state.mjs import
and change renderAttachmentPreview so groupId is supplied by its caller,
updating all affected call sites accordingly.
- Around line 253-262: Update the video preview flow in composerAttachments to
convert localBuffer into a Blob and use URL.createObjectURL for the video src
instead of constructing a base64 data URI, then pass that object URL to
renderTemplate and openMediaViewer. Apply the same object-URL approach to the
adjacent image preview branch, and revoke each created URL when its preview is
removed or no longer needed.

In `@src/public/parts/shells/chat/public/src/endpoints/groupChannel.mjs`:
- Around line 334-346: 简化 editChannelMessage 中对 files 的处理:移除两处多余的 (files ||
[]),直接使用默认值兜底的 files;同时将保留文件与上传文件的筛选合并为一次遍历分流,保持 retained、uploads 及后续请求体行为不变。

In `@src/public/parts/shells/chat/public/src/groupSettings/channelPermsTab.mjs`:
- Around line 20-28: Update roleHasOverride to inline the override?.allow and
override?.deny fallback expressions directly inside their respective Object.keys
calls, then remove the single-use allow and deny local variables while
preserving the existing boolean result.

In `@src/public/parts/shells/chat/public/src/templates/attachment_preview.html`:
- Around line 6-17: 统一 attachment_preview.html 中四个按钮图标的 i18n 处理:移除下载和删除按钮 img
元素上的 data-i18n 属性,仅保留按钮自身的翻译键;同时删除 zh-CN.json 中对应的 downloadIcon 和 deleteIcon
locale 条目。

In
`@src/public/parts/shells/chat/public/src/templates/group/settings/channel_permissions_panel.html`:
- Around line 29-38: Reduce the channel permissions panel’s initial DOM and
event-binding cost: avoid eagerly rendering every role’s `permRows` and do not
attach separate listeners to each permission button. Update the panel rendering
and its associated channel-permission interaction logic in channelPermsTab.mjs
to render permission rows when the role’s details section opens and use
container-level event delegation for state changes, while preserving the
existing permission states and role behavior.
- Around line 21-25: 在角色设置面板的颜色渲染处,不要直接将 panel.color 拼入 style;先通过白名单校验确保其为合法 Hex
颜色值,仅允许符合预期格式的值通过,否则回退为 `#888`,再将校验后的颜色用于 settings-role-swatch 的背景色。

In `@src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs`:
- Around line 46-48: Keep the existing null and duplicate-wiring guard in
addMessageAreaFileDrop at
src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs lines 46-48 as the
sole entry guard. In
src/public/parts/shells/chat/public/hub/wiring/composerEvents.mjs lines 17-24,
remove the HTMLElement check and `#messages` parent fallback, and pass
document.querySelector('.main-body') directly to addMessageAreaFileDrop.
- Around line 73-75: Remove the English fallback from the label assignment in
the drag-and-drop UI: set label.textContent directly to
geti18n('chat.hub.attachment.dropToUpload', { channel }) while preserving the
existing channel value.
- Around line 99-102: Update the dragleave handler on dropRoot to decrement
depth and hide the overlay only for file drag events, matching the
hasFiles(event) guard used by dragenter; ignore non-file drags so depth remains
symmetric and the overlay stays visible during an active file drag.

In `@src/public/parts/shells/chat/src/chat/dag/knownLocalEventIds.mjs`:
- Around line 22-45: Extract a private addKnownEventId(eventId, knownEventIds)
helper that normalizes the ID, validates it with isHex64, and adds valid values
to the set. Update addCheckpointKnownEventIds and its loops to call this helper,
rename out to knownEventIds and n to normalizedId where applicable, and remove
the repeated normalization/validation/add logic while preserving existing
handling of all checkpoint ID sources.

In `@src/public/parts/shells/chat/src/chat/federation/index.mjs`:
- Around line 313-324: Rename the federation catch-up loop variable `iter` to
`iteration` throughout the same function, including the `computeWantSet` call.
Rename the corresponding `MAX_CATCHUP_ITERS` constant to
`MAX_CATCHUP_ITERATIONS` at its declaration and every reference, without
changing loop behavior.

In `@src/public/parts/shells/chat/src/chat/whip/sdp.mjs`:
- Around line 53-57: Update loadNodeDataChannel to return the default export
directly from the dynamic import of npm:node-datachannel, removing the temporary
mod binding and the redundant mod.default ?? mod compatibility fallback.

In `@src/public/parts/shells/chat/src/group/queries.mjs`:
- Around line 201-208: 调整 listChannelMessages 的查询顺序:先对完整候选消息集应用
filterLinesToDisplayBranch 分支过滤,再执行 before、since 和 limit
分页截取,确保非展示分支不会占用页面容量;不要仅通过增加读取数量解决问题,并保持无分支过滤时现有分页行为不变。

In `@src/public/parts/shells/chat/src/group/routes/channelMessages.mjs`:
- Around line 111-127: 在 channelMessages 路由模块中复用统一的文件 base64 解码映射逻辑,避免 POST
路由与另一处重复实现;将 attachFilesToContent 从动态 import 改为文件顶部的静态导入,并保留现有文件合并与
maxDagPayloadBytes 处理行为。

In `@src/public/parts/shells/chat/src/group/routes/channelStreaming.mjs`:
- Around line 103-112: 将动态导入 startWhipIngest 的语句移入现有 try 块,使
import('../../chat/whip/ingest.mjs') 抛出的 WHIP_NATIVE_UNAVAILABLE 也经过当前
catch,并继续转换为 501 且设置 skip_report;保留成功响应和其他错误的现有处理。

In `@src/public/parts/shells/chat/src/group/routes/groups.mjs`:
- Around line 119-131: Update the existing-group response in the reuse path
around the rows lookup and registerGroupRuntime to return existing.friendBinding
instead of the request’s friendBinding, and add a test covering group reuse when
charname matches case-insensitively but entityHash differs.

In `@src/public/parts/shells/chat/test/frontend/attachments.spec.mjs`:
- Around line 35-36: 统一使用完整、可读的局部变量名:在
src/public/parts/shells/chat/test/frontend/attachments.spec.mjs:35-36 将 response
回调参数中的 res 改为 response;在同文件:64-66 将 img 改为 image;在
src/public/parts/shells/chat/test/frontend/composer.spec.mjs:72-77 将 img 改为
image;在
src/public/parts/shells/chat/test/integration/message_attachments.test.mjs:41 将
r 改为 row;在
src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs:21-24
将 i 改为 lineIndex,并同步更新各自作用域内的引用。
- Around line 10-14: Update the TINY_PNG_BUFFER JSDoc summaries to include
Chinese characters, using wording equivalent to “1×1 PNG 测试数据”, in
src/public/parts/shells/chat/test/frontend/attachments.spec.mjs lines 10-14,
src/public/parts/shells/chat/test/frontend/composer.spec.mjs lines 12-16, and
src/public/parts/shells/chat/test/integration/message_attachments.test.mjs lines
11-15.

In `@src/public/parts/shells/chat/test/integration/friend_char_group.test.mjs`:
- Line 16: Rename the CHAR constant to characterName and update all its
references. In the same test, replace viewerRes, createRes, and addCharRes with
descriptive full-word Response variable names, updating every use while
preserving behavior.
- Around line 25-27: 在 chatFetch 中移除仅使用一次的 sep 临时绑定,将 path.includes('?')
的条件表达式直接内联到 url 模板字符串中,并保持现有查询参数分隔符逻辑不变。

In `@src/public/parts/shells/chat/test/integration/message_attachments.test.mjs`:
- Around line 49-53: Update the waitUntil predicate around searchGroupMessages
so a match is accepted only when hit.text contains the query token and does not
contain “[image:”. Preserve the existing eventId matching requirement and reject
missing or empty hit.text values.

In
`@src/public/parts/shells/chat/test/integration/validate_ingest_pendable.test.mjs`:
- Around line 142-143: Remove the redundant dynamic imports and aliases around
the test setup, reusing the existing writeFile, mkdir, and dirname bindings
already available in the current scope. In the validation test, remove the
thrown-error capture and directly await validateIngestAuthz(...), allowing
unexpected exceptions to fail the test normally.

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`:
- Around line 16-27: Update staticNodeDatachannelImportsInFile in
src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs
(lines 16-27) to detect both import declarations and export-from declarations
referencing node-datachannel. Update the WHIP scan in lines 34-43 to recursively
inspect all .mjs files under whip/. Update the channel-route dependency check in
lines 50-53 to recognize export-from references containing whip/ as well as
imports.

In `@src/public/parts/shells/chat/test/manifest.json`:
- Around line 697-708: 在 attachments 子测试的 triggers 配置中加入 formatBytes.mjs
的路径,使修改该工具时能够触发附件测试;更新 diff 中的 attachments 配置,保留现有触发器不变。

In `@src/public/parts/shells/chat/test/pure/branch_message_filter.test.mjs`:
- Around line 19-24: 更新围绕 filterChannelMessageLinesByBranchTip 的测试数据,在 lines 中加入
tipA 的 root 祖先消息行;将断言扩展为按顺序同时保留 root 和 tipA,以验证祖先闭包行为,而非仅验证目标 tip 行被保留。

In `@src/public/parts/shells/chat/test/pure/memoize_promise.test.mjs`:
- Around line 24-25: Remove the unnecessary optional chaining in the assertions
within the memoize promise test: access second.then and second.catch directly,
while preserving the existing thenable and catch-function checks.

In `@src/public/parts/shells/chat/test/pure/run_uri.test.mjs`:
- Around line 28-30: 在 run_uri 测试中将缩写变量 pub、pow、node 重命名为
introducerPubKeyHash、powAnchorRef、introducerNodeHash,并同步更新相关调用与断言,确保变量名使用完整词汇且与字段语义一致。

---

Outside diff comments:
In `@src/public/parts/shells/chat/src/chat/channel/postMessage.mjs`:
- Around line 155-201: 在 attachFilesToContent 中抽取一个复用的文件描述符构造辅助函数,统一处理
fileId、name、mime_type、size 和经 sanitizeAlt 处理的 description;替换现有的 content.files
映射及新文件上传后的重复字段构造,并确保 description 每个文件只计算一次,同时保持现有过滤和字段默认值行为不变。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b4878364-0f76-42c7-8bba-a82e6fe70203

📥 Commits

Reviewing files that changed from the base of the PR and between 3e8fecf and 2f5ab0a.

📒 Files selected for processing (132)
  • deno.json
  • imgs/icon_anime/AGENTS.md
  • imgs/icon_anime/session.mjs
  • src/decl/locale_data.ts
  • src/log_viewer/index.mjs
  • src/public/locales/ar-SA.json
  • src/public/locales/de-DE.json
  • src/public/locales/emoji.json
  • src/public/locales/en-UK.json
  • src/public/locales/es-ES.json
  • src/public/locales/fr-FR.json
  • src/public/locales/hi-IN.json
  • src/public/locales/is-IS.json
  • src/public/locales/it-IT.json
  • src/public/locales/ja-JP.json
  • src/public/locales/ko-KR.json
  • src/public/locales/lzh.json
  • src/public/locales/nl-NL.json
  • src/public/locales/pt-PT.json
  • src/public/locales/ru-RU.json
  • src/public/locales/uk-UA.json
  • src/public/locales/vi-VN.json
  • src/public/locales/zh-CN.json
  • src/public/locales/zh-TW.json
  • src/public/pages/AGENTS.md
  • src/public/pages/scripts/components/mediaViewer.mjs
  • src/public/pages/scripts/lib/formatBytes.mjs
  • src/public/pages/scripts/lib/memo.mjs
  • src/public/pages/scripts/test/watch/locale.mjs
  • src/public/pages/scripts/test/watch/locale_script.mjs
  • src/public/parts/shells/chat/public/hub/AGENTS.md
  • src/public/parts/shells/chat/public/hub/components.css
  • src/public/parts/shells/chat/public/hub/composerDraft.mjs
  • src/public/parts/shells/chat/public/hub/composerExtras.mjs
  • src/public/parts/shells/chat/public/hub/core/domUtils.mjs
  • src/public/parts/shells/chat/public/hub/docs/ui-details.md
  • src/public/parts/shells/chat/public/hub/entityProfile.mjs
  • src/public/parts/shells/chat/public/hub/entityResolve.mjs
  • src/public/parts/shells/chat/public/hub/friendChat.mjs
  • src/public/parts/shells/chat/public/hub/groupContextMenu.mjs
  • src/public/parts/shells/chat/public/hub/hubContact.mjs
  • src/public/parts/shells/chat/public/hub/index.html
  • src/public/parts/shells/chat/public/hub/initCore.mjs
  • src/public/parts/shells/chat/public/hub/messages.css
  • src/public/parts/shells/chat/public/hub/messages/actions/edit.mjs
  • src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSend.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/file.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/index.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/text.mjs
  • src/public/parts/shells/chat/public/hub/presence.mjs
  • src/public/parts/shells/chat/public/hub/privateGroup.mjs
  • src/public/parts/shells/chat/public/hub/sendQueue.mjs
  • src/public/parts/shells/chat/public/hub/sidebar/selectChannel.mjs
  • src/public/parts/shells/chat/public/hub/unread.mjs
  • src/public/parts/shells/chat/public/hub/wiring/composerEvents.mjs
  • src/public/parts/shells/chat/public/hub/wiring/fileEvents.mjs
  • src/public/parts/shells/chat/public/hub/wiring/messageBubbleEvents.mjs
  • src/public/parts/shells/chat/public/llms.txt
  • src/public/parts/shells/chat/public/settings/index.html
  • src/public/parts/shells/chat/public/shared/branchMessageFilter.mjs
  • src/public/parts/shells/chat/public/shared/channelContent.mjs
  • src/public/parts/shells/chat/public/shared/entityProfileCard.mjs
  • src/public/parts/shells/chat/public/shared/entityProfileHoverCard.mjs
  • src/public/parts/shells/chat/public/shared/entityProfilePopup.mjs
  • src/public/parts/shells/chat/public/shared/friendBinding.mjs
  • src/public/parts/shells/chat/public/shared/runUri.mjs
  • src/public/parts/shells/chat/public/src/api/groupChannel.mjs
  • src/public/parts/shells/chat/public/src/composerAttachments.mjs
  • src/public/parts/shells/chat/public/src/endpoints/groupChannel.mjs
  • src/public/parts/shells/chat/public/src/endpoints/groupCore.mjs
  • src/public/parts/shells/chat/public/src/groupSettings/channelPermsTab.mjs
  • src/public/parts/shells/chat/public/src/groupSettings/load.mjs
  • src/public/parts/shells/chat/public/src/groupSettings/permissionsTab.mjs
  • src/public/parts/shells/chat/public/src/templates/attachment_preview.html
  • src/public/parts/shells/chat/public/src/templates/group/settings/channel_permissions_panel.html
  • src/public/parts/shells/chat/public/src/templates/group/settings/permission_role.html
  • src/public/parts/shells/chat/public/src/templates/group/settings/permissions_panel.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/channel_header.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/edit_body.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/inline_image.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/inline_video.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/trust_author_button.html
  • src/public/parts/shells/chat/public/src/templates/hub/messages/trusted_author_badge.html
  • src/public/parts/shells/chat/public/src/templates/hub/profile_edit_modal.html
  • src/public/parts/shells/chat/public/src/templates/hub/profile_popup.html
  • src/public/parts/shells/chat/public/src/templates/thread_drawer.html
  • src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs
  • src/public/parts/shells/chat/public/src/ui/modal.mjs
  • src/public/parts/shells/chat/src/chat/channel/postMessage.mjs
  • src/public/parts/shells/chat/src/chat/dag/foldEvents.mjs
  • src/public/parts/shells/chat/src/chat/dag/foldPolicy.mjs
  • src/public/parts/shells/chat/src/chat/dag/ingest.mjs
  • src/public/parts/shells/chat/src/chat/dag/knownLocalEventIds.mjs
  • src/public/parts/shells/chat/src/chat/dag/reducers/roles.mjs
  • src/public/parts/shells/chat/src/chat/federation/index.mjs
  • src/public/parts/shells/chat/src/chat/federation/pendingIngest.mjs
  • src/public/parts/shells/chat/src/chat/federation/room.mjs
  • src/public/parts/shells/chat/src/chat/files/groupFiles.mjs
  • src/public/parts/shells/chat/src/chat/whip/sdp.mjs
  • src/public/parts/shells/chat/src/entity/filesEndpoints.mjs
  • src/public/parts/shells/chat/src/entity/profile.mjs
  • src/public/parts/shells/chat/src/group/queries.mjs
  • src/public/parts/shells/chat/src/group/routes/channelMessages.mjs
  • src/public/parts/shells/chat/src/group/routes/channelStreaming.mjs
  • src/public/parts/shells/chat/src/group/routes/groups.mjs
  • src/public/parts/shells/chat/src/permissions/chat.mjs
  • src/public/parts/shells/chat/test/frontend/attachments.spec.mjs
  • src/public/parts/shells/chat/test/frontend/composer.spec.mjs
  • src/public/parts/shells/chat/test/frontend/phases.mjs
  • src/public/parts/shells/chat/test/frontend/secondaryPages.spec.mjs
  • src/public/parts/shells/chat/test/integration/friend_char_group.test.mjs
  • src/public/parts/shells/chat/test/integration/message_attachments.test.mjs
  • src/public/parts/shells/chat/test/integration/remote_profile_fetch_timeout.test.mjs
  • src/public/parts/shells/chat/test/integration/validate_ingest_pendable.test.mjs
  • src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs
  • src/public/parts/shells/chat/test/manifest.json
  • src/public/parts/shells/chat/test/pure/actor_authorize.test.mjs
  • src/public/parts/shells/chat/test/pure/archive_fold_and_hot_index.test.mjs
  • src/public/parts/shells/chat/test/pure/branch_message_filter.test.mjs
  • src/public/parts/shells/chat/test/pure/memoize_promise.test.mjs
  • src/public/parts/shells/chat/test/pure/message_text_projection.test.mjs
  • src/public/parts/shells/chat/test/pure/role_create_default.test.mjs
  • src/public/parts/shells/chat/test/pure/run_uri.test.mjs
  • src/public/parts/shells/userSettings/src/actions.mjs
  • src/public/parts/shells/userSettings/src/endpoints.mjs
  • src/scripts/memo.mjs
  • src/scripts/search/invertedIndex.mjs
  • src/scripts/test/docs/domain-harness.md
  • src/scripts/test/docs/playwright.md
  • src/scripts/test/selftest/page_watch.test.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • steve02081504/fount-p2p (manual)
💤 Files with no reviewable changes (4)
  • src/public/parts/shells/chat/public/src/templates/hub/messages/trust_author_button.html
  • src/public/parts/shells/chat/public/src/ui/modal.mjs
  • src/public/parts/shells/chat/public/hub/wiring/messageBubbleEvents.mjs
  • src/public/parts/shells/chat/public/src/api/groupChannel.mjs

Comment thread src/public/locales/emoji.json Outdated
Comment thread src/public/locales/en-UK.json Outdated
Comment thread src/public/locales/es-ES.json Outdated
"dropToUpload": "Suelta para subir a #${channel}"
},
"unreadBadge": {
"aria-label": "${count} elementos no leídos"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

为未读数量提供单数文案。

count1 时,当前文案会显示 1 elementos no leídos。这不符合西班牙语语法。请使用项目现有的复数机制,提供 1 elemento no leído 与复数形式。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/es-ES.json` at line 2786, Update the unread-count
aria-label translation near the "aria-label" entry to use the project's existing
pluralization mechanism, returning “1 elemento no leído” for count 1 and
retaining the plural “elementos no leídos” form for other counts.

Comment thread src/public/locales/es-ES.json
Comment thread src/public/locales/fr-FR.json Outdated
Comment thread src/public/parts/shells/chat/test/integration/validate_ingest_pendable.test.mjs Outdated
Comment thread src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs Outdated
Comment thread src/public/parts/shells/chat/test/manifest.json
Comment thread src/public/parts/shells/chat/test/pure/branch_message_filter.test.mjs Outdated
Comment thread src/public/parts/shells/chat/test/pure/memoize_promise.test.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment thread src/public/locales/uk-UA.json
Comment thread src/public/locales/uk-UA.json Outdated
Comment thread src/public/pages/scripts/components/mediaViewer.mjs Outdated
Comment thread src/public/pages/scripts/components/mediaViewer.mjs Outdated
Comment thread src/public/parts/shells/chat/public/hub/AGENTS.md Outdated
Comment thread src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs Outdated
Comment thread src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs Outdated
Comment thread src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs Outdated
Comment thread src/public/parts/shells/chat/src/group/routes/channelMessages.mjs Outdated
Comment thread src/public/parts/shells/chat/src/group/routes/channelStreaming.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment thread src/public/pages/scripts/components/mediaViewer.mjs
Comment thread src/public/parts/shells/chat/test/pure/run_uri.test.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 29

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
src/public/parts/shells/chat/src/group/routes/channelMessages.mjs (1)

123-135: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

先完成编辑校验,再上传附件。

attachFilesToContent 会为新附件调用 uploadPlainFileToGroup。当前流程在 findChannelMessageRow 和编辑授权钩子之前上传附件。目标消息不存在或编辑被拒绝时,文件已经写入群组存储,但不会写入消息事件。

先完成目标消息存在性和编辑权限校验,再调用 attachFilesToContent。不要用事后清理掩盖错误的副作用顺序。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/group/routes/channelMessages.mjs` around
lines 123 - 135, Reorder the edit flow so findChannelMessageRow and the
edit-authorization hook complete successfully before processing attachments.
Move the attachFilesToContent block that handles processedFiles and existing
contentObj.files after those validations, ensuring rejected or missing-message
edits never upload files.
src/public/locales/emoji.json (1)

1944-1949: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

用纯 emoji 替换新增的中文文案。

messageRenderFailed(第 1948 行)和 cabinet.breadcrumb.aria-label(第 4803 行)新增值为纯中文文案「消息渲染失败」「面包屑导航」,与 emoji locale 的意译约定不符。参考同文件其它字段(如 markdownRenderFailed: "💬🎨❌"、thread.breadcrumb: "🧵➡️")的处理方式,改为纯 emoji 意译。

🛠️ 建议修改
-			"messageRenderFailed": "消息渲染失败",
+			"messageRenderFailed": "💬🎨❌",
-		"breadcrumb": {
-			"aria-label": "面包屑导航"
-		},
+		"breadcrumb": {
+			"aria-label": "🍞➡️"
+		},

Also applies to: 4802-4804

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/emoji.json` around lines 1944 - 1949, Replace the newly
added Chinese values for messageRenderFailed and cabinet.breadcrumb.aria-label
in the emoji locale with pure-emoji equivalents, following nearby entries such
as markdownRenderFailed and thread.breadcrumb; leave all other locale values
unchanged.

Source: Path instructions

src/public/locales/is-IS.json (1)

1943-1948: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

messageRenderFailed 使用独立文案。

messageRenderFailedmarkdownRenderFailed 都是 "Skilaboð mistókst"。界面无法说明消息渲染失败。请改成明确表示“无法显示消息”的冰岛语文案。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/is-IS.json` around lines 1943 - 1948, Update the
messageRenderFailed translation to use distinct Icelandic wording that clearly
means the message cannot be displayed, while leaving markdownRenderFailed
unchanged.
src/public/locales/it-IT.json (1)

2792-2794: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

在可信作者徽章中说明 HTML 权限。

Line 2794 只写允许 Markdown 渲染,但该信任状态同时允许危险 HTML。此文本会低估实际权限范围。改为明确包含 Markdown/HTML

建议修改
- "title": "Il rendering Markdown di questo autore è consentito"
+ "title": "Il rendering Markdown/HTML di questo autore è consentito"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/it-IT.json` around lines 2792 - 2794, 更新意大利语本地化中的
trustedAuthorBadge.title,将仅说明允许 Markdown 渲染的文案改为明确说明允许 Markdown/HTML
渲染;保持徽章文本及其他本地化内容不变。
src/public/parts/shells/chat/test/manifest.json (1)

698-707: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

messageSurface.mjs 加入 attachments 触发器。

本 PR 修改了 src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs,但当前 attachments 触发器没有覆盖该文件。修改 messageSurface.mjs 时,attachments.spec.mjs 不会被命中,附件回归可能绕过定向测试。

依据 PR 的附件与媒体文件清单,messageSurface.mjs 属于附件渲染链路。

建议修复
 						"src/public/parts/shells/chat/public/hub/messages/render/**",
+						"src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs",
 						"src/public/parts/shells/chat/public/src/composerAttachments.mjs",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/test/manifest.json` around lines 698 - 707, 在
attachments 的 triggers 列表中加入 messageSurface.mjs,使其覆盖
src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs
的变更;保持现有附件触发器配置不变。
src/public/locales/nl-NL.json (2)

2477-2479: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

替换错误的 subthread 无障碍名称。

kinder draad 是字面直译,不是有效的领域术语。使用已有文案中的 Onderliggende thread,否则屏幕阅读器会读出无意义的标签。

建议修改
-					"aria-label": "kinder draad"
+					"aria-label": "Onderliggende thread"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/nl-NL.json` around lines 2477 - 2479, Update the
breadcrumb aria-label in the locale entry to use the established “Onderliggende
thread” wording instead of the incorrect subthread translation, preserving the
existing accessibility-label structure.

1500-1504: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

修正权限提示中的荷兰语语法错误。

Je heeft 不是正确表达。改为 Je hebt,否则 MANAGE_CHANNELS 权限错误提示会直接显示语法错误。

建议修改
-						"denied": "Je heeft geen MANAGE_CHANNELS-rechten om overschrijvingen van kanaalrechten te bewerken.",
+						"denied": "Je hebt geen MANAGE_CHANNELS-rechten om overschrijvingen van kanaalrechten te bewerken.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/nl-NL.json` around lines 1500 - 1504, In the Dutch locale
entry "denied", replace the grammatically incorrect "Je heeft" with "Je hebt"
while preserving the rest of the MANAGE_CHANNELS permission message unchanged.
src/public/locales/pt-PT.json (2)

2790-2790: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

改用自然的“已置顶消息”译文。

Line 2790 的 Ainda sem pins 混用英文术语,且不符合自然的葡萄牙语界面文案。改为 Ainda não há mensagens fixadas

建议修改
-				"pins": "Ainda sem pins"
+				"pins": "Ainda não há mensagens fixadas"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/pt-PT.json` at line 2790, Update the “pins” translation in
the Portuguese locale to use the natural wording “Ainda não há mensagens
fixadas” instead of the mixed-language “Ainda sem pins”.

1500-1500: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

修正权限提示的葡萄牙语语法。

Line 1500 的 Pode não tem permissão 语法错误。改为直接表达权限拒绝状态。

建议修改
-						"denied": "Pode não tem permissão de MANAGE_CHANNELS para editar substituições de permissão de canal.",
+						"denied": "Não tem permissão de MANAGE_CHANNELS para editar substituições de permissão de canal.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/pt-PT.json` at line 1500, Update the denied translation in
pt-PT.json to correct the Portuguese grammar by directly expressing that the
user lacks MANAGE_CHANNELS permission to edit channel permission overrides,
replacing the invalid “Pode não tem permissão” wording.
src/public/locales/ru-RU.json (1)

2790-2790: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

改用自然的俄语置顶消息文案。

Line 2790 的 Пин пока нет 是不自然的混合表达。改为 Закреплений пока нет

建议修改
-				"pins": "Пин пока нет"
+				"pins": "Закреплений пока нет"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/ru-RU.json` at line 2790, Update the “pins” locale entry
in ru-RU.json from the unnatural “Пин пока нет” wording to “Закреплений пока
нет”, leaving the surrounding translations unchanged.
src/public/locales/vi-VN.json (1)

2792-2794: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

修正可信作者徽章文案。

"Đã tin" 表达不完整,用户无法明确理解为“已信任”。改为 "Đã tin cậy"

建议修改
-				"textContent": "Đã tin",
+				"textContent": "Đã tin cậy",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/vi-VN.json` around lines 2792 - 2794, Update the
textContent value in the trustedAuthorBadge locale entry from “Đã tin” to “Đã
tin cậy”, leaving the title unchanged.
src/public/pages/scripts/components/mediaViewer.mjs (1)

204-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

不要劫持视频控件的方向键。

视频获得焦点后,ArrowLeftArrowRight 应控制视频播放器。当前文档级监听器会阻止默认行为并切换媒体项。

当事件目标在媒体控件内时,不处理方向键。保留 Escape 的全局关闭行为。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/pages/scripts/components/mediaViewer.mjs` around lines 204 - 216,
Update the onKey handler so ArrowLeft and ArrowRight are ignored when the event
target is within the video media controls, allowing the player’s native seeking
behavior. Keep the existing step behavior for other targets and preserve
Escape’s global close handling.
src/public/parts/shells/chat/src/group/queries.mjs (1)

251-260: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

内联一次性 branchLines

Line 254 的 branchLines 只传给 Line 259。将条件表达式直接传入 markStaleGeneratingMessages

建议修改
-	const branchLines = branchFiltered
-		? lines
-		: await filterLinesToDisplayBranch(username, groupId, state, lines)
 	const streamGeneratingIdleMs = Number(state.groupSettings?.streamGeneratingIdleMs)
 	let work = markStaleGeneratingMessages(
-		branchLines,
+		branchFiltered
+			? lines
+			: await filterLinesToDisplayBranch(username, groupId, state, lines),
 		Number.isFinite(streamGeneratingIdleMs) && streamGeneratingIdleMs > 0 ? streamGeneratingIdleMs : undefined,
 	)

As per coding guidelines, “Avoid one-off temporary bindings”. As per path instructions, “禁止只用一次的临时绑定,内联到使用处。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/group/queries.mjs` around lines 251 - 260, 在
finalizeChannelMessagesForViewer 中移除只使用一次的 branchLines 临时变量,将 branchFiltered
条件表达式直接作为 markStaleGeneratingMessages 的第一个参数传入;保持现有分支筛选逻辑不变。

Sources: Coding guidelines, Path instructions

src/public/parts/shells/chat/test/frontend/composer.spec.mjs (1)

12-17: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

使用大于上限的图片验证预览尺寸。

当前测试数据是 1×1 PNG。即使 72 像素上限失效,图片宽高也会自然满足 <= 72。同样,过小内容无法证明附件宽度受 180 限制。

请改用至少 128×128 的 PNG,再保留当前断言。

Also applies to: 61-88

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/test/frontend/composer.spec.mjs` around lines 12
- 17, Replace the 1×1 image represented by TINY_PNG_BUFFER with a valid PNG
whose dimensions are at least 128×128, while preserving the existing
preview-size assertions in the affected tests so they verify the 72-pixel and
180-pixel limits.
src/public/parts/shells/chat/public/hub/composerDraft.mjs (1)

118-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

移除无效的 async

applyDraft 只访问 DOM 和 localStorage。它不执行异步操作。将它改为同步函数,并在 loadDraft 中直接调用;只等待 restoreDraftFiles

As per coding guidelines: “Use async/await only for asynchronous control flow.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/hub/composerDraft.mjs` around lines 118 -
144, Remove the unnecessary async modifier from applyDraft and update loadDraft
to call it synchronously without awaiting it. Keep await only for the
asynchronous restoreDraftFiles operation, preserving the existing draft
application behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/public/locales/es-ES.json`:
- Around line 2799-2801: Update the unreadBadge.aria-label translation to use
the project’s existing pluralization mechanism, providing singular and plural
Spanish labels with the noun included: “1 mensaje sin leer” for one and
“${count} mensajes sin leer” otherwise.

In `@src/public/locales/fr-FR.json`:
- Around line 575-576: Update the Telegram button translations so both title and
aria-label use the action text "Ouvrir Telegram", preserving the brand name
Telegram rather than translating it.

In `@src/public/locales/hi-IN.json`:
- Line 2794: Update the `chat.hub.trustedAuthorBadge.title` translation in
`hi-IN.json` to use the complete Hindi imperative phrase ending with “की अनुमति
दें”, matching `chat.hub.trustAuthor.title` while preserving the existing title
meaning.

In `@src/public/locales/it-IT.json`:
- Around line 4802-4804: Update the Italian breadcrumb aria-label in the
"breadcrumb" locale entry from the food-related translation to "Percorso di
navigazione".
- Around line 575-576: Update the title and aria-label values in the Italian
locale entry to use the product name “Telegram”: replace both “Apri Telegramma”
strings with “Apri Telegram”.

In `@src/public/locales/nl-NL.json`:
- Around line 1754-1759: Update the filtersLabel aria-label in the panel
localization to “Berichten in Postvak IN filteren”, replacing the mixed “inbox”
terminology while leaving the panel label unchanged.
- Around line 4802-4804: Update the breadcrumb aria-label in the nl-NL locale
from “Paneermeel” to “Broodkruimelpad” so the Cabinet breadcrumb has the correct
Dutch accessibility name.

In `@src/public/locales/pt-PT.json`:
- Around line 4802-4804: Update the breadcrumb aria-label in
src/public/locales/pt-PT.json lines 4802-4804 from “Pão ralado” to “Navegação
estrutural”, and update the corresponding label in src/public/locales/ru-RU.json
lines 4802-4804 from “Панировочные сухари” to “Навигация по разделам”.
- Around line 575-576: Update the “title” and “aria-label” translations in the
Portuguese locale to use the branded application name “Telegram” instead of the
translated noun “telegrama”.

In `@src/public/locales/uk-UA.json`:
- Around line 2793-2794: Update the trusted-author badge’s title alongside the
"Довірено" textContent to use a trusted-state description rather than an action
prompt, indicating that this author is trusted and unsafe Markdown/HTML is
allowed.
- Around line 4802-4804: Update the breadcrumb aria-label value in the locale’s
"breadcrumb" entry from the food-related term to the standard Ukrainian
navigation term "Навігаційна стежка", preserving the existing localization
structure.

In `@src/public/locales/vi-VN.json`:
- Around line 4802-4804: Update the breadcrumb aria-label in the locale’s
"breadcrumb" entry from the literal crumb translation to the Vietnamese
navigation name "Đường dẫn điều hướng".

In `@src/public/locales/zh-TW.json`:
- Around line 573-575: 在 zh-TW.json 的 telegram 配置中,将 title 和 aria-label 的“打開
Telegram”统一改为“開啟 Telegram”,保持两处文案一致。

In `@src/public/pages/scripts/components/mediaViewer.mjs`:
- Around line 40-65: 将 createViewerRoot() 中手写的对话框骨架迁移到共享对话框模板,改用
openDialogFromTemplate 或原生 dialog 创建查看器。模板必须包含 modal-box 和
modal-backdrop,并保留媒体查看器所需的控件与本地化逻辑。确保打开查看器后启用焦点约束,Tab 不可访问遮罩后的页面内容。

In `@src/public/parts/shells/cabinet/src/shared/sync.mjs`:
- Line 80: Update the part_cabinet_operation_put inbound handling around
parsePartpath so a missing or invalid message.partpath is discarded immediately
when parsePartpath returns null. Remove the fallback to
getShellPartpath('cabinet') and preserve processing only for valid remote part
paths.

In `@src/public/parts/shells/chat/public/hub/entityProfile.mjs`:
- Around line 131-138: 在 normHex 函数的 JSDoc 中补充一行中文摘要,放在 `@param`
标签之前;保持现有参数、返回值说明及实现不变。

In `@src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs`:
- Around line 158-175: Update the file-buffer restoration logic around the
byNameMime map so duplicate name/mime keys preserve all buffers in insertion
order instead of overwriting earlier entries. Store each buffer in a queue and
consume one matching buffer per file during nextFiles mapping, while retaining
fileId-based matching and existing behavior for files that already have buffers.

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs`:
- Around line 28-30: Update the content assignment in the message-send flow to
remove the typeof/object conditional and always clone event.content with object
spread. Preserve the existing event.content source while eliminating the string
fallback and defensive type check.

In `@src/public/parts/shells/chat/public/hub/sendQueue.mjs`:
- Around line 31-44: 在数据库打开成功的处理逻辑中,为返回的数据库实例添加 versionchange 监听;触发时调用
database.close() 并将 dbOpenPromise 清空,以便后续 schema 升级不会被旧连接阻塞。修改 indexedDB.open 的
success 处理流程,保留现有成功返回行为。

In `@src/public/parts/shells/chat/public/hub/wiring/composerEvents.mjs`:
- Around line 17-22: Remove the one-use dropRoot and channel temporary bindings
in the addMessageAreaFileDrop setup. Inline document.querySelector('.main-body')
as the first argument and read the current channel’s name directly in the
callback’s return expression, preserving the existing fallback order.

In `@src/public/parts/shells/chat/public/src/composerAttachments.mjs`:
- Around line 159-175: 拆分
renderAttachmentPreview,保留其仅负责模板参数组装、预览路径选择和附件节点组装;将本地媒体预览、群文件图片预览,以及编辑、转写、删除等操作绑定分别提取为职责单一的本地函数,并让
renderAttachmentPreview 调用这些函数,保持现有预览和交互行为不变。

In `@src/public/parts/shells/chat/src/chat/channel/postMessage.mjs`:
- Around line 144-147: 为该 JSDoc 块补充一行中文摘要,描述其用于规范化附件 wire 描述符;保留现有 `@param` 和
`@returns` 标签,确保对应方法的注释不再只有标签而缺少摘要。

In `@src/public/parts/shells/chat/src/chat/dag/knownLocalEventIds.mjs`:
- Around line 41-44: 在处理 checkpoint.hot_posts.latestByChannel 的循环中,删除只使用一次的临时变量
list,并将其数组归一化表达式直接内联到内层 for...of 循环中;保持 addKnownEventId(knownSet, id) 的现有行为不变。
- Around line 17-32: 为 addKnownEventId 和 addCheckpointKnownEventIds 各补充一行中文
JSDoc 摘要,分别概括其写入规范化事件 ID 以及从群快照添加已知事件 ID 的行为;保留现有参数和返回值标签不变。

In `@src/public/parts/shells/chat/src/group/queries.mjs`:
- Around line 214-218: 简化 branchFilterActive:移除 tip 和 tips 这两个一次性临时变量及
Array.isArray 防御性检查,直接读取本地物化状态的 dagTips 字段并保留现有分支过滤判断行为。

In `@src/public/parts/shells/chat/test/frontend/composer.spec.mjs`:
- Around line 106-112: Rename the callback parameter in the scroll.evaluate call
from el to element, and update its scrollTop references accordingly while
preserving the existing scroll-position behavior.

In `@src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs`:
- Around line 20-30: 更新 mailbox summary 集成测试,使其通过已认证的 HTTP 请求调用
registerMailboxRoutes 注册的 mailbox/summary 路由,而不是直接调用
countMailboxPendingForRecipient。使用目标 recipient 的公钥哈希预置一条待处理 mailbox
记录,并断言路由响应中的待处理数量精确为 1;保留对 activePubKeyHex 转换的验证,确保测试覆盖生产路由传递 Uint8Array 的实际调用链。

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`:
- Around line 21-24: 更新静态依赖扫描循环,使其跨行识别完整的 import/export 模块声明,而不是逐行检查;确保多行
node-datachannel 或 whip 声明会被检测到,同时保持动态 import 的处理逻辑不变。为该扫描器补充覆盖多行声明的回归夹具,并验证
Termux 回归测试不会误通过。

In `@src/public/parts/shells/chat/test/pure/run_uri.test.mjs`:
- Around line 33-36: Update the parseJoinRunUri tests to assert the returned
parsed value before reading its fields, then use direct property assertions so
null results cannot pass through optional chaining. For one-time URI inputs,
inline the formatJoinRunUri(...) call or URI literal directly into
parseJoinRunUri(...) at both affected test cases instead of storing them in uri
variables.

---

Outside diff comments:
In `@src/public/locales/emoji.json`:
- Around line 1944-1949: Replace the newly added Chinese values for
messageRenderFailed and cabinet.breadcrumb.aria-label in the emoji locale with
pure-emoji equivalents, following nearby entries such as markdownRenderFailed
and thread.breadcrumb; leave all other locale values unchanged.

In `@src/public/locales/is-IS.json`:
- Around line 1943-1948: Update the messageRenderFailed translation to use
distinct Icelandic wording that clearly means the message cannot be displayed,
while leaving markdownRenderFailed unchanged.

In `@src/public/locales/it-IT.json`:
- Around line 2792-2794: 更新意大利语本地化中的 trustedAuthorBadge.title,将仅说明允许 Markdown
渲染的文案改为明确说明允许 Markdown/HTML 渲染;保持徽章文本及其他本地化内容不变。

In `@src/public/locales/nl-NL.json`:
- Around line 2477-2479: Update the breadcrumb aria-label in the locale entry to
use the established “Onderliggende thread” wording instead of the incorrect
subthread translation, preserving the existing accessibility-label structure.
- Around line 1500-1504: In the Dutch locale entry "denied", replace the
grammatically incorrect "Je heeft" with "Je hebt" while preserving the rest of
the MANAGE_CHANNELS permission message unchanged.

In `@src/public/locales/pt-PT.json`:
- Line 2790: Update the “pins” translation in the Portuguese locale to use the
natural wording “Ainda não há mensagens fixadas” instead of the mixed-language
“Ainda sem pins”.
- Line 1500: Update the denied translation in pt-PT.json to correct the
Portuguese grammar by directly expressing that the user lacks MANAGE_CHANNELS
permission to edit channel permission overrides, replacing the invalid “Pode não
tem permissão” wording.

In `@src/public/locales/ru-RU.json`:
- Line 2790: Update the “pins” locale entry in ru-RU.json from the unnatural
“Пин пока нет” wording to “Закреплений пока нет”, leaving the surrounding
translations unchanged.

In `@src/public/locales/vi-VN.json`:
- Around line 2792-2794: Update the textContent value in the trustedAuthorBadge
locale entry from “Đã tin” to “Đã tin cậy”, leaving the title unchanged.

In `@src/public/pages/scripts/components/mediaViewer.mjs`:
- Around line 204-216: Update the onKey handler so ArrowLeft and ArrowRight are
ignored when the event target is within the video media controls, allowing the
player’s native seeking behavior. Keep the existing step behavior for other
targets and preserve Escape’s global close handling.

In `@src/public/parts/shells/chat/public/hub/composerDraft.mjs`:
- Around line 118-144: Remove the unnecessary async modifier from applyDraft and
update loadDraft to call it synchronously without awaiting it. Keep await only
for the asynchronous restoreDraftFiles operation, preserving the existing draft
application behavior.

In `@src/public/parts/shells/chat/src/group/queries.mjs`:
- Around line 251-260: 在 finalizeChannelMessagesForViewer 中移除只使用一次的 branchLines
临时变量,将 branchFiltered 条件表达式直接作为 markStaleGeneratingMessages
的第一个参数传入;保持现有分支筛选逻辑不变。

In `@src/public/parts/shells/chat/src/group/routes/channelMessages.mjs`:
- Around line 123-135: Reorder the edit flow so findChannelMessageRow and the
edit-authorization hook complete successfully before processing attachments.
Move the attachFilesToContent block that handles processedFiles and existing
contentObj.files after those validations, ensuring rejected or missing-message
edits never upload files.

In `@src/public/parts/shells/chat/test/frontend/composer.spec.mjs`:
- Around line 12-17: Replace the 1×1 image represented by TINY_PNG_BUFFER with a
valid PNG whose dimensions are at least 128×128, while preserving the existing
preview-size assertions in the affected tests so they verify the 72-pixel and
180-pixel limits.

In `@src/public/parts/shells/chat/test/manifest.json`:
- Around line 698-707: 在 attachments 的 triggers 列表中加入 messageSurface.mjs,使其覆盖
src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs
的变更;保持现有附件触发器配置不变。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2c64bd0f-4c17-4af6-9fbb-331b5bcbd0bb

📥 Commits

Reviewing files that changed from the base of the PR and between 2f5ab0a and 14b6027.

📒 Files selected for processing (133)
  • .github/pages/wait/install/index.html
  • deno.json
  • src/decl/locale_data.ts
  • src/public/locales/ar-SA.json
  • src/public/locales/de-DE.json
  • src/public/locales/emoji.json
  • src/public/locales/en-UK.json
  • src/public/locales/es-ES.json
  • src/public/locales/fr-FR.json
  • src/public/locales/hi-IN.json
  • src/public/locales/is-IS.json
  • src/public/locales/it-IT.json
  • src/public/locales/ja-JP.json
  • src/public/locales/ko-KR.json
  • src/public/locales/lzh.json
  • src/public/locales/nl-NL.json
  • src/public/locales/pt-PT.json
  • src/public/locales/ru-RU.json
  • src/public/locales/uk-UA.json
  • src/public/locales/vi-VN.json
  • src/public/locales/zh-CN.json
  • src/public/locales/zh-TW.json
  • src/public/pages/scripts/components/emojiPicker.mjs
  • src/public/pages/scripts/components/mediaViewer.mjs
  • src/public/pages/scripts/lib/formatBytes.mjs
  • src/public/parts/serviceGenerators/AI/proxy/test/integration/prompt_cache_rate.test.mjs
  • src/public/parts/serviceGenerators/AI/proxy/test/pure/openai_prompt_cache_mock.test.mjs
  • src/public/parts/shells/cabinet/public/index.html
  • src/public/parts/shells/cabinet/src/publish.mjs
  • src/public/parts/shells/cabinet/src/shared/blobs.mjs
  • src/public/parts/shells/cabinet/src/shared/sync.mjs
  • src/public/parts/shells/chat/public/hub/AGENTS.md
  • src/public/parts/shells/chat/public/hub/composerDraft.mjs
  • src/public/parts/shells/chat/public/hub/core/domUtils.mjs
  • src/public/parts/shells/chat/public/hub/entityProfile.mjs
  • src/public/parts/shells/chat/public/hub/hubContact.mjs
  • src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageActionsUi.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSend.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSurface.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/file.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/index.mjs
  • src/public/parts/shells/chat/public/hub/sendQueue.mjs
  • src/public/parts/shells/chat/public/hub/sidebar/selectChannel.mjs
  • src/public/parts/shells/chat/public/hub/wiring/composerEvents.mjs
  • src/public/parts/shells/chat/public/hub/wiring/fileEvents.mjs
  • src/public/parts/shells/chat/public/shared/branchMessageFilter.mjs
  • src/public/parts/shells/chat/public/shared/channelContent.mjs
  • src/public/parts/shells/chat/public/shared/friendBinding.mjs
  • src/public/parts/shells/chat/public/src/composerAttachments.mjs
  • src/public/parts/shells/chat/public/src/endpoints/groupChannel.mjs
  • src/public/parts/shells/chat/public/src/groupSettings/channelPermsTab.mjs
  • src/public/parts/shells/chat/public/src/templates/attachment_preview.html
  • src/public/parts/shells/chat/public/src/templates/hub/inbox/panel.html
  • src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs
  • src/public/parts/shells/chat/src/chat/archive/monthChunks.mjs
  • src/public/parts/shells/chat/src/chat/channel/postMessage.mjs
  • src/public/parts/shells/chat/src/chat/channel_keys/store.mjs
  • src/public/parts/shells/chat/src/chat/chunkProviders.mjs
  • src/public/parts/shells/chat/src/chat/dag/checkpointPayload.mjs
  • src/public/parts/shells/chat/src/chat/dag/knownLocalEventIds.mjs
  • src/public/parts/shells/chat/src/chat/federation/archiveHandshake.mjs
  • src/public/parts/shells/chat/src/chat/federation/archiveMonthWire.mjs
  • src/public/parts/shells/chat/src/chat/federation/bootstrap/wire.mjs
  • src/public/parts/shells/chat/src/chat/federation/charRpc.mjs
  • src/public/parts/shells/chat/src/chat/federation/chunks.mjs
  • src/public/parts/shells/chat/src/chat/federation/groupCardFederation.mjs
  • src/public/parts/shells/chat/src/chat/federation/groupEmojiFederation.mjs
  • src/public/parts/shells/chat/src/chat/federation/index.mjs
  • src/public/parts/shells/chat/src/chat/federation/pullEnvelope.mjs
  • src/public/parts/shells/chat/src/chat/federation/roomHandlers/identity.mjs
  • src/public/parts/shells/chat/src/chat/federation/roomHandlers/rpc.mjs
  • src/public/parts/shells/chat/src/chat/federation/volatile.mjs
  • src/public/parts/shells/chat/src/chat/federation/wireSchemas.mjs
  • src/public/parts/shells/chat/src/chat/files/groupFiles.mjs
  • src/public/parts/shells/chat/src/chat/manifestAcl.mjs
  • src/public/parts/shells/chat/src/chat/whip/sdp.mjs
  • src/public/parts/shells/chat/src/emojiPacks/discoverNetwork.mjs
  • src/public/parts/shells/chat/src/emojiPacks/packStore.mjs
  • src/public/parts/shells/chat/src/endpoints/mailbox.mjs
  • src/public/parts/shells/chat/src/entity/entitySearch.mjs
  • src/public/parts/shells/chat/src/entity/filesEndpoints.mjs
  • src/public/parts/shells/chat/src/entity/ownerProfileUpdate.mjs
  • src/public/parts/shells/chat/src/entity/profile.mjs
  • src/public/parts/shells/chat/src/group/emojiContentResolve.mjs
  • src/public/parts/shells/chat/src/group/queries.mjs
  • src/public/parts/shells/chat/src/group/routes/channelMessages.mjs
  • src/public/parts/shells/chat/src/group/routes/channelStreaming.mjs
  • src/public/parts/shells/chat/src/group/routes/groups.mjs
  • src/public/parts/shells/chat/test/frontend/attachments.spec.mjs
  • src/public/parts/shells/chat/test/frontend/composer.spec.mjs
  • src/public/parts/shells/chat/test/integration/emoji_content_resolve.test.mjs
  • src/public/parts/shells/chat/test/integration/entity_search.test.mjs
  • src/public/parts/shells/chat/test/integration/friend_char_group.test.mjs
  • src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs
  • src/public/parts/shells/chat/test/integration/message_attachments.test.mjs
  • src/public/parts/shells/chat/test/integration/owner_profile_update.test.mjs
  • src/public/parts/shells/chat/test/integration/remote_profile_fetch_timeout.test.mjs
  • src/public/parts/shells/chat/test/integration/validate_ingest_pendable.test.mjs
  • src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs
  • src/public/parts/shells/chat/test/manifest.json
  • src/public/parts/shells/chat/test/pure/branch_message_filter.test.mjs
  • src/public/parts/shells/chat/test/pure/format_bytes.test.mjs
  • src/public/parts/shells/chat/test/pure/memoize_promise.test.mjs
  • src/public/parts/shells/chat/test/pure/run_uri.test.mjs
  • src/public/parts/shells/social/public/src/templates/explore_account.html
  • src/public/parts/shells/social/public/src/templates/explore_post.html
  • src/public/parts/shells/social/public/src/templates/post_card.html
  • src/public/parts/shells/social/public/src/views/replies.mjs
  • src/public/parts/shells/social/src/discover/postDiscover.mjs
  • src/public/parts/shells/social/src/emojiPacks.mjs
  • src/public/parts/shells/social/src/emojiPacks/discoverNetwork.mjs
  • src/public/parts/shells/social/src/federation/partQuery.mjs
  • src/public/parts/shells/social/src/federation/rpc/wire.mjs
  • src/public/parts/shells/social/src/live/network.mjs
  • src/public/parts/shells/social/src/manifestAcl.mjs
  • src/public/parts/shells/social/src/search/network.mjs
  • src/public/parts/shells/social/src/timeline/fanout.mjs
  • src/public/parts/shells/social/src/trending/network.mjs
  • src/public/parts/shells/social/src/vaultAcl.mjs
  • src/public/parts/shells/social/src/vault_crypto/followApproveImport.mjs
  • src/public/parts/shells/subfounts/src/link_room.mjs
  • src/scripts/test/AGENTS.md
  • src/scripts/test/core/state.mjs
  • src/scripts/test/docs/continue-report.md
  • src/scripts/test/docs/resource-scheduling.md
  • src/scripts/test/fixtures/openai_prompt_cache_mock.mjs
  • src/scripts/test/fixtures/serviceSources/AI/proxy_openai_mock/main.mjs
  • src/scripts/test/live/federation/common.mjs
  • src/scripts/test/runner/index.mjs
  • src/scripts/test/runner/run_command.mjs
  • src/scripts/test/selftest/runner_watchdog.test.mjs
  • src/server/p2p_server/inbound_handlers.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • steve02081504/fount-p2p (manual)
💤 Files with no reviewable changes (1)
  • src/public/locales/zh-CN.json

Comment on lines +2799 to 2801
"unreadBadge": {
"aria-label": "${count} sin leer"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

未读数量文案仍缺少单复数处理。

unreadBadge.aria-label 目前是 "${count} sin leer",当 count 为 1 时读作「1 sin leer」,不符合西班牙语单复数语法要求(应为「1 sin leer」对应单数标签、其余用复数标签,且缺失名词导致语义不完整,如 "1 mensaje sin leer" / "${count} mensajes sin leer")。请使用项目已有的复数机制补齐单复数两种文案。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/es-ES.json` around lines 2799 - 2801, Update the
unreadBadge.aria-label translation to use the project’s existing pluralization
mechanism, providing singular and plural Spanish labels with the noun included:
“1 mensaje sin leer” for one and “${count} mensajes sin leer” otherwise.

Comment thread src/public/locales/fr-FR.json Outdated
Comment on lines +575 to +576
"title": "Télégramme ouvert",
"aria-label": "Télégramme ouvert"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

将 Telegram 按钮改为动作文案。

"Télégramme ouvert" 表示“电报已打开”,不是打开 Telegram 的操作。品牌名也不应翻译成 Télégramme。将 titlearia-label 统一为 "Ouvrir Telegram"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/fr-FR.json` around lines 575 - 576, Update the Telegram
button translations so both title and aria-label use the action text "Ouvrir
Telegram", preserving the brand name Telegram rather than translating it.

Comment thread src/public/locales/hi-IN.json Outdated
},
"trustedAuthorBadge": {
"textContent": "विश्वसनीय",
"title": "इस लेखक पर भरोसा करें (खतरनाक Markdown/HTML की अनुमति)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

补全可信作者徽标的标题。

Line 2794 的 chat.hub.trustedAuthorBadge.title 以“की अनुमति”结束,语义不完整。对应的 chat.hub.trustAuthor.title 已使用“की अनुमति दें”。请统一为完整的祈使句。

建议修改
-				"title": "इस लेखक पर भरोसा करें (खतरनाक Markdown/HTML की अनुमति)"
+				"title": "इस लेखक पर भरोसा करें (खतरनाक Markdown/HTML की अनुमति दें)"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"title": "इस लेखक पर भरोसा करें (खतरनाक Markdown/HTML की अनुमति)"
"title": "इस लेखक पर भरोसा करें (खतरनाक Markdown/HTML की अनुमति दें)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/hi-IN.json` at line 2794, Update the
`chat.hub.trustedAuthorBadge.title` translation in `hi-IN.json` to use the
complete Hindi imperative phrase ending with “की अनुमति दें”, matching
`chat.hub.trustAuthor.title` while preserving the existing title meaning.

Comment thread src/public/locales/it-IT.json Outdated
Comment on lines +575 to +576
"title": "Apri Telegramma",
"aria-label": "Apri Telegramma"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

保留产品名 Telegram

Line 575 和 Line 576 使用了 "Apri Telegramma"Telegramma 不是应用名称,会产生错误语义。将两个值改为 "Apri Telegram"

建议修改
- "title": "Apri Telegramma",
- "aria-label": "Apri Telegramma"
+ "title": "Apri Telegram",
+ "aria-label": "Apri Telegram"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"title": "Apri Telegramma",
"aria-label": "Apri Telegramma"
"title": "Apri Telegram",
"aria-label": "Apri Telegram"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/it-IT.json` around lines 575 - 576, Update the title and
aria-label values in the Italian locale entry to use the product name
“Telegram”: replace both “Apri Telegramma” strings with “Apri Telegram”.

Comment on lines +4802 to +4804
"breadcrumb": {
"aria-label": "Pangrattato"
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

修正文件柜面包屑的意大利语标签。

Line 4803 的 "Pangrattato" 表示食物“面包屑”,不是导航面包屑。改为 "Percorso di navigazione"

建议修改
- "aria-label": "Pangrattato"
+ "aria-label": "Percorso di navigazione"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"breadcrumb": {
"aria-label": "Pangrattato"
},
"breadcrumb": {
"aria-label": "Percorso di navigazione"
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/it-IT.json` around lines 4802 - 4804, Update the Italian
breadcrumb aria-label in the "breadcrumb" locale entry from the food-related
translation to "Percorso di navigazione".

Comment thread src/public/parts/shells/chat/src/group/queries.mjs Outdated
Comment thread src/public/parts/shells/chat/test/frontend/composer.spec.mjs
Comment thread src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs Outdated
Comment thread src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs Outdated
Comment thread src/public/parts/shells/chat/test/pure/run_uri.test.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/public/locales/lzh.json (2)

2907-2909: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

把下载提示改成明确动词。

Line [2907] 的 title 为“取”,语义不完整;同一控件的 aria-label 已使用“拓存”。请统一为下载语义。

建议修改
- "title": "取",
+ "title": "拓存",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/lzh.json` around lines 2907 - 2909, 将 lzh.json 中该控件的 title
从“取”更新为与 aria-label 一致的明确下载语义“拓存”,保持其他本地化字段不变。

2792-2794: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

让可信作者徽章明确危险渲染范围。

Line [2794] 只写“將盡數渲染其文”,没有说明信任会允许危险 Markdown/HTML。请与 chat.hub.trustAuthor.title 的安全提示保持一致。

建议修改
- "title": "已信任此作者,將盡數渲染其文"
+ "title": "已信任此作者(聽許危險之 Markdown/HTML 渲染)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/lzh.json` around lines 2792 - 2794, 更新 lzh.json 中
trustedAuthorBadge.title,使其明确说明信任作者会允许渲染危险的 Markdown/HTML 内容,并与
chat.hub.trustAuthor.title 的安全提示保持一致;不要修改 trustedAuthorBadge.textContent。
src/public/parts/shells/chat/public/hub/messages/messageSend.mjs (1)

45-68: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

把文件大小从选择阶段传入 pending 附件。

pendingRowFromComposer 现在写入 size,但 handleFilesSelectsrc/public/parts/shells/chat/public/src/composerAttachments.mjs Line 89-123 创建的 newFile 没有 size。因此乐观消息和失败重试 payload 都没有真实附件大小,新的 formatBytes 路径无法在服务端确认前显示正确值。

在文件选择阶段写入原始 File.size,不要在 pending 构造处填充猜测值。

建议修改
 const newFile = {
 	name: file.name,
 	mime_type: file.type,
+	size: file.size,
 	buffer: arrayBufferToBase64(await file.arrayBuffer()),
 	description: '',
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs` around
lines 45 - 68, Update handleFilesSelect to include the original File.size when
constructing each newFile object. In pendingRowFromComposer, use the provided
file.size directly for the pending attachment and remove the base64-length
fallback so optimistic messages and retry payloads preserve the real
selected-file size.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Line 23: Update the I18n guidance in AGENTS.md to state that zh-CN is the
default locale edited directly, while allowing an individual non-zh-CN locale to
be corrected manually after synchronization. Preserve the existing generator
workflow and reference to locale-edits.md.

In `@src/public/locales/docs/locale-edits.md`:
- Line 12: Rewrite the “Shared-path value kinds” guidance to separate pre-sync
normalization from the post-sync contract: state that update-locales.py may
normalize string and single DOM applicator objects during synchronization, but
completed shared paths must have matching types and remaining mismatches cause
exit 1. Explicitly preserve string↔switch forms without restructuring switch
cases.

In `@src/public/locales/lzh.json`:
- Around line 4802-4804: Update the breadcrumb aria-label in the lzh locale to
replace the incorrect “麵屑導航” text with the requested Traditional Chinese wording
“麵包屑導覽”.
- Line 1948: 更新 lzh.json 中 chat.hub.messageRenderFailed 对应的翻译,使其明确表示消息显示失败,并与
chat.hub.markdownRenderFailed 的 Markdown 渲染失败文案区分开;保留两个 key 的错误范围语义,避免继续使用相同文本。

In `@src/public/parts/shells/chat/public/hub/core/domUtils.mjs`:
- Around line 33-34: Update normalizeCharPartName to require a string input and
call trim().toLowerCase() directly, removing the fallback coercion that maps
nullish, boolean, or numeric values to an empty cache key. Preserve immediate
failure for invalid upstream values.

In `@src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs`:
- Around line 161-176: Update the file-matching merge logic around the prevFiles
indexing and nextFiles mapping so each byId entry also records its corresponding
byNameMime key. When a fileId match is consumed, remove the same buffer from
that name/MIME queue before returning it, while preserving existing fallback
matching for files without IDs. Add coverage for duplicate attachments where one
server entry has fileId and the other does not, including image or video
content.

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs`:
- Line 28: Remove the single-use content binding in the message-send flow and
inline the object spread directly into the returned object as content: {
...event.content }. Preserve the existing return structure and behavior.

In `@src/public/parts/shells/chat/public/src/composerAttachments.mjs`:
- Around line 281-283: 更新图片预览点击处理器,使其在触发时读取当前 previewImg.src,而不是捕获初始
previewUrl;确保 composerAttachments 中图片编辑后,openMediaViewer 使用新生成且未被撤销的 URL。

In `@src/public/parts/shells/chat/src/api/channel.mjs`:
- Around line 50-53: Update mapFiles so EVFS reference strings in file.buffer
remain unchanged for attachFilesToContent and parseEvfsRef to recognize them.
Only pass actual base64 or binary upload data through asUploadBuffer, preserving
the existing file mapping behavior for all other fields.

In `@src/public/parts/shells/chat/src/group/routes/channelMessages.mjs`:
- Around line 111-123: 调整频道消息编辑流程:在调用 attachFilesToContent 上传或写入附件前,先执行
BeforeUserEdit 和 MessageEdit hooks,并立即处理其 reject 结果;仅当 hooks
允许编辑时才继续附件处理及后续编辑事件写入,确保拒绝编辑不会产生孤立文件。

In `@src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs`:
- Around line 25-26: Rename the abbreviated local variables in the mailbox
summary integration test to descriptive full names: use federationSettings
instead of fed, request instead of req, and response instead of res. Update all
references consistently across the affected test code, preserving behavior.

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`:
- Around line 30-33: 将用于匹配静态模块声明的正则变量 `re` 重命名为
`staticDeclarationPattern`,并同步更新该代码块中对该变量的引用,保持匹配逻辑不变。
- Around line 26-40: Replace the raw-text regex logic in
staticModuleDeclarations with tokenization that skips strings, block comments,
and line comments before recognizing declarations. Identify only
ImportDeclaration and ExportNamedDeclaration forms, including side-effect
imports, so staticNodeDatachannelImports() reports actual dependencies without
matching comment or string contents.

In `@src/scripts/checks/i18n_keys.mjs`:
- Line 121: 将 switch 对象视为结构扫描的叶子:在 scanI18nKeyStructure() 递归遍历值之前跳过
isSwitchValue(value),避免检查其 cases 内的数字分支键。新增覆盖 switchLeaf 的测试,调用
scanI18nKeyStructure 并验证问题列表为空,且 cases 必须包含键 1。

In `@src/scripts/i18n/switch_value.mjs`:
- Around line 4-8: Remove the cross-runtime re-export layer represented by
areLocaleLeafKindsCompatible, isSwitchValue, and resolveSwitchCase in
switch_value.mjs. Move their implementation into a shared module importable
directly by both frontend and backend code, update every consumer to use that
shared module, then delete this file and all remaining re-export paths without
adding compatibility aliases or deprecated wrappers.

---

Outside diff comments:
In `@src/public/locales/lzh.json`:
- Around line 2907-2909: 将 lzh.json 中该控件的 title 从“取”更新为与 aria-label
一致的明确下载语义“拓存”,保持其他本地化字段不变。
- Around line 2792-2794: 更新 lzh.json 中
trustedAuthorBadge.title,使其明确说明信任作者会允许渲染危险的 Markdown/HTML 内容,并与
chat.hub.trustAuthor.title 的安全提示保持一致;不要修改 trustedAuthorBadge.textContent。

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs`:
- Around line 45-68: Update handleFilesSelect to include the original File.size
when constructing each newFile object. In pendingRowFromComposer, use the
provided file.size directly for the pending attachment and remove the
base64-length fallback so optimistic messages and retry payloads preserve the
real selected-file size.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fbfe0dcc-688e-4de2-8410-01819b619aac

📥 Commits

Reviewing files that changed from the base of the PR and between 14b6027 and e1dae32.

📒 Files selected for processing (77)
  • .esh/commands/update-locales.py
  • AGENTS.md
  • deno.json
  • src/public/locales/ar-SA.json
  • src/public/locales/de-DE.json
  • src/public/locales/docs/locale-edits.md
  • src/public/locales/emoji.json
  • src/public/locales/es-ES.json
  • src/public/locales/fr-FR.json
  • src/public/locales/hi-IN.json
  • src/public/locales/is-IS.json
  • src/public/locales/it-IT.json
  • src/public/locales/ja-JP.json
  • src/public/locales/ko-KR.json
  • src/public/locales/lzh.json
  • src/public/locales/nl-NL.json
  • src/public/locales/pt-PT.json
  • src/public/locales/ru-RU.json
  • src/public/locales/uk-UA.json
  • src/public/locales/vi-VN.json
  • src/public/locales/zh-TW.json
  • src/public/pages/AGENTS.md
  • src/public/pages/docs/i18n-notes.md
  • src/public/pages/scripts/components/mediaViewer.mjs
  • src/public/pages/scripts/i18n/index.mjs
  • src/public/pages/scripts/i18n/switch_value.mjs
  • src/public/parts/shells/cabinet/src/shared/sync.mjs
  • src/public/parts/shells/chat/public/hub/composerDraft.mjs
  • src/public/parts/shells/chat/public/hub/composerExtras.mjs
  • src/public/parts/shells/chat/public/hub/composerFiles.mjs
  • src/public/parts/shells/chat/public/hub/core/domUtils.mjs
  • src/public/parts/shells/chat/public/hub/entityProfile.mjs
  • src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageActionsUi.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSend.mjs
  • src/public/parts/shells/chat/public/hub/messages/render/file.mjs
  • src/public/parts/shells/chat/public/hub/sendQueue.mjs
  • src/public/parts/shells/chat/public/hub/wiring/composerEvents.mjs
  • src/public/parts/shells/chat/public/shared/friendBinding.mjs
  • src/public/parts/shells/chat/public/src/composerAttachments.mjs
  • src/public/parts/shells/chat/public/src/groupSettings/channelPermsTab.mjs
  • src/public/parts/shells/chat/public/src/templates/group/settings/channel_perm_row.html
  • src/public/parts/shells/chat/public/src/templates/group/settings/channel_permissions_panel.html
  • src/public/parts/shells/chat/public/src/ui/dragAndDrop.mjs
  • src/public/parts/shells/chat/src/api/channel.mjs
  • src/public/parts/shells/chat/src/chat/channel/postMessage.mjs
  • src/public/parts/shells/chat/src/chat/channel_keys/store.mjs
  • src/public/parts/shells/chat/src/chat/dag/checkpointPayload.mjs
  • src/public/parts/shells/chat/src/chat/dag/knownLocalEventIds.mjs
  • src/public/parts/shells/chat/src/chat/federation/archiveHandshake.mjs
  • src/public/parts/shells/chat/src/chat/federation/archiveMonthPull.mjs
  • src/public/parts/shells/chat/src/chat/federation/chunks.mjs
  • src/public/parts/shells/chat/src/chat/federation/pullEnvelope.mjs
  • src/public/parts/shells/chat/src/chat/federation/roomHandlers/identity.mjs
  • src/public/parts/shells/chat/src/chat/federation/roomHandlers/rpc.mjs
  • src/public/parts/shells/chat/src/chat/federation/userRoomEmojiRegistry.mjs
  • src/public/parts/shells/chat/src/chat/federation/volatile.mjs
  • src/public/parts/shells/chat/src/chat/federation/wireSchemas.mjs
  • src/public/parts/shells/chat/src/entity/filesEndpoints.mjs
  • src/public/parts/shells/chat/src/group/queries.mjs
  • src/public/parts/shells/chat/src/group/routes/channelMessages.mjs
  • src/public/parts/shells/chat/test/frontend/composer.spec.mjs
  • src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs
  • src/public/parts/shells/chat/test/integration/validate_ingest_pendable.test.mjs
  • src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs
  • src/public/parts/shells/chat/test/manifest.json
  • src/public/parts/shells/chat/test/pure/run_uri.test.mjs
  • src/public/parts/shells/social/src/vault_crypto/followApproveImport.mjs
  • src/scripts/checks/AGENTS.md
  • src/scripts/checks/i18n_keys.mjs
  • src/scripts/checks/i18n_refs.mjs
  • src/scripts/checks/test/i18n_keys.test.mjs
  • src/scripts/checks/test/i18n_refs.test.mjs
  • src/scripts/checks/test/manifest.json
  • src/scripts/i18n/bare.mjs
  • src/scripts/i18n/switch_value.mjs
  • src/server/p2p_server/inbound_handlers.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • steve02081504/fount-p2p (manual)

Comment thread AGENTS.md Outdated
Comment thread src/public/locales/docs/locale-edits.md Outdated
Comment thread src/public/locales/lzh.json Outdated
Comment thread src/public/locales/lzh.json
Comment on lines +33 to +34
export function normalizeCharPartName(name) {
return String(name || '').trim().toLowerCase()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major

不要把错误角色名静默折叠为空缓存键。

String(name || '') 会把 nullundefinedfalse0 转成同一个空字符串。缓存写入和读取随后可能共享空键,导致错误角色实体被覆盖或误命中。将函数契约收紧为字符串,直接使用 name.trim().toLowerCase();上游传错值应立即失败。

As per path instructions: “类型盘问与防御性编程是坏味道;垃圾数据直接炸。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/hub/core/domUtils.mjs` around lines 33 -
34, Update normalizeCharPartName to require a string input and call
trim().toLowerCase() directly, removing the fallback coercion that maps nullish,
boolean, or numeric values to an empty cache key. Preserve immediate failure for
invalid upstream values.

Source: Path instructions

Comment thread src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs Outdated
Comment on lines +30 to +33
const re = /\b(?:import|export)\s+[\s\S]*?from\s*['"][^'"]+['"]/g
for (const match of body.matchAll(re)) {
const statement = match[0].replace(/\s+/g, ' ').trim()
if (modulePattern.test(statement)) hits.push(statement)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

re 改为完整语义名称。

re 改为 staticDeclarationPattern。该变量保存静态模块声明的匹配模式。

As per path instructions,“可读全词:禁止 i/msg/ctx/btn/S 等烂名”。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`
around lines 30 - 33, 将用于匹配静态模块声明的正则变量 `re` 重命名为
`staticDeclarationPattern`,并同步更新该代码块中对该变量的引用,保持匹配逻辑不变。

Source: Path instructions

Comment thread src/scripts/checks/i18n_keys.mjs
Comment on lines +4 to +8
export {
areLocaleLeafKindsCompatible,
isSwitchValue,
resolveSwitchCase,
} from '../../public/pages/scripts/i18n/switch_value.mjs'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

移除这个跨运行时重导出层。

后端模块反向依赖 src/public,并只重导出三个符号。将实现移动到前后端都可直接导入的共享模块。随后删除此文件和所有重导出路径。

As per path instructions: “不向后兼容;不做 @deprecated / 重导出;删就删干净。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/i18n/switch_value.mjs` around lines 4 - 8, Remove the
cross-runtime re-export layer represented by areLocaleLeafKindsCompatible,
isSwitchValue, and resolveSwitchCase in switch_value.mjs. Move their
implementation into a shared module importable directly by both frontend and
backend code, update every consumer to use that shared module, then delete this
file and all remaining re-export paths without adding compatibility aliases or
deprecated wrappers.

Source: Path instructions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
src/public/parts/shells/chat/public/shared/friendBinding.mjs (1)

23-29: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

集中好友绑定匹配规则。

当前服务端和 Hub 各自实现了按 entityHashcharname 匹配的逻辑。集中该规则,避免匹配语义漂移,并删除 charKeygfb 等局部缩写。

  • src/public/parts/shells/chat/public/shared/friendBinding.mjs#L23-L29: 导出 friendBindingMatches(candidate, requested)
  • src/public/parts/shells/chat/src/group/routes/groups.mjs#L85-L91: 使用共享匹配函数。
  • src/public/parts/shells/chat/public/hub/friendChat.mjs#L81-L88: 使用共享匹配函数。

As per coding guidelines, “Avoid one-off temporary bindings, poor abbreviations.” As per path instructions, “精简、DRY、SRP。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/shared/friendBinding.mjs` around lines 23
- 29, 集中好友绑定匹配规则:在
src/public/parts/shells/chat/public/shared/friendBinding.mjs#L23-L29 导出
friendBindingMatches(candidate, requested),统一按 entityHash 或 charname 匹配并避免
charKey、g、fb 等缩写;在
src/public/parts/shells/chat/src/group/routes/groups.mjs#L85-L91 和
src/public/parts/shells/chat/public/hub/friendChat.mjs#L81-L88
改用该共享函数,移除各处重复的本地匹配逻辑。

Sources: Coding guidelines, Path instructions

src/public/parts/shells/chat/src/chat/federation/chunks.mjs (2)

506-514: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

改用 async/await,不要保留 Promise 链。

Line 506-514 使用 .then() 处理动态导入。将 attachTrustGraphChunkHandlers 改为 async 函数,用 try/catch 包住 await import(...)。调用方不等待时,使用 void 明确表示有意悬空。

As per coding guidelines:异步代码必须使用 async/await

As per path instructions:DAG 联邦改动遵循 async/await 约定。

建议改法
-export function attachTrustGraphChunkHandlers(room, fedOut, rtcLimits = {}, roomKey = '') {
-	import('npm:`@steve02081504/fount-p2p/files/chunk/responder`').then(({ attachTrustGraphFedChunkResponder }) => {
+export async function attachTrustGraphChunkHandlers(room, fedOut, rtcLimits = {}, roomKey = '') {
+	try {
+		const { attachTrustGraphFedChunkResponder } = await import('npm:`@steve02081504/fount-p2p/files/chunk/responder`')
 		attachTrustGraphFedChunkResponder(
 			room,
 			fedOut,
 			isFederationActionAllowedUnderLoad,
 			rtcLimits,
 			roomKey,
 		)
-	}).catch(error => console.warn('federation: failed to attach trust-graph chunk handlers', error))
+	} catch (error) {
+		console.warn('federation: failed to attach trust-graph chunk handlers', error)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/chat/federation/chunks.mjs` around lines 506
- 514, 将动态导入及调用逻辑所在的 attachTrustGraphChunkHandlers 改为 async 函数,用 try/catch 包裹
await import(...) 和 attachTrustGraphFedChunkResponder
调用,并保留现有错误警告内容;所有不等待该异步函数的调用处使用 void,明确表示其 Promise 被有意悬空。

Sources: Coding guidelines, Path instructions


506-514: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

将动态导入改为 async/await

attachTrustGraphFedChunkResponder 的导出路径、名称和五参数签名均正确。仅需把 .then() 链改为 await import(...),并保留现有 catch

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/chat/federation/chunks.mjs` around lines 506
- 514, 在包含 attachTrustGraphFedChunkResponder 的动态导入流程中,将 import(...).then(...)
链改为使用 await import(...),保持现有导出路径、五参数调用签名及处理逻辑不变,并保留当前 catch 错误处理和日志。

Source: Linked repositories

src/public/locales/en-UK.json (2)

1501-1502: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

统一 Allow/Deny 权限术语。

Line 1502 使用 Allow/Deny,但 channelPerms.stateDeny 在 Line 1508 显示为 reject。同一权限状态会出现两个名称。建议将 stateDeny 改为 deny,与权限模型保持一致。

建议修改
- "stateDeny": "reject",
+ "stateDeny": "deny",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/en-UK.json` around lines 1501 - 1502, 统一 channelPerms
中拒绝状态的权限术语:将 stateDeny 使用的 “reject” 改为 “deny”,使其与 denied 提示及权限模型中的 Allow/Deny
命名一致,并保留其他状态不变。

573-581: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

统一频道权限状态术语。 channelPerms.hint 使用 Allow/Deny,但按钮使用 allow/reject/inherit。将状态文案统一为 Allow/Deny/Inherit

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/en-UK.json` around lines 573 - 581, Update the channel
permission status translations associated with channelPerms.hint and the
allow/reject/inherit controls so their displayed terminology is consistently
“Allow/Deny/Inherit”; replace the existing “Reject” wording with “Deny” and
capitalize all three status labels.
src/public/locales/is-IS.json (1)

1948-1948: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

markdownRenderFailed 改为明确的 Markdown 渲染失败提示。

"Skilaboð mistókst" 未说明 Markdown。将其改为明确表达 Markdown 渲染失败的冰岛语文案,并保留 messageRenderFailed 的通用提示。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/is-IS.json` at line 1948, 更新冰岛语 locale 中的
markdownRenderFailed 文案,使其明确表示 Markdown 渲染失败,而不是使用当前含义不明确的提示;保留
messageRenderFailed 的通用失败文案不变。
src/public/locales/ja-JP.json (1)

1502-1502: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

删除已移除流程的“添加覆盖”说明。

Line [1502] 仍写着“ここに「上書きを追加」ボタンはありません”。当前频道权限 UI 已移除该按钮。此文案会向用户说明不存在的控件。删除最后一句,只保留继承、允许、拒绝和 Everyone 文件上传说明。

建议修改
- "hint": "特定チャンネルの例外のみ設定できます。継承=ロールの権限に従う、許可/拒否=このチャンネルで強制上書き。全員に添付を許可するには「役割の権限」で Everyone のファイルをアップロードを有効にしてください。ここに「上書きを追加」ボタンはありません。"
+ "hint": "特定チャンネルの例外のみ設定できます。継承=ロールの権限に従う、許可/拒否=このチャンネルで強制上書き。全員に添付を許可するには「役割の権限」で Everyone のファイルをアップロードを有効にしてください。"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/locales/ja-JP.json` at line 1502, Update the “hint” translation
entry in ja-JP.json to remove the final sentence mentioning the “上書きを追加” button,
while preserving the preceding explanations about inheritance, allow/deny
overrides, and Everyone’s file-upload permission.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/public/pages/scripts/components/emojiPicker.mjs`:
- Line 619: Update mountEmojiPicker to catch failures after attaching the panel,
remove the panel element before rethrowing, and preserve propagation to the
existing handleError('chat.emoji.loadFailed') caller. Ensure cleanup applies
when buildSections or any later mounting step fails.

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs`:
- Line 55: 在消息发送流程中更新附件元数据构造处的 file.size 赋值,移除 Number(file.size) || 0
的类型转换和防御性兜底,直接保留 file.size 的原始值;不要引入临时变量或其他校验。

In `@src/public/parts/shells/chat/public/shared/friendBinding.mjs`:
- Around line 38-43: Remove the duplicate charFriendBindingInput definition from
privateGroup.mjs and import the shared implementation from friendBinding.mjs.
Update its existing call sites to use the imported function while preserving the
current input behavior.

In `@src/public/parts/shells/chat/public/src/composerAttachments.mjs`:
- Line 102: 在图片编辑替换 file.buffer 的处理逻辑中,将 file.size 同步设置为
edited.size,确保发送元数据和附件展示使用新大小;同时增加断言验证 file.size 与 edited.size 保持一致。

In `@src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs`:
- Around line 34-35: Normalize charname with charname?.trim() when constructing
the DELETE path in the groupFriendBinding endpoint at
src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs lines
34-35. Apply the same change in the groupFriendBinding API implementation at
src/public/parts/shells/chat/public/src/api/groupFriendBinding.mjs lines 34-35
so both unbinding paths behave consistently.

In `@src/public/parts/shells/chat/src/chat/lib/inbox.mjs`:
- Around line 108-109: Remove the single-use temporary bindings across the three
sites: in src/public/parts/shells/chat/src/chat/lib/inbox.mjs lines 108-109,
inline the author display-name expression in the returned object; in
src/public/parts/shells/chat/src/entity/member.mjs line 28, return
ensureAgentEntityIdentity(...).entityHash directly; and in
src/public/parts/shells/chat/src/group/routes/channelMessages.mjs lines 113-128,
pass the maxDagPayloadBytes expression directly to attachFilesToContent.

In `@src/public/parts/shells/chat/src/chat/session/partConfig.mjs`:
- Around line 233-235: Remove the direct ensureAgentEntityIdentity import and
call from the surrounding configuration flow, and obtain the canonical
character-part name through resolveCharPartName() instead. Preserve
appendAgentMemberJoin() as the sole agent identity initialization entry point
and keep the resulting name assignment behavior unchanged.

In `@src/public/parts/shells/chat/src/entity/charPartName.mjs`:
- Around line 12-15: 移除 `resolveCharPartNameAgainstList` 重导出上方的空 JSDoc
注释;如果项目要求为该重导出保留文档,则改为提供真实的一行中文摘要,避免使用空的 `/** */` 占位块。

In `@src/public/parts/shells/chat/src/entity/charPartNameMatch.mjs`:
- Around line 16-21: 在 charPartNameMatch 的名称匹配逻辑中移除 String(raw || '') 和
String(part) 的类型转换及防御性处理,直接按已声明的字符串契约调用字符串方法;将 lower
重命名为表达完整名称含义的变量,并使用该变量完成比较,同时保留 chars/ 前缀清理和必填校验。

In `@src/public/parts/shells/chat/src/entity/identity.mjs`:
- Around line 433-436: Remove the one-time temporary bindings in identity.mjs by
passing resolveCharPartName(username, charPartName) directly as
ensureEntityIdentity’s charPartName value. In dagSession.mjs, assign
row.charPartName directly to content.charname; apply these changes at
src/public/parts/shells/chat/src/entity/identity.mjs:433-436 and
src/public/parts/shells/chat/src/chat/session/dagSession.mjs:61-66.

In `@src/public/parts/shells/chat/src/group/routes/channelReactions.mjs`:
- Line 32: 在 channel reaction 路由中移除仅使用一次的 resolvedCharname 临时绑定,并在传给
triggerReply 的 charname 参数处直接使用 charname?.trim() || null,确保空白角色名按 null
处理而不是作为显式回复目标。

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`:
- Around line 16-23: Replace abbreviated local variables throughout the scanner
and related test fixtures with descriptive names: use index instead of i,
character instead of c, and source instead of s. Apply the renames consistently
across isIdentContinue, staticModuleDeclarations, and the referenced test ranges
without changing scanning behavior.

In `@src/scripts/checks/json_lf.mjs`:
- Around line 56-66: Update scanJsonLf to obtain its input paths from Git’s
tracked-file list instead of listRepoFiles, while preserving the options.under
filtering and existing JSON suffix filtering; continue scanning each selected
file with scanFileJsonLf and returning the same files/issues structure.

In `@src/scripts/checks/test/json_lf.test.mjs`:
- Line 40: 在 issues 的 map 回调中,将缩写参数 i 重命名为 issue,并同步更新回调体内对该参数的引用;保持现有格式化与 join
逻辑不变。

In `@src/scripts/test/AGENTS.md`:
- Line 60: Update the Chat / Social fixtures entry in AGENTS.md to replace all
Chinese text with equivalent English wording, preserving the existing fixture
names, resolveCharPartName behavior, directory matching, and Hub exact-string
comparison guidance.

---

Outside diff comments:
In `@src/public/locales/en-UK.json`:
- Around line 1501-1502: 统一 channelPerms 中拒绝状态的权限术语:将 stateDeny 使用的 “reject” 改为
“deny”,使其与 denied 提示及权限模型中的 Allow/Deny 命名一致,并保留其他状态不变。
- Around line 573-581: Update the channel permission status translations
associated with channelPerms.hint and the allow/reject/inherit controls so their
displayed terminology is consistently “Allow/Deny/Inherit”; replace the existing
“Reject” wording with “Deny” and capitalize all three status labels.

In `@src/public/locales/is-IS.json`:
- Line 1948: 更新冰岛语 locale 中的 markdownRenderFailed 文案,使其明确表示 Markdown
渲染失败,而不是使用当前含义不明确的提示;保留 messageRenderFailed 的通用失败文案不变。

In `@src/public/locales/ja-JP.json`:
- Line 1502: Update the “hint” translation entry in ja-JP.json to remove the
final sentence mentioning the “上書きを追加” button, while preserving the preceding
explanations about inheritance, allow/deny overrides, and Everyone’s file-upload
permission.

In `@src/public/parts/shells/chat/public/shared/friendBinding.mjs`:
- Around line 23-29: 集中好友绑定匹配规则:在
src/public/parts/shells/chat/public/shared/friendBinding.mjs#L23-L29 导出
friendBindingMatches(candidate, requested),统一按 entityHash 或 charname 匹配并避免
charKey、g、fb 等缩写;在
src/public/parts/shells/chat/src/group/routes/groups.mjs#L85-L91 和
src/public/parts/shells/chat/public/hub/friendChat.mjs#L81-L88
改用该共享函数,移除各处重复的本地匹配逻辑。

In `@src/public/parts/shells/chat/src/chat/federation/chunks.mjs`:
- Around line 506-514: 将动态导入及调用逻辑所在的 attachTrustGraphChunkHandlers 改为 async 函数,用
try/catch 包裹 await import(...) 和 attachTrustGraphFedChunkResponder
调用,并保留现有错误警告内容;所有不等待该异步函数的调用处使用 void,明确表示其 Promise 被有意悬空。
- Around line 506-514: 在包含 attachTrustGraphFedChunkResponder 的动态导入流程中,将
import(...).then(...) 链改为使用 await import(...),保持现有导出路径、五参数调用签名及处理逻辑不变,并保留当前
catch 错误处理和日志。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bed3e284-005e-41ee-bb4f-e50dc8778c8a

📥 Commits

Reviewing files that changed from the base of the PR and between e1dae32 and e93ac20.

📒 Files selected for processing (88)
  • AGENTS.md
  • src/decl/locale_data.ts
  • src/public/locales/ar-SA.json
  • src/public/locales/de-DE.json
  • src/public/locales/docs/locale-edits.md
  • src/public/locales/emoji.json
  • src/public/locales/en-UK.json
  • src/public/locales/es-ES.json
  • src/public/locales/fr-FR.json
  • src/public/locales/hi-IN.json
  • src/public/locales/is-IS.json
  • src/public/locales/it-IT.json
  • src/public/locales/ja-JP.json
  • src/public/locales/ko-KR.json
  • src/public/locales/lzh.json
  • src/public/locales/nl-NL.json
  • src/public/locales/pt-PT.json
  • src/public/locales/ru-RU.json
  • src/public/locales/uk-UA.json
  • src/public/locales/vi-VN.json
  • src/public/locales/zh-CN.json
  • src/public/locales/zh-TW.json
  • src/public/pages/scripts/components/emojiPicker.mjs
  • src/public/parts/shells/chat/public/hub/core/domUtils.mjs
  • src/public/parts/shells/chat/public/hub/core/state.mjs
  • src/public/parts/shells/chat/public/hub/entityResolve.mjs
  • src/public/parts/shells/chat/public/hub/friendBindings.mjs
  • src/public/parts/shells/chat/public/hub/friendChat.mjs
  • src/public/parts/shells/chat/public/hub/friendsList.mjs
  • src/public/parts/shells/chat/public/hub/groupContextMenu.mjs
  • src/public/parts/shells/chat/public/hub/messages/channelMessageStore.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageSend.mjs
  • src/public/parts/shells/chat/public/hub/messages/messageShared.mjs
  • src/public/parts/shells/chat/public/hub/presence.mjs
  • src/public/parts/shells/chat/public/hub/privateGroup.mjs
  • src/public/parts/shells/chat/public/hub/profilePopup.mjs
  • src/public/parts/shells/chat/public/shared/friendBinding.mjs
  • src/public/parts/shells/chat/public/shared/retainLocalAttachmentBuffers.mjs
  • src/public/parts/shells/chat/public/src/api/groupFriendBinding.mjs
  • src/public/parts/shells/chat/public/src/composerAttachments.mjs
  • src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs
  • src/public/parts/shells/chat/src/api/channel.mjs
  • src/public/parts/shells/chat/src/chat/archive/postSnapshot.mjs
  • src/public/parts/shells/chat/src/chat/dag/hydration.mjs
  • src/public/parts/shells/chat/src/chat/dag/reducers/members.mjs
  • src/public/parts/shells/chat/src/chat/federation/chunks.mjs
  • src/public/parts/shells/chat/src/chat/federation/roomHandlers/rpc.mjs
  • src/public/parts/shells/chat/src/chat/lib/inbox.mjs
  • src/public/parts/shells/chat/src/chat/session/channelActivity.mjs
  • src/public/parts/shells/chat/src/chat/session/dagSession.mjs
  • src/public/parts/shells/chat/src/chat/session/partConfig.mjs
  • src/public/parts/shells/chat/src/chat/session/triggerPipeline.mjs
  • src/public/parts/shells/chat/src/entity/charPartName.mjs
  • src/public/parts/shells/chat/src/entity/charPartNameMatch.mjs
  • src/public/parts/shells/chat/src/entity/entitySearch.mjs
  • src/public/parts/shells/chat/src/entity/identity.mjs
  • src/public/parts/shells/chat/src/entity/member.mjs
  • src/public/parts/shells/chat/src/group/access.mjs
  • src/public/parts/shells/chat/src/group/lib/friendBinding.mjs
  • src/public/parts/shells/chat/src/group/lib/mentionSuggest.mjs
  • src/public/parts/shells/chat/src/group/routes/channelCrud.mjs
  • src/public/parts/shells/chat/src/group/routes/channelMessages.mjs
  • src/public/parts/shells/chat/src/group/routes/channelReactions.mjs
  • src/public/parts/shells/chat/src/group/routes/groupSync.mjs
  • src/public/parts/shells/chat/src/group/routes/groups.mjs
  • src/public/parts/shells/chat/test/harness.mjs
  • src/public/parts/shells/chat/test/integration/entity_search.test.mjs
  • src/public/parts/shells/chat/test/integration/friend_char_group.test.mjs
  • src/public/parts/shells/chat/test/integration/mailbox_summary.test.mjs
  • src/public/parts/shells/chat/test/integration/owner_profile_update.test.mjs
  • src/public/parts/shells/chat/test/integration/trust_graph_chunk_handlers.test.mjs
  • src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs
  • src/public/parts/shells/chat/test/pure/char_part_name.test.mjs
  • src/public/parts/shells/chat/test/pure/retain_local_attachment_buffers.test.mjs
  • src/scripts/checks/AGENTS.md
  • src/scripts/checks/i18n_keys.mjs
  • src/scripts/checks/json_lf.mjs
  • src/scripts/checks/test/i18n_keys.test.mjs
  • src/scripts/checks/test/json_lf.test.mjs
  • src/scripts/checks/test/manifest.json
  • src/scripts/i18n/bare.mjs
  • src/scripts/test/AGENTS.md
  • src/scripts/test/core/estimate.mjs
  • src/scripts/test/playwright/cdn_cache.mjs
  • src/scripts/test/runner/index.mjs
  • src/scripts/test/runner/report.mjs
  • src/scripts/test/selftest/cdn_cache.test.mjs
  • src/scripts/test/selftest/estimate.test.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • steve02081504/fount-p2p (manual)
💤 Files with no reviewable changes (10)
  • src/public/parts/shells/chat/public/hub/core/state.mjs
  • src/public/locales/es-ES.json
  • src/public/locales/ru-RU.json
  • src/public/locales/hi-IN.json
  • src/public/locales/nl-NL.json
  • src/public/locales/it-IT.json
  • src/public/locales/uk-UA.json
  • src/public/locales/fr-FR.json
  • src/public/locales/vi-VN.json
  • src/public/locales/pt-PT.json

button.addEventListener('click', event => {
event.preventDefault()
void mountEmojiPicker(button, onInsert, pickerContext)
mountEmojiPicker(button, onInsert, pickerContext).catch(handleError('chat.emoji.loadFailed'))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

在加载失败时移除浮动面板。

mountEmojiPicker 在等待 buildSections() 前已将 panel 挂到 document.body。失败时,wireOutsideClickClose 尚未注册。当前代码只报告错误,会留下无法通过外部点击关闭的 picker。

mountEmojiPicker 内部捕获失败,执行 panel.remove() 后再重新抛出错误。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/pages/scripts/components/emojiPicker.mjs` at line 619, Update
mountEmojiPicker to catch failures after attaching the panel, remove the panel
element before rethrowing, and preserve propagation to the existing
handleError('chat.emoji.loadFailed') caller. Ensure cleanup applies when
buildSections or any later mounting step fails.

fileId: '',
name: file.name || 'file',
mime_type: file.mime_type || 'application/octet-stream',
size: Number(file.size) || 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

删除附件大小的类型盘问。

Line 55 会把缺失或非法的 file.size 静默改成 0。直接使用 file.size,不要伪造零字节附件元数据。

建议修改
-			size: Number(file.size) || 0,
+			size: file.size,

As per coding guidelines: “Avoid one-off temporary bindings, poor abbreviations, and unnecessary type questioning or defensive checks.” As per path instructions: “类型盘问与防御性编程是坏味道。”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
size: Number(file.size) || 0,
size: file.size,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/hub/messages/messageSend.mjs` at line 55,
在消息发送流程中更新附件元数据构造处的 file.size 赋值,移除 Number(file.size) || 0 的类型转换和防御性兜底,直接保留
file.size 的原始值;不要引入临时变量或其他校验。

Sources: Coding guidelines, Path instructions

Comment on lines +38 to +43
export function charFriendBindingInput(charname, displayName) {
if (!charname) throw new Error('charname required')
return {
entityHash: eh,
charname: name,
...displayName ? { displayName } : {},
charname,
...displayName ? { displayName: String(displayName).trim() } : {},
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

删除重复的好友输入构造器。

src/public/parts/shells/chat/public/hub/privateGroup.mjs 仍定义了相同的 charFriendBindingInput。保留两个实现会使输入契约分叉。删除本地定义,并直接导入此函数。

As per path instructions, “精简、DRY、SRP。”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/shared/friendBinding.mjs` around lines 38
- 43, Remove the duplicate charFriendBindingInput definition from
privateGroup.mjs and import the shared implementation from friendBinding.mjs.
Update its existing call sites to use the imported function while preserving the
current input behavior.

Source: Path instructions

const newFile = {
name: file.name,
mime_type: file.type,
size: file.size,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 'file\.(buffer|size|name|mime_type)\s*=|size:' \
  src/public/parts/shells/chat/public/src/composerAttachments.mjs

Repository: steve02081504/fount

Length of output: 4054


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- attachment module ---'
sed -n '80,120p' src/public/parts/shells/chat/public/src/composerAttachments.mjs
sed -n '250,292p' src/public/parts/shells/chat/public/src/composerAttachments.mjs

printf '%s\n' '--- size consumers and tests ---'
rg -n -C 5 '\.size\b|size:' src/public/parts/shells/chat src | head -240
printf '%s\n' '--- related test files ---'
git ls-files | rg 'composerAttachments|chat.*test|test.*chat' || true

Repository: steve02081504/fount

Length of output: 40762


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- attachment size paths ---'
rg -n -C 6 'selectedFiles|file\.size|formatBytes|attachments' \
  src/public/parts/shells/chat/public/src/composerAttachments.mjs \
  src/public/parts/shells/chat/public/src src/public/parts/shells/chat/public/*.mjs \
  src/public/parts/shells/chat/test/frontend/attachments.spec.mjs \
  src/public/parts/shells/chat/test/frontend/composer.spec.mjs \
  src/public/parts/shells/chat/test/integration/message_attachments.test.mjs \
  src/public/parts/shells/chat/test/pure/retain_local_attachment_buffers.test.mjs \
  2>/dev/null | head -300

printf '%s\n' '--- exact size declarations/usages in chat JavaScript ---'
rg -n '\bsize\b' src/public/parts/shells/chat --glob '*.mjs' --glob '*.js' --glob '*.ts' | head -240

Repository: steve02081504/fount

Length of output: 49188


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- send metadata path ---'
sed -n '1,85p' src/public/parts/shells/chat/public/hub/messages/messageSend.mjs
sed -n '145,220p' src/public/parts/shells/chat/src/chat/channel/postMessage.mjs

printf '%s\n' '--- rendered attachment size path ---'
sed -n '1,65p' src/public/parts/shells/chat/public/shared/channelContent.mjs
sed -n '235,260p' src/public/parts/shells/chat/public/hub/messages/render/file.mjs

printf '%s\n' '--- composer callers ---'
rg -n -C 8 'messageSend|postMessage|selectedFiles|attachments' \
  src/public/parts/shells/chat/public/hub src/public/parts/shells/chat/public/src \
  --glob '*.mjs' | head -320

Repository: steve02081504/fount

Length of output: 39596


同步编辑后的附件大小。

图片编辑替换 file.buffer 后未更新 file.size。发送元数据和消息附件展示会继续使用旧大小。将 file.size 设置为 edited.size,并增加断言验证二者同步。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/src/composerAttachments.mjs` at line 102,
在图片编辑替换 file.buffer 的处理逻辑中,将 file.size 同步设置为
edited.size,确保发送元数据和附件展示使用新大小;同时增加断言验证 file.size 与 edited.size 保持一致。

Comment on lines +34 to +35
if (charname)
await groupFetch(groupPath(groupId, 'char', charname), { method: 'DELETE' })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

统一在解绑边界规范化 charname

两份实现都把原始 truthy 值传入解绑路径。groupPath 只编码,不规范化。DELETE 失败后仍清除群绑定,会留下未解绑的 session。

  • src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs#L34-L35:使用 charname?.trim() 构造 DELETE 路径。
  • src/public/parts/shells/chat/public/src/api/groupFriendBinding.mjs#L34-L35:使用相同规范化逻辑,避免两个 endpoint 实现继续漂移。
📍 Affects 2 files
  • src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs#L34-L35 (this comment)
  • src/public/parts/shells/chat/public/src/api/groupFriendBinding.mjs#L34-L35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs`
around lines 34 - 35, Normalize charname with charname?.trim() when constructing
the DELETE path in the groupFriendBinding endpoint at
src/public/parts/shells/chat/public/src/endpoints/groupFriendBinding.mjs lines
34-35. Apply the same change in the groupFriendBinding API implementation at
src/public/parts/shells/chat/public/src/api/groupFriendBinding.mjs lines 34-35
so both unbinding paths behave consistently.

const { groupId, channelId } = req.groupContext
const { charname } = req.body || {}
const resolvedCharname = String(charname || '').trim() || null
const resolvedCharname = charname || null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

不要把空白角色名当成显式回复目标。

Line 32 让 ' ' 保持 truthy,Line 35 会把它作为明确的 charname 传给 triggerReply。这会把空选择器变成无效角色选择。直接使用 charname?.trim() || null,并删除一次性绑定。

修复建议
-		const resolvedCharname = charname || null
-
 		const { client } = await chatClientFromReq(req)
-		await (await (await client.group(groupId)).channel(channelId)).triggerReply(resolvedCharname)
+		await (await (await client.group(groupId)).channel(channelId)).triggerReply(charname?.trim() || null)

As per path instructions: “禁止只用一次的临时绑定,内联到使用处”。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/src/group/routes/channelReactions.mjs` at line
32, 在 channel reaction 路由中移除仅使用一次的 resolvedCharname 临时绑定,并在传给 triggerReply 的
charname 参数处直接使用 charname?.trim() || null,确保空白角色名按 null 处理而不是作为显式回复目标。

Source: Path instructions

Comment on lines +16 to +23
function isIdentContinue(text, index) {
const code = text.charCodeAt(index)
return code >= 48 && code <= 57
|| code >= 65 && code <= 90
|| code >= 97 && code <= 122
|| code === 36
|| code === 95
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

替换扫描器中的缩写局部变量。

ic 和测试夹具中的 s 不表达词法状态或内容。将它们统一改为 indexcharactersource。这会使 staticModuleDeclarations 的状态推进更容易审查。

As per path instructions,规则要求“可读全词:禁止 i”。As per coding guidelines,规则要求避免 “poor abbreviations”。

Also applies to: 31-56, 84-102, 123-149, 157-180, 190-229, 278-289

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/parts/shells/chat/test/integration/whip_native_optional.test.mjs`
around lines 16 - 23, Replace abbreviated local variables throughout the scanner
and related test fixtures with descriptive names: use index instead of i,
character instead of c, and source instead of s. Apply the renames consistently
across isIdentContinue, staticModuleDeclarations, and the referenced test ranges
without changing scanning behavior.

Sources: Coding guidelines, Path instructions

Comment on lines +56 to +66
export async function scanJsonLf(repoRoot, options = {}) {
const paths = await listRepoFiles(repoRoot, JSON_LF_SUFFIXES, { under: options.under })
/** @type {JsonLfIssue[]} */
const issues = []
for (const relativePath of paths) {
const bytes = new Uint8Array(await readFile(join(repoRoot, relativePath)))
const issue = scanFileJsonLf(relativePath, bytes)
if (issue) issues.push(issue)
}
const hitFiles = [...new Set(issues.map(issue => issue.path))].sort()
return { files: hitFiles, issues }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

只扫描 Git 已跟踪的 JSON 文件。

listRepoFiles() 会扫描未被 .gitignore 排除的工作区文件。未跟踪的 .json 文件也会进入检查。此时本地临时文件可使 repo: JSON files use LF line endings 失败。

改用 Git 已跟踪文件列表作为输入,并保留 under 过滤。

As per path instructions: “All tracked JSON files must use LF line endings; CRLF and lone CR line endings are invalid.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/checks/json_lf.mjs` around lines 56 - 66, Update scanJsonLf to
obtain its input paths from Git’s tracked-file list instead of listRepoFiles,
while preserving the options.under filtering and existing JSON suffix filtering;
continue scanning each selected file with scanFileJsonLf and returning the same
files/issues structure.

Source: Path instructions

Deno.test('repo: JSON files use LF line endings', async () => {
const { issues } = await scanJsonLf(REPO_ROOT)
if (issues.length) {
const sample = issues.slice(0, 12).map(i => `${i.path} (${i.kind})`).join('\n')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

替换缩写参数名。

i 不表达问题对象的含义。改为 issue

建议修改
-		const sample = issues.slice(0, 12).map(i => `${i.path} (${i.kind})`).join('\n')
+		const sample = issues.slice(0, 12).map(issue => `${issue.path} (${issue.kind})`).join('\n')

As per coding guidelines: “Avoid one-off temporary bindings, poor abbreviations, and unnecessary type questioning or defensive checks.”
As per path instructions: “禁止 i/msg/ctx/btn/S/aj.json 等烂名。”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sample = issues.slice(0, 12).map(i => `${i.path} (${i.kind})`).join('\n')
const sample = issues.slice(0, 12).map(issue => `${issue.path} (${issue.kind})`).join('\n')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/checks/test/json_lf.test.mjs` at line 40, 在 issues 的 map
回调中,将缩写参数 i 重命名为 issue,并同步更新回调体内对该参数的引用;保持现有格式化与 join 逻辑不变。

Sources: Coding guidelines, Path instructions

- **Live WS probes**: `createLiveShellHttp({ shell? })` from `wsHarness.mjs` — do not re-declare local HTTP helpers. End with `finishLiveWs` / `failLiveWsPrecondition`; frames via `waitForWsFrame`.
- **Polling**: `pollUntil` (live/fed, seconds, soft) / `waitUntil` (integration & selftest, ms, throws) — definitions in `core/wait.mjs`.
- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`.
- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `seedStubCharPart`(仅 `main.mjs` 占位,供 `resolveCharPartName` / `ensureAgent*`)/ `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`. 角色 part 名在写入边界经 `resolveCharPartName` 对齐 `chars/` 真实目录名;Hub 按原串 `===`,勿再 fold。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

移除 AGENTS.md 中的中文。

Line 60 含有 CJK 字符。agents_md_english 会扫描所有 AGENTS.md,因此该静态检查会失败。将这条说明改为英文。

建议修改
-- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `seedStubCharPart`(仅 `main.mjs` 占位,供 `resolveCharPartName` / `ensureAgent*`)/ `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`. 角色 part 名在写入边界经 `resolveCharPartName` 对齐 `chars/` 真实目录名;Hub 按原串 `===`,勿再 fold。
+- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `seedStubCharPart` (only a `main.mjs` placeholder for `resolveCharPartName` / `ensureAgent*`) / `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`. Resolve character part names to the real `chars/` directory name at the write boundary. Hub comparisons use the original string with `===`; do not fold it.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `seedStubCharPart`(仅 `main.mjs` 占位,供 `resolveCharPartName` / `ensureAgent*`/ `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`. 角色 part 名在写入边界经 `resolveCharPartName` 对齐 `chars/` 真实目录名;Hub 按原串 `===`,勿再 fold
- **Chat / Social fixtures**: `createCharBoot` / `seedCharFixture` / `seedStubCharPart` (only a `main.mjs` placeholder for `resolveCharPartName` / `ensureAgent*`) / `waitUntil` from `shells/chat/test/harness.mjs`; Social agents: `seedAgentChar` in `shells/social/test/harness.mjs`. Resolve character part names to the real `chars/` directory name at the write boundary. Hub comparisons use the original string with `===`; do not fold it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/test/AGENTS.md` at line 60, Update the Chat / Social fixtures
entry in AGENTS.md to replace all Chinese text with equivalent English wording,
preserving the existing fixture names, resolveCharPartName behavior, directory
matching, and Hub exact-string comparison guidance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants