Agent reference: Chat Panel
Deep subsystem reference for AI assistants. Open this when a task touches the Chat panel, composer, link previews, notifications, dedup, hop badges, reactions/tapbacks, mentions, or chat/support export. Hard rules live in AGENTS.md.
- Components:
ChatPanel.tsx(channel/DM UI) + sharedChatComposer.tsx(drafts, mentions, chunking, spellcheck, emoji; also used byRoomsPanel.tsx). Reticulum DM Share as paper / Scan paper viaChatDmPaperControls.tsx+createReticulumPaperMessage.ts. Scroll-at-bottom helper:chatScrollUtils.ts(getDistFromChatBottom). - Composer limits / send cadence:
chatComposerLimits.ts—getMaxChunks(protocol)viaProtocolCapabilities.composerMaxChunks(MeshCore = 1: no outbound[i/N]split;splitChatMessagereturnsnullwhen text needs more than one packet), room payload viagetMeshcoreRoomPayloadLimit,computeComposerLimitStatusphases (warnsurfaces a single-packet ⓘ hint;overMaxSingledisables send and shows arole="note"callout). MeshCore also gets a non-blocking ~5s "sending too fast" advisory (role="status", dismissible) from an app-wide clock inmeshcoreSendRateNotice.ts(recordMeshcoreSend/isMeshcoreSendTooFast,MESHCORE_FAST_SEND_WARN_INTERVAL_MS). Every live MeshCore send advances the clock and can show the advisory — text (handleSend), GIF, and share-location inChatComposer, plus outbox drain (useChatOutbox.ts). Legacy MeshCore outbox rows withgroupTotal > 1or[i/N]payloads are quarantined (blocked) on drain instead of being transmitted. Inbound multi-part[i/N]merge is unchanged. i18n:chatPanel.composeLimit.meshcoreSingleNotice.*,chatPanel.meshcoreFastSend.warning,chatPanel.outboxLegacyMultipartBlocked. Seemeshcore-meshtastic-parity.md. - Payload / links:
ChatPayloadText.tsx— mention highlighting, search marks, URL linkification; link previews viachat:fetchLinkPreview(src/main/fetchLinkPreview.ts): Open Graph for HTML pages; YouTube watch/shorts/youtu.be via oEmbed + thumbnail; direct image URLs (path extension viachatDirectImageUrl.tsor rasterContent-Type) returnkind: 'image'and render as inline embeds (ChatInlineImage/DirectImageEmbed); OG/YouTube use card layout. Security: DNS-pinned undiciAgent, private/loopback blocked, magic-byte MIME sniff (safeRasterImageMime.ts), HTTPS-only image embeds, 10s fetch / 3s DNS, 64 KiB HTML cap, 2 MiB image fetch cap (256 KiB cache payload cap), LRU caches, single-flight dedup (renderer map capped). Previews load even when scrolled up. LXMF attachment rasters:chat:readReticulumAttachmentAsDataUrl(reticulum-attachment-image.ts; path jail, magic-byte MIME, SVG rejected, 2 MiB, IPC rate limit) →ReticulumAttachmentLine. LXMF voice memos (hasReticulumVoiceMemo): Chat DM mic → sidecar/api/v1/voice/memo/*viaelectronAPI.reticulum.voiceMemo.*; playback viachat:readReticulumAttachmentBytes(reticulum-attachment-audio.ts; OggS sniff, 256 KiB) →ReticulumVoiceMemoLine. Reply quotes:replyPreview.ts. - Storage helpers:
src/renderer/lib/chatPanelProtocolStorage.ts— drafts (mesh-client:drafts:<protocol>), open DM tabs, last-read, per-view mute (mesh-client:mutedViews:<protocol>), starred (mesh-client:starred:<protocol>, cap 200), MeshCore flood-scope overrides per chat view (mesh-client:floodScopeOverrides:<protocol>, channel or DMviewKey). - Notifications:
src/renderer/lib/chatNotifications.ts—playMessageNotification(type)via Web Audio:channel= single 880 Hz pulse (150 ms);dm/reply= dual pulse (587.33 Hz then 783.99 Hz, 50 ms each, 35 ms gap). Resumes suspendedAudioContextwhen the window is hidden/minimized. Type selection inchatUnreadCounts.ts(resolveChatNotificationType,pickAudibleNotificationType; batch priority reply > dm > channel). ChatPanel plays when the user is on Chat but reading another view; App plays for other panels / backgrounded window (avoids double beep). Meshtastic hidden-window desktop notifications are visual-only (silent: trueinmeshtasticRouterSideEffects.ts); typed Web Audio from App owns sound. Global mutemesh-client:notifMuted; per-view mute inmutedViews. Main-process tray icon shows unread when chat or MeshCore Rooms traffic arrives while backgrounded (src/main/index.tsbuildTrayIcon). - Meshtastic dedup:
meshtasticMessageDedup.ts— merges delayed RF/MQTT duplicates (10-minute content window) inuseMeshtasticRuntimeingest. - Hop badges:
MessageRecord.rxHops/viaStoreForwardround-trip viastoreRecordAdapters.tsandmeshtasticDbCacheHydration.ts(hopCountonMessageRecordbridges torxHopsfor Meshtastic PacketRouter rows); Chat hop pills readChatMessage.rxHops. MeshCore (primary): companionpathLenon events 7/8 →meshcoreCompanionRxPathLenToHopCount→DomainEvent.payload.hopCount(MeshCoreProtocol,meshcoreDirectMessageDecode); waiting-message drain also setsrxHopsfrompathLen. MeshCore (fallback): raw-log correlation viaresolveMeshcoreIngestRxHops/MESHCORE_CHAT_CORRELATE_WINDOW_MS(3000ms);rawPacketsRefsynced inside event-136setRawPacketsupdater for same-tick ingest (meshcoreConnSideEffects). Meshtastic:MeshtasticProtocolusesmeshtasticComputedRfHopsAway— omit hops forviaMqttandhopStart === 0; elsehopStart - hopLimitwhenhopStart > 0 && hopLimit <= hopStart. - MeshCore dedup:
meshcoreStoreDedup.ts— RF/MQTT merge, companion TX echo, tapback self-echo, room BBS paths inuseMeshcoreRuntimeingest (5-minute cross-transport window; distinct from Meshtastic’s 10-minute window). - Reactions / tapbacks:
reactions.ts(Meshtastic protobuf) +meshcoreChannelText.ts(MeshCore default outbound keyless@[Name] emoji/@[Name] body; opt-in MeshCore Open compatibility in App enables keyed replies,r:reactions, andg:GIF send viameshcoreOpenWireCompatEnabledinappSettingsStorage.ts; inbound keyed/sec↔ms parent match viameshcoreMessageMatchesReplyKey; inbound emoji-only replies promoted viameshcorePromoteEmojiOnlyReplyToTapback; inbound Openr:HASH:INDEXinmeshcoreOpenReaction.ts);ChatPanel.tsxattaches tapbacks viareplyId+ runtime/panelsendReaction. - Mention segments:
src/renderer/lib/chatMentionSegments.ts— parse/build@[Name]tokens;MentionAutocomplete.tsxrenders the dropdown. - Export IPC:
chat:export— renderer callswindow.electronAPI.chat.export(messages); main opens a Save dialog and writes a.txtfile. - Support bundle IPC:
support:exportBundle—exportSupportBundle.ts→window.electronAPI.support.exportBundle(mode, json); mainsupport-bundle.tswrites zip (github= logs + debug snapshot including Reticulum diagnostic JSON and Meshtastic channel layout triage viadebugSnapshotMeshtasticContext.ts;developer= SQLite plus redactedreticulum/configandreticulum/mesh_client_stack.json). Modes insupport-bundle.types.ts.