Add mention highlighting

This commit is contained in:
Jack Kingsman
2026-01-09 18:42:38 -08:00
parent e497d3dceb
commit 2a6aeebabe
6 changed files with 76 additions and 29 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RemoteTerm for MeshCore</title>
<script type="module" crossorigin src="/assets/index-DycUwj3W.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C3_XTTUI.css">
<script type="module" crossorigin src="/assets/index-BHdy0kQg.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CtV9BARe.css">
</head>
<body>
<div id="root"></div>

View File

@@ -816,6 +816,7 @@ export function App() {
hasOlderMessages={hasOlderMessages}
onSenderClick={activeConversation.type === 'channel' ? handleSenderClick : undefined}
onLoadOlder={fetchOlderMessages}
radioName={config?.name}
/>
<MessageInput
ref={messageInputRef}

View File

@@ -1,4 +1,4 @@
import { useEffect, useLayoutEffect, useRef, useCallback, useState } from 'react';
import { useEffect, useLayoutEffect, useRef, useCallback, useState, type ReactNode } from 'react';
import type { Contact, Message } from '../types';
import { formatTime, parseSenderFromText } from '../utils/messageParser';
import { pubkeysMatch } from '../utils/pubkey';
@@ -13,6 +13,51 @@ interface MessageListProps {
hasOlderMessages?: boolean;
onSenderClick?: (sender: string) => void;
onLoadOlder?: () => void;
radioName?: string;
}
// Helper to render text with highlighted @[Name] mentions
function renderTextWithMentions(text: string, radioName?: string): ReactNode {
if (!radioName) return text;
const mentionPattern = /@\[([^\]]+)\]/g;
const parts: ReactNode[] = [];
let lastIndex = 0;
let match: RegExpExecArray | null;
let keyIndex = 0;
while ((match = mentionPattern.exec(text)) !== null) {
// Add text before the match
if (match.index > lastIndex) {
parts.push(text.slice(lastIndex, match.index));
}
const mentionedName = match[1];
const isOwnMention = mentionedName === radioName;
parts.push(
<span
key={keyIndex++}
className={cn(
"rounded px-0.5",
isOwnMention
? "bg-primary/30 text-primary font-medium"
: "bg-muted-foreground/20"
)}
>
@[{mentionedName}]
</span>
);
lastIndex = match.index + match[0].length;
}
// Add remaining text after last match
if (lastIndex < text.length) {
parts.push(text.slice(lastIndex));
}
return parts.length > 0 ? parts : text;
}
export function MessageList({
@@ -23,6 +68,7 @@ export function MessageList({
hasOlderMessages = false,
onSenderClick,
onLoadOlder,
radioName,
}: MessageListProps) {
const listRef = useRef<HTMLDivElement>(null);
const prevMessagesLengthRef = useRef<number>(0);
@@ -234,7 +280,7 @@ export function MessageList({
<div className="break-words whitespace-pre-wrap">
{content.split('\n').map((line, i, arr) => (
<span key={i}>
{line}
{renderTextWithMentions(line, radioName)}
{i < arr.length - 1 && <br />}
</span>
))}