Commit Graph

3523 Commits

Author SHA1 Message Date
jared d39aef0aac docs: add backlog (E–K) of fixed-but-unverified items to testing guide
CI / Build & Quality Checks (push) Successful in 10m27s
CI / Trigger Desktop Build (push) Successful in 19s
Sweeps every remaining "FIXED ⚠️ UNTESTED" item from LOTUS_BUGS.md and
LOTUS_TODO.md into the testing guide, grouped by environment (mobile,
theming, calls, media/perf, accessibility/screen-reader, desktop/Tauri,
features) so each category can be verified in one pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 04:07:39 -04:00
jared 9f533b1077 docs: fix section C numbering in LOTUS_TESTING.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 04:04:42 -04:00
jared fdaba40ba9 docs: mark N4 fixed; add LOTUS_TESTING.md manual test guide
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 04:04:21 -04:00
jared caf6318a5d fix(poll): render vote buttons with folds tokens (N4)
Poll answer buttons referenced undefined CSS vars (--accent-cyan,
--accent-cyan-dim, --accent-cyan-border, --border-color) plus hardcoded
rgba()/#fff and raw rem font sizes, so they rendered unstyled on every
non-TDS theme (invisible borders, no selected/progress state).

Replace all colors with always-defined folds tokens (Primary.* for the
selected/indicator state, SurfaceVariant.* for the resting surface +
progress fill), size/spacing/radii with config.* tokens, and the
checkbox/radio glyphs + percentage/label text with folds <Text>. The
progress-bar-behind-text affordance is preserved (folds Button has no
equivalent), now theme-reactive. Merged the duplicate checkbox/radio
indicator spans into one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 04:02:13 -04:00
jared 23649d85b0 docs(bugs): mark #4 (DM/group call ringtone + in-call notify) FIXED
CI / Build & Quality Checks (push) Failing after 15m32s
CI / Trigger Desktop Build (push) Has been skipped
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:45:41 -04:00
jared c67aed01dc feat(calls): non-intrusive incoming-call banner while already in a call (#4b)
Previously a second incoming call was dropped from the UI entirely when the
user was already in a call (`!joined && callInfo`). Now, when joined to a
different call, a compact corner banner (caller avatar + name + Answer/Reject)
is shown instead of the full-screen IncomingCall overlay, with a single soft
ping (one-shot ringtone) rather than the looping ring so it doesn't talk over
the active call. The full overlay still shows when not in any call; being in
the ringing room's own call still shows nothing.

Built with folds primitives + TDS tokens (no hardcoded colors).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:45:18 -04:00
jared 66cc51d6d0 docs(bugs): update #1 (camera focus) and #4 (ringtone) statuses
CI / Build & Quality Checks (push) Successful in 10m56s
CI / Trigger Desktop Build (push) Successful in 37s
#1 documented as implemented (focusCameraParticipant + MemberGlance
"Focus camera" menu); #4 ringtone selection landed, with the remaining
active-call non-intrusive-notification work scoped and deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:38:05 -04:00
jared 4a87588435 feat(calls): selectable incoming-call ringtone (#4)
Adds a ringtoneId setting (classic | chime | soft | retro | none) so the
incoming-call ring is no longer hardcoded to call.ogg. The three synth
styles are generated in-browser via a new utils/ringtones.ts module
(mirroring the existing callSounds.ts WebAudio pattern), so no new binary
assets are bundled; 'classic' keeps the existing call.ogg clip and 'none'
is a silent, visual-only incoming-call UI.

- ringtones.ts: startRingtone() loops until stopped; previewRingtone()
  plays a single non-looping preview and auto-cancels the prior preview.
- IncomingCall: ring driven by the setting; <audio> element removed.
- Settings > Calls: Ringtone selector with on-select preview, beside the
  existing Ringtone Volume slider.
- settings.ts: persisted value whitelisted back to a known id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:25:32 -04:00
jared c0fd372529 docs(bugs): reconcile LOTUS_BUGS statuses with wave-1 fixes
CI / Build & Quality Checks (push) Successful in 10m35s
CI / Trigger Desktop Build (push) Successful in 14s
Mark items resolved by commits b7e1f89c / d2946c00 / 0394fce9 / 203568c9
as FIXED, and record the false-positives surfaced during the audit
(useMatrixEventRenderer null contract, Lobby getRoom already memoized,
RoomTimeline/RoomInput already wrapped by RoomView's ErrorBoundary).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 09:35:51 -04:00
jared 203568c967 fix(logging): redact PII from media-error console warnings
CI / Build & Quality Checks (push) Successful in 11m5s
CI / Trigger Desktop Build (push) Successful in 17s
msgContent media load/thumbnail failures now log only the error name+message,
not the full error/event object that may carry content data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:22:01 -04:00
jared 0394fce929 feat(calls): EC iframe load watchdog + recovery UI; avatar decorations on call tiles
- CallEmbed: 25s load watchdog that fails fast on iframe error / preparing-error /
  timeout instead of hanging on a permanent spinner; additive onLoadError API,
  cleared on ready/capabilities/joined.
- CallView: user-visible "call failed to load" overlay with Retry/Leave (folds +
  tokens) via a new useCallLoadError hook.
- CallMemberCard: wrap the participant avatar in AvatarDecoration so decorations
  render in the call roster (the tile rendered UserAvatar bare while member lists
  already wrapped it).

Addresses LOTUS_BUGS item 3 (avatar decorations in calls) and EC iframe failure monitoring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:22:01 -04:00
jared d2946c00ce fix(matrix): upload retry/backoff, robust MatrixError, typed m.direct, reliable presence on unload
- uploadContent: bounded retry (max 3) reusing rateLimitedActions' capped
  exponential backoff; retries only transient failures (network/429/5xx), never 4xx.
- Robust MatrixError construction from UploadResponse / unknown error shapes.
- addRoomIdToMDirect/removeRoomIdFromMDirect: drop `as any`, use typed
  EventType.Direct + MDirectContent.
- usePresenceUpdater: keep fetch({keepalive}) for the unload offline-presence
  update (sendBeacon can't set the auth header) and log redacted warnings instead
  of silently swallowing presence errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:22:00 -04:00
jared b7e1f89c1d perf(room): memoize timeline/composer handlers and emoji-pack room lookups
- RoomTimeline: wrap jump-to-latest/unread + mark-as-read handlers in useCallback
  (the handlers passed to memoized message children were already memoized).
- RoomInput: wrap file/upload/emoji/sticker/location callbacks in useCallback so
  the editor and toolbar don't re-render needlessly.
- EmojiBoard: hoist repeated mx.getRoom() pack-label lookups into a useMemo'd map
  in the emoji and sticker sidebars (previously called per-render in map loops).

Behavior unchanged. (RoomTimeline/RoomInput already have ErrorBoundary wrappers
in RoomView, so no boundary added.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:22:00 -04:00
jared c0f9867218 Merge upstream v4.12.3 (Element Call 0.20.1) into lotus
CI / Build & Quality Checks (push) Successful in 10m42s
CI / Trigger Desktop Build (push) Successful in 10s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 04:11:41 -04:00
Krishan 69515e8e81 chore: release v4.12.3 (#2996)
Release v4.12.3
2026-06-22 23:58:25 +10:00
renovate[bot] 70b8d03c02 chore(deps): lock file maintenance (#2995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-22 23:42:57 +10:00
dependabot[bot] f8baf3f258 chore(deps): bump docker/build-push-action from 7.1.0 to 7.2.0 (#2967)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.1.0 to 7.2.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/bcafcacb16a39f128d818304e6c9c0c18556b85f...f9f3042f7e2789586610d6e8b85c8f03e5195baf)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 21:41:36 +10:00
dependabot[bot] b76f1a6009 chore(deps): bump docker/metadata-action from 5.10.0 to 6.1.0 (#2966)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.10.0 to 6.1.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/c299e40c65443455700f0fdfc63efafe5b349051...80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 21:41:17 +10:00
dependabot[bot] 7841d1dc87 chore(deps): bump docker/login-action from 4.1.0 to 4.2.0 (#2965)
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/4907a6ddec9925e35a0a9e82d7399ccc52663121...650006c6eb7dba73a995cc03b0b2d7f5ca915bee)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 21:40:47 +10:00
dependabot[bot] ee421a71e2 chore(deps): bump docker/setup-buildx-action from 3.12.0 to 4.1.0 (#2964)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.12.0 to 4.1.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/8d2750c68a42422c14e847fe6c8ac0403b4cbd6f...d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-20 21:35:21 +10:00
Krishan e37971456a chore(deps): update dependency element-call from 0.19.1 to v0.20.1 (#2992)
* chore(deps): update dependency @element-hq/element-call-embedded from 0.19.1 to v0.20.1

* fix reaction button click

* fix call settings button query

* fix screenshare and spotlight button state update

* fix incomming call text alignment

* prevent displaying call notification for voice rooms

* disable mic/video icon when toggle in progress

* update matrix-js-sdk

---------

Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
2026-06-20 21:20:25 +10:00
jared 79f8fabb1b fix(ui): GIF picker surface tokens + background swatch chrome (N8, N70)
CI / Build & Quality Checks (push) Successful in 10m46s
CI / Trigger Desktop Build (push) Successful in 6s
- N8: GifPicker non-TDS container used undefined var(--bg-surface) + raw
  rgba/12px/boxShadow. Switch to folds tokens (color.Surface.Container,
  config.radii.R400, color.Surface.ContainerLine, color.Other.Shadow).
  TDS branch keeps its --lt-* glow chrome.
- N70: ChatBgGrid/SeasonalBgGrid swatch buttons moved chrome (radius, border,
  hover, keyboard :focus-visible ring, selected via data-selected) into shared
  BgSwatch.css.ts using design tokens; only per-swatch size + live preview
  background stay inline (custom preview tiles, not MenuItem/Chip candidates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 21:01:57 -04:00
jared dfd2c9c49e fix(ui): mention color picker, send-animation conflict, DM virtualizer (N69,N10,N22)
- N69: @mention highlight color now uses HexColorPickerPopOut + react-colorful
  HexColorPicker behind a folds Button (color swatch); built-in onRemove
  replaces the separate Reset, dropping the OS-native <input type="color">
- N10: mentionPulseKeyframes animates only box-shadow (dropped the imperceptible
  scale(1.003)) so it no longer fights MsgAppearClass over `transform` on
  self-sent @mention messages
- N22: Direct.tsx virtualizer estimateSize 38 -> 52 (two-line DM row height) to
  avoid the initial-render jump before measureElement corrects each row

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:54:32 -04:00
jared 5470e25bb0 fix(ui): report category dropdown uses folds menu, not native select (N56)
CI / Build & Quality Checks (push) Successful in 10m52s
CI / Trigger Desktop Build (push) Successful in 7s
Extract a shared ReportCategorySelect: folds Button trigger + PopOut +
FocusTrap + Menu + MenuItem (escape + arrow-key nav, like OrderButton),
replacing the OS-styled native <select> in both ReportRoomModal and
ReportUserModal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:41:46 -04:00
jared 374d6dc396 fix(ui): note join/leave sound preview affordance (N82)
Selecting a join/leave sound auto-plays a preview, but nothing communicated
that. Add it to the SettingTile description.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:37:55 -04:00
jared d0715774a8 fix(ui): ScheduleMessageModal + PollCreator use folds Dialog shell (N15, N29)
Both rendered as <Box as="form" role="dialog"> with manually assembled
background/borderRadius(R400)/boxShadow. Switch to <Dialog as="form"
variant="Surface"> so the surface comes from the design system (R300 radius),
matching the other message-action dialogs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:32:16 -04:00
jared 6f544e2b1f fix(ui): report modals use folds Dialog shell (N63)
ReportRoomModal/ReportUserModal rendered as <Box as="form" role="dialog">
with inline background/borderRadius(R400)/boxShadow. Switch both to
<Dialog as="form" variant="Surface"> so the surface (background, R300 radius,
shadow) comes from the design system, matching MessageReportItem and every
other message-action modal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:26:17 -04:00
jared e713d47319 fix(ui): forward-dialog header, notification presets, syntax-highlight tokens
CI / Build & Quality Checks (push) Successful in 10m39s
CI / Trigger Desktop Build (push) Successful in 8s
- N14 ForwardMessageDialog: add folds <Header> with title + close IconButton
  (was closeable only by clicking outside)
- N20 Notification presets: bare <button> with undefined --border-interactive-
  normal / --bg-surface-low vars -> folds <Button variant="Secondary" fill="Soft">
- N68 syntaxHighlight tokenStyle: use the theme-aware --prism-* variable family
  (keyword/selector/boolean/atrule/comment) instead of TDS-only --lt-accent-*
  vars with dark-only Monokai fallbacks; comment uses --prism-comment not opacity

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 18:22:25 -04:00
jared b361d43088 fix(ui): native inputs/checkboxes, QR fallback, focus + report modal cleanup
- N23 RoomServerACL: raw text input -> folds Input; raw checkbox -> folds Checkbox
- N24 PolicyListViewer: raw room-id input -> folds Input (Critical variant on error)
- N25 ExportRoomHistory: raw <input type="date"> x2 -> folds Input
- N26 RoomShareInvite: QR <img> gets loading="lazy" + onError fallback card
  ("QR code unavailable") instead of a broken-image icon
- N27 GifPicker: FocusTrap returnFocusOnDeactivate:false (matches EmojiBoard)
- N76 Report modals: drop redundant Cancel button (dismiss via header x /
  click-outside, like MessageReportItem)
- N5 ReadReceiptAvatars: hover/focus moved to co-located css :hover/:focus-visible
  (removed JS onMouseEnter/Leave .style mutation)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 18:12:25 -04:00
jared a33d28a7ae style: apply Prettier formatting to remaining files
Pure formatting reflows (multi-line wrapping of long lines/imports/tables);
no behavior change. Clears the working tree of pending prettier diffs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 16:41:57 -04:00
jared 4a4dede105 fix(media-gallery): dock as a flex sibling like MembersDrawer (was floating)
CI / Build & Quality Checks (push) Successful in 10m34s
CI / Trigger Desktop Build (push) Successful in 6s
The real reason the gallery didn't look or function like the Members drawer
or Saved Messages: it was a position:fixed overlay floating over the timeline,
mounted from RoomViewHeader. Now it docks into the room layout row exactly like
MembersDrawer.

- new mediaGalleryAtom (mirrors bookmarksPanelAtom) holds the open state
- RoomViewHeader toggles the atom instead of local useState and no longer
  renders the panel
- Room.tsx renders <MediaGallery> as a flex sibling of the timeline with a
  vertical Line separator on desktop and key={room.roomId} to reset per room
- MediaGallery.css: static width on desktop, position:fixed inset:0 full-screen
  only on mobile (identical strategy to MembersDrawer.css); root Box shrink="No"

The panel now shares the row with the timeline instead of overlapping it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 16:14:50 -04:00
jared b818d3fc5a refactor(media-gallery): redesign chrome to match native folds conventions
CI / Build & Quality Checks (push) Successful in 10m25s
CI / Trigger Desktop Build (push) Successful in 11s
The Media Gallery panel worked but didn't look like a first-party Cinny
drawer. Redesign the chrome to match MembersDrawer / Saved Messages and the
PolicyListViewer tab precedent:

- panel + header: Surface -> Background variant; header uses Text size="H4"
  and a plain close IconButton (dropped the bespoke tooltip-wrapped button)
- tabs: moved into a bordered toolbar strip; adopt the repo's
  variant={active?'Primary':'Secondary'} fill={active?'Solid':'Soft'} pattern
  and show per-tab counts (Images (N) / Videos (N) / Files (N))
- month grouping: replaced the centered "lines + label" divider with a
  left-aligned group label (the Cinny group-label pattern)
- thumbnail tiles: hover/focus border + caption overlay are now CSS-driven
  (:hover / :focus-visible) instead of React state, and live in
  MediaGallery.css.ts; grid + file rows tokenized
- caption overlay also reveals on keyboard focus (a11y)

All styling consolidated into MediaGallery.css.ts; no inline grid/tile styles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:07:51 -04:00
jared cf839e7345 fix(ui): avatar-decoration reliability, Saved Messages + Media Gallery redesign
CI / Build & Quality Checks (push) Successful in 10m30s
CI / Trigger Desktop Build (push) Successful in 9s
Avatar decorations: useAvatarDecoration cached ALL profile-field fetch
failures as "no decoration" permanently for the session. The member list
and timeline mount many avatars at once, so one rate-limited (429) burst
would wipe everyone's decoration until a full reload. Now only a genuine
404 (field unset) is cached; transient errors retry on the next mount.

Saved Messages panel — full redesign to match the canonical MembersDrawer:
- co-located BookmarksPanel.css.ts: toRem(266) + max-width:750px full-screen
  media query, replacing the old position:absolute/zIndex:100 mobile "modal"
  that had no backdrop or escape
- variant="Background" header; room avatars on each item (was a generic hash)
- priority tokens replace all raw opacity hacks; 3px borderLeft accent removed
- Escape-to-close; multi-line preview is now a proper folds Button (N38)

Media Gallery (N12): moved fixed positioning + width into MediaGallery.css.ts
using toRem(320) + a full-screen media query; border/header use config tokens;
added Escape-to-close on the panel (previously only the lightbox handled it).

Presence (SettingsTab / useUserPresence):
- N16: wrap presence-dot trigger in TooltipProvider; replace undefined
  --bg-surface with color.Background.Container
- N17: add escapeDeactivates + isKeyForward/isKeyBackward to the FocusTrap
- N19: align reader labels (usePresenceLabel) to the setter vocabulary
  (Online/Idle/Offline) so a chosen status matches the tooltip others see

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:21:29 -04:00
jared c54cb126ff fix(ui): settings modal sizing regression + 17 more folds audit findings
CI / Build & Quality Checks (push) Successful in 10m47s
CI / Trigger Desktop Build (push) Successful in 5s
Fix settings modal regression: Modal500 was wrapped in useModalStyle(560),
forcing maxWidth 560px and squishing the two-pane Settings layout (folds
size="500" is ~50rem). Restore desktop width to the folds recipe while
keeping mobile fullscreen.

N-series fixes:
- N13 ScheduledMessagesTray header: <Box as="button"> -> folds <Button>
- N28 composer char counter: drop undefined --tc-surface-low + opacity,
  use priority="300" and config.space token
- N31 collapsible "Read more" toggle: padded <Button> -> flush inline-button
  pattern matching (edited) link
- N41 UserPrivateNotes "Saving..." now shows a folds <Spinner>
- N43 Night Light slider: add accentColor; label opacity -> priority
- N44 mention-highlight Reset: bare <button> -> folds <Button> (drops
  undefined --border-interactive-normal); Boot button kept (TDS-only)
- N45 SelectTheme trigger variant -> Secondary to match SettingsSelect
- N49 RoomInsights StatTile emoji -> folds <Icon> (Photo/VideoCamera/
  Headphone/File)
- N54/N57 PiP overlay badges + fullscreen button: token discipline
  (config.radii/space, folds Text); dark scrim kept for video legibility
- N60 knock badge: match Pinned Messages pattern (no wrapper div, toRem
  insets, no hardcoded size overrides)
- N62 unverified-device banner: 3px left-accent -> standard border via
  color.Warning.ContainerLine; drop opacity hacks
- N65 Edit History: real "Load more" pagination (accumulate next_batch,
  de-dupe by id, re-sort by ts) replacing passive text
- N66 search date fields: raw <input type="date"> -> folds <Input>
- N67 SeasonalEffect z-index 9999 -> 9997 (below Night Light + modals)
- N73 Pending Requests header uses css.MembersGroupLabel
- N74 remove raw em-sized emoji <span> in RoomNavItem name
- N85/N86 RemindMeDialog: <Box role="dialog"> -> folds <Dialog>; preset
  MenuItems -> Buttons (fixes invalid menuitem-in-dialog ARIA)

Document deliberate WON'T FIX rationale for N9, N51, N61, N71, N75, N77.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:15:35 -04:00
jared 8dc4c4d072 fix(ui): resolve 29 native UI/UX inconsistencies from folds design audit
CI / Build & Quality Checks (push) Successful in 10m25s
CI / Trigger Desktop Build (push) Successful in 6s
Fixes N1–N94 findings from LOTUS_BUGS.md audit pass. Key changes:

- ProfileDecoration: raw <button> → folds <Button> for save/remove; remove
  undefined --accent-cyan var
- UserRoomProfile: textarea border uses color.SurfaceVariant.ContainerLine
  and config tokens instead of undefined --border-interactive var
- LotusToastContainer: z-index raised from 9997 → 10001 so toasts appear
  above Night Light overlay (9998) and modals (9999)
- Message.tsx: DeliveryStatus replaces Unicode glyphs with Icon components;
  MessageQuickReactions returns null instead of <span />; forward menu item
  gets correct size="100" on after icon
- AudioContent: speed chip variant/radii now matches Play chip (Secondary/300)
- ReadReceiptAvatars: pill border/radius/padding → folds config tokens;
  remove dead receipt-pill-btn className
- EventReaders: Header size 600→500; close button gets radii="300";
  borderBottom shorthand → borderBottomWidth token; remove raw fontSize
- General.tsx: selected background/seasonal picker border uses
  color.Primary.Main instead of color.Critical.Main (error red)
- RoomInsights: SectionHeader drops textTransform/letterSpacing/opacity;
  chart borderRadius → config tokens; remove raw fontSize:9;
  warning banner → SequenceCard
- RoomProfile.tsx: formatting toolbar raw <button> → folds <Button>;
  topic read-mode renders formatted_body via sanitizeCustomHtml
- MsgTypeRenderers: location Open button Chip→Button; opacity:0.65→priority
- UploadCardRenderer: caption raw <input> → folds <Input>
- VoiceMessageRecorder: replace undefined --bg-surface-variant/--tc-*
  vars with color.* tokens; replace bare <audio controls> with
  IconButton play/pause toggle
- App.tsx: mention highlight uses WCAG 2.1 relative luminance (gamma
  linearization) instead of simplified approximation; border now rgba
  semi-transparent instead of same color as background
- RoomNavItem: Mute MenuItem icon moved to before prop
- SearchFilters: HasLink chip variant="Success" outlined to match filter bar
- RoomViewHeader: Server Notice chip radii Pill→300; fix jotai import order
- Fix ESLint import/order errors in DeviceVerificationSetup, RoomTopicViewer,
  MediaGallery, and RoomViewHeader

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:46:19 -04:00
jared 9742eaea28 feat(call): full-screen camera broadcast parity
CI / Build & Quality Checks (push) Successful in 10m22s
CI / Trigger Desktop Build (push) Successful in 7s
Four changes to match screenshare full-screen UX for camera feeds:

1. Fullscreen button always visible
   CallControls.tsx: remove `screenshare &&` gate — the ⛶ fullscreen
   button now appears in camera-only calls, not just during screenshare.

2. Per-participant camera focus (CallControl.focusCameraParticipant)
   Finds the target's video tile in the EC iframe DOM via:
     [data-testid="videoTile"] / [data-video-fit]
     closest ancestor of [aria-label="${userId}"]
   Enables spotlight mode if not already active, then clicks the tile
   so EC's internal focus handler runs. Falls back gracefully if the
   tile is not in the DOM (camera off).

3. MemberGlance participant popup
   Clicking a participant avatar in the call status bar now shows a
   small menu: "Focus camera" (calls focusCameraParticipant) and
   "View profile" (existing behaviour). Previously it opened the
   profile immediately with no way to focus the camera.

4. PiP fullscreen button
   A ⛶/⊡ icon button appears in the PiP overlay top-right area,
   letting users go fullscreen directly from PiP mode without
   navigating back to the call room first.

UNTESTED — requires a real multi-participant call to verify tile
clicking behaviour and fullscreen transitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:39:58 -04:00
jared fb66c0ed90 fix(privacy): sanitize console error/warn to prevent PII leakage
CI / Build & Quality Checks (push) Successful in 10m39s
CI / Trigger Desktop Build (push) Successful in 7s
Replace raw error object logging (which may contain Matrix event
payloads, user IDs, or message bodies) with e.message-only strings
in three files:
- CallEmbed.ts: state update and event widget feed errors
- msgContent.ts: image/video element load failures and thumb errors
- RoomInput.tsx: GIF send failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:24:52 -04:00
jared 9deeef6e8d fix(pip): correctly identify whose mic is muted in PiP overlay
Previously PipMuteOverlay fired on useRemoteAllMuted (any remote
muted) and rendered in the bottom-left corner — the conventional
position for local-user mic status — causing users to think their
own mic was muted when it wasn't.

Fix: split into two distinct indicators
- Bottom-left: local mic muted only (from useCallControlState),
  labelled "You" so attribution is unambiguous
- Top-right: "All muted" warning (warning color, not critical) when
  all remote participants are muted

UNTESTED — verify in a real call at chat.lotusguild.org.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:23:12 -04:00
jared e2b957b6bd fix: resilience, speaker animation, CSS variable fixes
CI / Build & Quality Checks (push) Successful in 10m39s
CI / Trigger Desktop Build (push) Successful in 6s
- Wrap RoomTimeline in ErrorBoundary — a single bad event no longer
  crashes the entire timeline; shows a graceful "Timeline unavailable"
  message instead
- Wrap RoomInput in ErrorBoundary — composer crashes show a fallback
  placeholder rather than a blank white section
- Animate SpeakerAvatarOutline with a 1.2s pulse keyframe so it's
  visually distinct from a static ring; respects prefers-reduced-motion
- Fix var(--border-surface-variant) undefined variable in UserRoomProfile
  device session rows; replaced with color.SurfaceVariant.ContainerLine

UNTESTED — verify at chat.lotusguild.org post-deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:19:54 -04:00
jared abf15391f6 feat(search): sender picker, date presets, has:link filter
- Add SelectSenderButton: clickable people picker for the From filter
  replacing the text-only from:@user syntax
- Add date preset shortcuts in DateRangeButton (Today, Last week,
  Last month, Last year) for one-click range selection
- Add Has link chip backed by Matrix contains_url API filter; toggle
  removes cleanly with X badge
- Wire containsUrl through URL params, useMessageSearch hook, and
  SearchFilters props

UNTESTED — verify at chat.lotusguild.org post-deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:14:42 -04:00
jared 44e36f7dd2 fix(search): use proper folds color tokens for date range picker (P4-9)
Replaces undefined --border-surface-variant CSS variable with
color.SurfaceVariant.ContainerLine from folds, and --bg-surface-variant
with color.SurfaceVariant.Container. Both are valid theme tokens
that adapt to light/dark mode correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:03:21 -04:00
jared a77c4b6db5 feat(calls): configurable ringtone volume in Settings (Bug #4 partial)
CI / Build & Quality Checks (push) Successful in 10m29s
CI / Trigger Desktop Build (push) Successful in 15s
Adds 'Ringtone Volume' slider (0–100, default 70%) to Settings → Calls.
The IncomingCall audio element reads the setting and applies it as
audioElement.volume before playing, replacing the implicit browser
default of 1.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:56:04 -04:00
jared cb3d2c40e5 fix(a11y): descriptive aria-label on reaction buttons (P3-4)
Reaction.tsx now computes aria-label='{shortcode} reaction, N people'
using getShortcodeFor so screen readers announce emoji name and count
instead of an ambiguous button. Custom (mxc://) emoji falls back to
'custom emoji reaction'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:50:19 -04:00
jared f50e14d7a5 fix(a11y): label room page header with room name for screen readers (P3-4)
PageHeader now exposes aria-label="{room name} room header" so screen
reader users know which room's header they are navigating.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:46:30 -04:00
jared 0ead519a80 fix(a11y): add role=log aria-live to message timeline (P3-4)
Screen readers now announce new messages politely via role='log' +
aria-live='polite' on the message container in RoomTimeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:43:50 -04:00
jared 7d98b49a30 fix(a11y): add aria-label to exit-formatting and pin-menu buttons (P3-4)
Two icon-adjacent buttons were missing descriptive labels: the
"Exit formatting" key-symbol button in Toolbar.tsx and the "Pinned
messages" pin icon in RoomViewHeader.tsx.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:41:20 -04:00
jared f054abfbd2 fix(notifications): click navigates to specific message, not inbox
CI / Build & Quality Checks (push) Successful in 10m30s
CI / Trigger Desktop Build (push) Successful in 7s
OS notifications now show the real message body ("user: text" instead
of "New inbox notification from user"), clicking jumps directly to the
room at the triggering event, and window.focus() brings the tab to
front. Reminder toasts also link to the specific event via eventId.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:37:19 -04:00
jared 2b5c6fd606 perf(media): center-top focal point on cover-fit thumbnails (P5-6)
Adds objectPosition:'center top' to all cover-fit thumbnail surfaces so
portrait images show faces/subjects instead of the center-slice when
the 600px AttachmentBox height cap forces cropping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:33:36 -04:00
jared ffa490e767 fix(mobile): make media gallery and members panel accessible on mobile
CI / Build & Quality Checks (push) Successful in 10m30s
CI / Trigger Desktop Build (push) Successful in 9s
MediaGallery: fixed panel now goes full-width (100%) on mobile instead
of the inaccessible 320px right sidebar. Added 'Media Gallery' MenuItem
to RoomMenu (visible only on mobile) so users can open it from the
More Options (···) button.

MembersDrawer: removed ScreenSize.Desktop gate in Room.tsx so it now
renders on mobile too. CSS media query (≤750px) makes it position:fixed
inset:0 width:100% on mobile instead of the 266px desktop sidebar.
Added 'Members' MenuItem to RoomMenu for mobile access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:21:58 -04:00
jared 8ac42cdbad perf(gallery): gate tile decryption until near-viewport
Adds enabled=true param to useDecryptedMediaUrl in MediaGallery.tsx.
GalleryTile now uses useNearViewport(300px) — decryption is deferred
until the tile approaches the viewport, preventing burst of 100
concurrent decrypt/fetch calls when a pagination batch loads.

Blob revocation was already correct (no actual leak); this fixes the
load-burst performance issue. Full windowing virtualization deferred.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:16:15 -04:00