From 79c12aee939c17702cb3ad17c64eb7394176ec66 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 23 Oct 2026 11:39:52 -0700 Subject: On a phone a live stream is laid out the way Nostrich does it: the picture edge to edge, the chat a plain full-width list under it Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4 --- src/features/messages.js | 4 ++-- src/style.css | 17 +++++++++++++++++ tools/live-stream-test.js | 7 +++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/features/messages.js b/src/features/messages.js index 030d9fa..dea454c 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -9309,7 +9309,7 @@ export function messagesFeature(ctx) { }; return h('div', { class: 'col live-page', style: 'gap:12px' }, ctx.brandHeader(!ui.pubProf && wallet.loaded), - h('div', { class: 'card col', style: 'gap:10px' }, + h('div', { class: 'card col live-main', style: 'gap:10px' }, h('div', { class: 'row gap6', style: 'align-items:center' }, mine ? h('button', { class: 'iconbtn chat-back', onClick: () => { if (confirm(t('goLiveEndAsk'))) endBroadcast(); } }, '‹') : backBtn(() => { ui.liveStream = null; }), @@ -9361,7 +9361,7 @@ export function messagesFeature(ctx) { watchersRow(w), liveSummary(ev) ? h('div', { class: 'small muted', style: 'white-space:pre-wrap;overflow-wrap:anywhere' }, liveSummary(ev).slice(0, 600)) : null, ui.liveReport && ui.liveReport.a === s.a ? liveReportPanel(ev) : null), - h('div', { class: 'card col', style: 'gap:8px' }, + h('div', { class: 'card col live-chat-card', style: 'gap:8px' }, h('div', { class: 'small muted' }, t('liveChat')), h('div', { class: 'live-chat', 'data-key': 'live-chat:' + s.a, diff --git a/src/style.css b/src/style.css index 275bb59..384b6d6 100644 --- a/src/style.css +++ b/src/style.css @@ -929,6 +929,23 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; } .live-line-body { min-width: 0; overflow-wrap: anywhere; white-space: pre-wrap; line-height: 1.35; } .live-zap .live-line-body b { color: var(--accent); } .live-chat .chat-row { padding-right: 2px; } +/* on phones the stream is the page, the way Nostrich lays one out: the + picture runs edge to edge and the chat under it is a plain full-width + list of lines, no bubbles — the card walls step outside #app's padding */ +@media (max-width: 520px) { + .live-page > .card { + margin-left: -16px; margin-right: -16px; padding: 10px 0 12px; + border-left: none; border-right: none; border-radius: 0; box-shadow: none; + } + .live-page > .live-chat-card { margin-top: 0; padding-top: 6px; border-top: none; } + .live-page > .card > :not(.live-player, .live-chat) { margin-left: 14px; margin-right: 14px; } + .live-page .live-player { border-radius: 0; } + .live-page .live-chat { padding: 10px 14px 0; max-height: 60vh; } + .live-page .live-title { font-size: 17px; } + .live-chat .chat-row { gap: 10px; } + .live-chat .chat-bubble { display: block; background: transparent; border: 0; border-radius: 0; padding: 0; font-size: 15px; } + .live-chat .chat-quote { margin-bottom: 2px; } +} .bc-preview.mirror { transform: scaleX(-1); } .bc-bar .bc-clock { font-variant-numeric: tabular-nums; font-weight: 650; } .go-live-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #e0245e; box-shadow: 0 0 0 3px rgba(224, 36, 94, .25); } diff --git a/tools/live-stream-test.js b/tools/live-stream-test.js index ef76043..2be6051 100644 --- a/tools/live-stream-test.js +++ b/tools/live-stream-test.js @@ -197,6 +197,12 @@ try { check('the host\'s face on their post wears the live ring', await waitFor((pk) => !!document.querySelector('.notes-feed .note-post .note-avatar.live-ring'), 5000)); await page.evaluate(() => document.querySelector('.notes-feed .note-post .note-avatar.live-ring').click()); check('...and a tap on it opens the stream', await waitFor(() => !!document.querySelector('.live-page'), 5000)); + const lay = await page.evaluate(() => { + const r = (q) => document.querySelector(q)?.getBoundingClientRect(); + const p = r('.live-page .live-player'), c = r('.live-page .live-chat'); + return { w: document.body.clientWidth, player: p && [p.left, p.width], chat: c && [c.left, c.width] }; + }); + check('on a phone the picture runs edge to edge and the chat is as wide', lay.player && lay.player[0] === 0 && lay.player[1] === lay.w && lay.chat && lay.chat[0] === 0 && lay.chat[1] === lay.w, JSON.stringify(lay)); await page.goBack(); await waitFor(() => !document.querySelector('.live-page') && !!document.querySelector('.media-modes'), 5000); await page.evaluate(() => document.querySelector('.media-mode[data-mode="live"]').click()); @@ -217,6 +223,7 @@ try { check('...and the zap, its amount and words', await waitText('zapped 21 sats', 5000) && await waitText('great stream', 1000)); const rowSel = '.live-message[data-key="' + chat.id + '"]'; check('comments use the group-chat bubble and shared composer', await page.$(rowSel + ' .chat-bubble') && await page.$('.live-page coinos-text#msg-draft')); + check('...drawn plain on a phone, no bubble walls', await page.evaluate((q) => { const b = document.querySelector(q); return b && getComputedStyle(b).borderStyle === 'none' && getComputedStyle(b).display === 'block'; }, rowSel + ' .chat-bubble')); check('another client’s e-only reaction arrives on the comment', await waitFor(id => document.querySelector('.live-message[data-key="'+id+'"] .chat-reacts')?.textContent.includes('👍'), 6000, chat.id)); check('per-comment zap receipts show under the bubble', await waitFor(id => document.querySelector('.live-message[data-key="'+id+'"] .zap-tally')?.textContent.includes('5'), 6000, chat.id)); await page.click(rowSel + ' .chat-bubble'); -- cgit v1.2.3