diff --git a/src/frontend/web/von_interface/static/js/components/messagePanel.js b/src/frontend/web/von_interface/static/js/components/messagePanel.js index 92a9935a..22d46826 100644 --- a/src/frontend/web/von_interface/static/js/components/messagePanel.js +++ b/src/frontend/web/von_interface/static/js/components/messagePanel.js @@ -37,6 +37,9 @@ import { renderRecommendationReviewPayload, } from './paperRecommendationUi.js'; +// Match the conversation composer’s send icon; Messages retains direct delivery semantics. +const SEND_MESSAGE_ICON = ''; + // Message panel state let _messagesContainer = null; let _messageListEl = null; @@ -443,7 +446,7 @@ function renderMessagesTabContent() {
@@ -1325,7 +1328,7 @@ function renderComposePendingUi(scope) { } button.disabled = pending || imagesBlocked(attachmentScope(scope)) || (scope === COMPOSE_SCOPE_REPLY && _exchange?.other_participant_ids?.length > 1); if (scope === COMPOSE_SCOPE_REPLY) { - button.innerHTML = ``; + button.innerHTML = pending ? '' : SEND_MESSAGE_ICON; button.setAttribute('aria-label', pending ? 'Sending message' : 'Send message'); button.title = pending ? 'Sending message' : 'Send message'; } else { diff --git a/src/frontend/web/von_interface/static/styles.css b/src/frontend/web/von_interface/static/styles.css index 0b252ee9..69df7196 100644 --- a/src/frontend/web/von_interface/static/styles.css +++ b/src/frontend/web/von_interface/static/styles.css @@ -11835,7 +11835,8 @@ body[data-active-tab="settingsTab"] .tab-content-area { min-height: 44px; } -.chat-composer .composer-icon-button { +.chat-composer .composer-icon-button, +#sendMessageBtn.composer-icon-button { display: inline-flex; align-items: center; justify-content: center; @@ -18656,7 +18657,8 @@ body.settings-body { .chat-composer-more-actions { display: block; position: static; grid-column: 1 / -1; grid-row: 2; } .chat-composer #sendButton { grid-column: 3; grid-row: 1; width: 44px; min-width: 44px; } .chat-composer #sendButton[data-has-draft="false"]:not([aria-busy="true"]) { display: none; } - .chat-composer .composer-icon-button svg { width: 20px; height: 20px; } + .chat-composer .composer-icon-button svg, + #sendMessageBtn.composer-icon-button svg { width: 20px; height: 20px; } .chat-composer-more-actions > summary { position: absolute; top: 6px; left: 6px; width: 44px; height: 44px; justify-content: center; diff --git a/tests/browser/messageUnreadNavigation.cjs b/tests/browser/messageUnreadNavigation.cjs index 9ec40b2a..a73b6e4f 100644 --- a/tests/browser/messageUnreadNavigation.cjs +++ b/tests/browser/messageUnreadNavigation.cjs @@ -74,9 +74,10 @@ const server = http.createServer(async (req, res) => { assert.equal(await content.evaluate(el => el.scrollTop), 0, 'default loading preserves the top'); const send = page.getByRole('button', { name: 'Send message', exact: true }); const input = page.locator('#messageInput'); - await input.fill('A reply from the down-arrow control.'); + await input.fill('A reply from the up-arrow control.'); await expect(send).toBeVisible(); - await expect(send).toHaveText('↓'); + await expect(send.locator('svg path')).toHaveAttribute('d', await page.locator('#sendButton svg path').getAttribute('d')); + assert.equal(await send.evaluate(el => getComputedStyle(el).borderRadius), '50%'); const latest = page.getByRole('button', { name: 'Scroll to latest message', exact: true }); await expect(latest).toHaveText('Latest message'); const box = await send.boundingBox(); @@ -86,7 +87,7 @@ const server = http.createServer(async (req, res) => { await latest.click(); await expect(latest).toBeHidden(); assert.equal(sent.length, 0, 'latest navigation does not send the draft'); - await expect(input).toHaveValue('A reply from the down-arrow control.'); + await expect(input).toHaveValue('A reply from the up-arrow control.'); await content.evaluate(el => { el.scrollTop = el.scrollHeight - el.clientHeight - 10; }); const bottomPosition = await content.evaluate(el => el.scrollTop); assert.ok(bottomPosition > 0, 'fixture must overflow'); @@ -133,7 +134,7 @@ const server = http.createServer(async (req, res) => { await send.click(); await expect(input).toHaveValue(''); assert.equal(sent.length, 1); - assert.equal(sent[0].content, 'A reply from the down-arrow control.'); + assert.equal(sent[0].content, 'A reply from the up-arrow control.'); assert.deepEqual(errors, []); await page.screenshot({ path: path.join(evidence, `unread-${width}.png`) }); results.push({ width, cursors: [...cursors], sendArrow: true, sendTarget: box, sends: sent.length, labelledLatestNavigation: true, defaultTopPreserved: true, backgroundPosition: bottomPosition, intermediateAnchorOffset: anchor.offset, mostRecentUnreadFocused: true, targetInViewport: true }); diff --git a/tests/frontend/messagePanelSendFailure.test.js b/tests/frontend/messagePanelSendFailure.test.js index 5d29ab67..c6b080b7 100644 --- a/tests/frontend/messagePanelSendFailure.test.js +++ b/tests/frontend/messagePanelSendFailure.test.js @@ -408,7 +408,7 @@ describe('message panel send failure recovery', () => { await flushUi(); expect(sendButton.disabled).toBe(false); - expect(sendButton.textContent).toBe('↓'); + expect(sendButton.querySelector('svg path').getAttribute('d')).toBe('M12 19V5m-7 7 7-7 7 7'); expect(sendButton.getAttribute('aria-label')).toBe('Send message'); expect(sendButton.getAttribute('aria-busy')).toBe('false'); expect(replyInput.value).toBe('Please review this once.'); @@ -429,7 +429,7 @@ describe('message panel send failure recovery', () => { expect(replyInput.value).toBe(''); expect(sendButton.disabled).toBe(false); - expect(sendButton.textContent).toBe('↓'); + expect(sendButton.querySelector('svg path').getAttribute('d')).toBe('M12 19V5m-7 7 7-7 7 7'); expect(sendButton.getAttribute('aria-label')).toBe('Send message'); });