diff --git a/web/src/features/messages/MentionPicker.tsx b/web/src/features/messages/MentionPicker.tsx
index 3e6b8e5b..c1a2983e 100644
--- a/web/src/features/messages/MentionPicker.tsx
+++ b/web/src/features/messages/MentionPicker.tsx
@@ -50,28 +50,38 @@ export function MentionPicker({ topicId, query, onPick }: MentionPickerProps) {
所有人
) : null}
- {items.map((m) => (
-
+ );
+ })}
>
)}
diff --git a/web/src/lib/api/groups.ts b/web/src/lib/api/groups.ts
index d3dd340b..04d8e4f7 100644
--- a/web/src/lib/api/groups.ts
+++ b/web/src/lib/api/groups.ts
@@ -147,7 +147,23 @@ export type GroupRole = 'owner' | 'admin' | 'member';
export interface GroupMember {
user_id: string;
+ /**
+ * Real, live-confirmed gotcha (2026-09-24): this is NOT always the member's own real name.
+ * Reading server/im_http.go's userSummaryPayload confirms `display_name` here unconditionally
+ * prefers the CALLING viewer's own private remark for that member when one is set, falling back
+ * to their real public nickname only when the viewer has no remark for them -- correct and
+ * intended for rendering a list to yourself (README's member list/search), completely wrong for
+ * anything that gets baked into shared message CONTENT once and read by everyone from then on
+ * (an @mention's label): a real reported bug was exactly this -- a group owner's own private
+ * remark for a member ("小号1") ended up as that member's permanent @mention text, which every
+ * OTHER member then saw with no idea who it meant. Use `nickname` (below) instead for that case.
+ */
display_name?: string;
+ /** The member's own real public profile nickname -- same backend response, but from
+ * copyUserNickname(), which reads straight off their public profile and is NEVER shadowed by the
+ * viewer's own remark the way `display_name` above is. This is what stays correct for everyone
+ * once baked into shared content (an @mention's label), not just for the viewer's own screen. */
+ nickname?: string;
avatar_file_id?: string;
role: GroupRole;
mentionable?: boolean;