From 5e1bdb2cc13f13dc6a67bd5d5a764ad5f50fb1cf Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Thu, 2 Apr 2026 00:23:07 -0700 Subject: [PATCH] Fix terminals on hash room parsing --- frontend/src/test/messageList.test.tsx | 26 +++++++++++++++++++++++++ frontend/src/test/messageParser.test.ts | 20 ++++++++++++++----- frontend/src/utils/messageParser.ts | 2 +- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/frontend/src/test/messageList.test.tsx b/frontend/src/test/messageList.test.tsx index ce68d79..26a9523 100644 --- a/frontend/src/test/messageList.test.tsx +++ b/frontend/src/test/messageList.test.tsx @@ -169,6 +169,32 @@ describe('MessageList channel sender rendering', () => { expect(onChannelReferenceClick).toHaveBeenCalledWith('#mesh-room'); }); + it('links valid channel references when followed by clause punctuation', async () => { + const user = userEvent.setup(); + const onChannelReferenceClick = vi.fn(); + + render( + + ); + + await user.click(screen.getByRole('button', { name: '#mesh-room' })); + await user.click(screen.getByRole('button', { name: '#ops-room' })); + await user.click(screen.getByRole('button', { name: '#alpha-room' })); + + expect(onChannelReferenceClick).toHaveBeenNthCalledWith(1, '#mesh-room'); + expect(onChannelReferenceClick).toHaveBeenNthCalledWith(2, '#ops-room'); + expect(onChannelReferenceClick).toHaveBeenNthCalledWith(3, '#alpha-room'); + }); + it('links valid channel references in direct messages too', async () => { const user = userEvent.setup(); const onChannelReferenceClick = vi.fn(); diff --git a/frontend/src/test/messageParser.test.ts b/frontend/src/test/messageParser.test.ts index e90d842..2b67329 100644 --- a/frontend/src/test/messageParser.test.ts +++ b/frontend/src/test/messageParser.test.ts @@ -122,11 +122,21 @@ describe('linked channel references', () => { ]); }); - it('ignores invalid or embedded channel-like text', () => { + it('finds linked channel references terminated by clause punctuation', () => { expect( - findLinkedChannelReferences( - 'skip #Bad #bad--name abc#ops #ops- #opsRoom #ops_room #good-room,' - ) - ).toEqual([]); + findLinkedChannelReferences('Join #mesh-room, then #ops2; finally #alpha-room.') + ).toEqual([ + { label: '#mesh-room', start: 5, end: 15 }, + { label: '#ops2', start: 22, end: 27 }, + { label: '#alpha-room', start: 37, end: 48 }, + ]); + }); + + it('ignores invalid or embedded channel-like text', () => { + const references = findLinkedChannelReferences( + 'skip #Bad #bad--name abc#ops #ops- #opsRoom #ops_room #good-room,' + ); + + expect(references.map((reference) => reference.label)).toEqual(['#good-room']); }); }); diff --git a/frontend/src/utils/messageParser.ts b/frontend/src/utils/messageParser.ts index 5fb864a..3d57684 100644 --- a/frontend/src/utils/messageParser.ts +++ b/frontend/src/utils/messageParser.ts @@ -3,7 +3,7 @@ * Channel messages have format "sender: message". */ const HASHTAG_CHANNEL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; -const HASHTAG_CHANNEL_REFERENCE_PATTERN = /(^|\s)(#[a-z0-9]+(?:-[a-z0-9]+)*)(?=$|\s)/g; +const HASHTAG_CHANNEL_REFERENCE_PATTERN = /(^|\s)(#[a-z0-9]+(?:-[a-z0-9]+)*)(?=$|[\s.,;:])/g; export function parseSenderFromText(text: string): { sender: string | null; content: string } { const colonIndex = text.indexOf(': ');