Skip to content
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
387e045
feat(chat): combine captions with media and fix redacted timeline rows
cursoragent Apr 14, 2026
a700b1a
feat(chat): add drag-and-drop file attach to human chat composer
cursoragent Apr 14, 2026
82967f6
fix(chat): preserve human chat scroll when history shrinks
cursoragent Apr 14, 2026
6e81095
fix: apply CodeRabbit auto-fixes
cursoragent Apr 14, 2026
bb86193
fix(matrix): satisfy RoomMessageEventContent format literal in sendMe…
cursoragent Apr 14, 2026
d08a24f
fix(matrix): allow format on Hypha media event content for captions
cursoragent Apr 15, 2026
6134e39
fix(chat): keep add-reaction emoji picker open inside overflow menus
cursoragent Apr 15, 2026
d6c9d5b
style(chat): compact composer selection format toolbar
cursoragent Apr 15, 2026
66ee428
feat(matrix): send voice as m.audio with duration metadata
cursoragent Apr 15, 2026
ea8f461
feat(chat): mic menu for dictation vs voice, Telegram-style audio pla…
cursoragent Apr 15, 2026
c275c80
feat(i18n): add HumanChatPanel strings for dictation and voice UI
cursoragent Apr 15, 2026
e48dd95
fix(chat): YouTube-style inline video playback and draft play overlay
cursoragent Apr 15, 2026
fbd4285
feat(matrix): edit media messages via m.replace with slots
cursoragent Apr 15, 2026
417ba6b
feat(chat): edit captions and remove attachments on media messages
cursoragent Apr 15, 2026
ce431d3
feat(ui): add squircle shape and chat avatar sizes
cursoragent Apr 15, 2026
4b0aa41
feat(chat): resolve Matrix member avatars for timeline and replies
cursoragent Apr 15, 2026
c8dfd7e
feat(chat): add Discord-style reply header and curved connector
cursoragent Apr 15, 2026
9b4ac28
fix(ci): restore check-types for matrix media edit and chat overflow
cursoragent Apr 15, 2026
3cd63a0
fix(chat): circular avatars and reply row alignment
cursoragent Apr 15, 2026
15ed069
fix: apply CodeRabbit auto-fixes
cursoragent Apr 15, 2026
d328110
feat(chat): improve voice composer stop control and dictation icon
cursoragent Apr 15, 2026
969c571
fix(chat): reply header avatars and top-aligned main avatar
cursoragent Apr 15, 2026
967e083
fix(chat): balance composer vertical padding around textarea and toolbar
cursoragent Apr 15, 2026
2491aa9
fix(matrix): count online members from presence status
cursoragent Apr 15, 2026
5eaa6d6
fix: apply CodeRabbit auto-fixes
cursoragent Apr 15, 2026
0fcafca
fix(chat): strip dictation ZWSP without polynomial regex (CodeQL)
cursoragent Apr 15, 2026
b5d6973
fix(chat): align main avatar top with sender when replying
cursoragent Apr 15, 2026
269048e
fix(human-chat): send on Enter when attach menu is open
cursoragent Apr 15, 2026
e90f203
fix(human-chat): open sibling toolbar popovers in one click
cursoragent Apr 15, 2026
3a4987c
fix(human-chat): align reply connector to quoted avatar
cursoragent Apr 15, 2026
9efbf0a
fix: apply CodeRabbit auto-fixes
cursoragent Apr 15, 2026
c20b13c
fix(human-chat): measure reply thread connector between avatars
cursoragent Apr 15, 2026
c79ca79
fix(human-chat): send on Enter when focus is on draft attachments
cursoragent Apr 15, 2026
b6626d3
fix(human-chat): stream dictation text into composer in real time
cursoragent Apr 15, 2026
7757725
docs(test): link media-edit reply coverage to matrix-provider
cursoragent Apr 15, 2026
4cee0dd
fix(human-chat): resolve member display names from Hypha profile
cursoragent Apr 18, 2026
f5ec335
fix(human-chat): tune Discord-style reply connector geometry
cursoragent Apr 18, 2026
ec3e416
fix(human-chat): Enter sends when toolbar focused or after voice flows
cursoragent Apr 18, 2026
c51f49e
feat(human-chat): Telegram-style voice row in timeline and composer
cursoragent Apr 18, 2026
9476bd4
fix(human-chat): show mic and dictate buttons in composer bar
cursoragent Apr 18, 2026
02787ac
fix(human-chat): spoiler preview and reveal for voice attachments
cursoragent Apr 18, 2026
ea14f57
feat(human-chat): cancel pending uploads and black voice play button
cursoragent Apr 18, 2026
f40dbe9
fix(human-chat): show format toolbar after selection drag ends
cursoragent Apr 18, 2026
f1ff261
fix(human-chat): copy Hypha chat deep links instead of matrix.to
cursoragent Apr 18, 2026
b565a3a
feat(human-chat): unread markers, daily separators, and mark read
cursoragent Apr 18, 2026
b5039c1
fix(human-chat): polish voice/dictate stop button affordance
cursoragent Apr 18, 2026
069d00b
fix(human-chat): close overflow when using quick reaction emojis
cursoragent Apr 18, 2026
00c0a81
feat(human-chat): highlight pasted URLs in composer like Discord
cursoragent Apr 18, 2026
5cf93ab
fix(ci): unblock check-types for Matrix read markers and human chat
cursoragent Apr 18, 2026
c1c938b
fix(human-chat): avoid ReDoS in composer URL punctuation trim
cursoragent Apr 18, 2026
73585d1
feat(human-chat): animate voice waveform bars while audio plays
cursoragent Apr 18, 2026
0a5b0d0
fix(human-chat): swap voice toolbar icons and per-slot stop controls
cursoragent Apr 18, 2026
e4f26fc
feat(human-chat): use microphone icon for idle voice playback control
cursoragent Apr 18, 2026
34fc11f
style(human-chat): match video play overlay to black circle like voic…
cursoragent Apr 18, 2026
006859c
feat(human-chat): REC on-air blink for recording/dictate stop slots
cursoragent Apr 18, 2026
ee9307d
fix(human-chat): anchor format toolbar above selection
cursoragent Apr 18, 2026
b00fbfe
test(core): tighten media edit empty-caption reply regression tests
cursoragent Apr 18, 2026
d601230
fix(human-chat): show reply connector above avatars
cursoragent Apr 18, 2026
e602423
fix(human-chat): redraw reply connector with reliable L path
cursoragent Apr 18, 2026
6129e95
fix(human-chat): resolve prod_privy_* Matrix display names via Hypha …
cursoragent Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/core/src/matrix/__tests__/chat-markup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
matrixTextEventContentWithOptionalFormatting,
parseChatMarkup,
} from '../chat-markup';
import { splitRichReplyPlainBody } from '../rich-reply';

describe('chatMarkupLooksFormatted', () => {
it('is false for plain text', () => {
Expand Down Expand Up @@ -44,4 +45,23 @@ describe('buildRichReplyMatrixContent', () => {
expect(r.formatted_body).toContain('<strong>');
expect(r.formatted_body).toContain('&lt;@u:h&gt;');
});

/**
* Media edit with cleared caption but existing reply: matrix-provider passes a
* single space so rich-reply markup stays valid (regression guard).
*/
it('preserves reply thread with space-only reply text (empty caption placeholder)', () => {
const r = buildRichReplyMatrixContent(
'@alice:example.org',
'original',
' ',
);
expect(r.body).toContain('> <@alice:example.org>');
expect(r.body).toContain('original');
expect(r.format).toBe('org.matrix.custom.html');
expect(r.formatted_body.length).toBeGreaterThan(0);
const split = splitRichReplyPlainBody(r.body);
expect(split.reply).toBe('');
expect(split.quoted).toContain('@alice:example.org');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { describe, expect, it, vi } from 'vitest';

import { applyMediaEditCaptionAndReply } from '../edit-room-message-media-caption';
import {
MATRIX_CUSTOM_HTML_FORMAT,
splitRichReplyPlainBody,
} from '../rich-reply';

/** Covers `editRoomMessage` media branch in matrix-provider (empty caption + reply). */
describe('applyMediaEditCaptionAndReply', () => {
it('preserves m.in_reply_to when caption is cleared but replyToId is set', async () => {
const combined = {
msgtype: 'm.image',
body: 'photo.png',
filename: 'photo.png',
url: 'mxc://hs/a',
};
const resolveReply = vi.fn().mockResolvedValue({
eventId: '$parent:example.org',
sender: '@alice:example.org',
body: 'original',
});

const out = await applyMediaEditCaptionAndReply(
combined,
'',
'$reply:event',
resolveReply,
'photo.png',
);

expect(resolveReply).toHaveBeenCalledWith('$reply:event');
expect(out['m.relates_to']).toEqual({
'm.in_reply_to': { event_id: '$parent:example.org' },
});
expect(out.format).toBe(MATRIX_CUSTOM_HTML_FORMAT);
expect(typeof out.formatted_body).toBe('string');
expect(out.formatted_body!.length).toBeGreaterThan(0);
expect(out.body).toContain('> <@alice:example.org>');
expect(out.body).toContain('original');
/** Same path as matrix-provider: `buildRichReplyMatrixContent(..., ' ')`. */
const split = splitRichReplyPlainBody(out.body);
expect(split.reply).toBe('');
expect(split.quoted).toContain('@alice:example.org');
expect(split.quoted).toContain('original');
});

it('uses filename fallback when caption and reply are absent', async () => {
const combined = {
msgtype: 'm.image',
body: 'x',
filename: 'x',
url: 'mxc://hs/b',
};
const resolveReply = vi.fn();

const out = await applyMediaEditCaptionAndReply(
combined,
'',
undefined,
resolveReply,
'fallback.png',
);

expect(resolveReply).not.toHaveBeenCalled();
expect(out.body).toBe('fallback.png');
expect(out['m.relates_to']).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import useSWR from 'swr';
import { getLinkByMatrixUserIdAction } from '../../server/actions';

export interface UseUserPrivyIdByMatrixIdInput {
matrixUserId: string;
/** When omitted or empty, no request is made (safe for conditional resolution). */
matrixUserId?: string;
}

export const useUserPrivyIdByMatrixId = ({
Expand All @@ -19,7 +20,11 @@ export const useUserPrivyIdByMatrixId = ({
if (typeof window === 'undefined') return undefined;
return determineEnvironment(window.location.href);
}, []);
const arg = isLoadingJwt ? null : { matrixUserId, environment };
const trimmedId = matrixUserId?.trim();
const arg =
isLoadingJwt || !trimmedId
? null
: { matrixUserId: trimmedId, environment };

const { data: privyUserId, isLoading } = useSWR(
[jwt ? arg : null, 'getLinkByMatrixUserId'],
Expand Down
Loading
Loading