Adds 530 decorations in 19 new categories (631 total). Decorations that
contain Discord branding (the Clyde-visor helmets) are excluded.
The decorations are ~1 MB animated PNGs, so a picker that rendered every
one would pull ~590 MB while scrolling. The picker now:
- shows static 144px WebP thumbnails (~9.5 KB each, `thumbs/` on the CDN)
and loads the animated file only on hover, focus or selection, with a
fallback to the full file if a thumbnail is missing;
- mounts one category at a time behind tabs, plus a name search across
all categories.
scripts/makeDecorationThumbs.py builds the thumbnails from the busiest
frame of each animation (many start on an empty frame).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
- io.lotus.request_state: when the fork's lotus handlers (re)register
(an EC-side remount that doesn't unmount us) we re-send deafen, quality
and the focus pin, and the decoration pusher re-pushes its roster —
decorations and the pin no longer vanish for the rest of the call
(element-call#17).
- focus_participant carries the per-device media id from call_state
(speaking device preferred) so a multi-device user pins the right
device (element-call#30).
- injectAudio returns the fork's reply; when it refuses with
reason:"muted" the soundboard shows "Unmute your microphone…" instead
of playing the clip locally as if it went out (element-call#13).
All backwards compatible with the 0.25.0-lotus.1 bundle (unknown action
is acked; missing reply fields default to "played").
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
The MSC4133 io.lotus.avatar_decoration value was interpolated into the
CDN URL verbatim, letting a room member steer the path/query of a request
every viewer's browser makes. Accept only slugs present in the catalog;
anything else is treated as no decoration. Unit-tested.
Fixes#64
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
Wave-3 bug-hunt fixes (findings in LOTUS_TODO), reviewed + gate-green:
- 🔴 ACL editor [H1–H4]: block saving an empty allow-list (was a one-click
federation brick), warn on self-ban (case-insensitive glob match of
mx.getDomain() vs allow/deny), accept real globs (1.2.3.*, *.evil.*), and
gate Save behind a confirm dialog.
- 🔴 [P1] room context menu no longer acts on the wrong room after a live
reorder (key by roomId, not list index). 🔴 [P2] status writes no longer
force presence to online over Invisible/DND (shared presenceStateFromSetting).
- 🟠 [P3] timed mutes restored on boot; [P4] custom-status auto-clear now fires
(always-mounted StatusExpiryMonitor); [P5] timezone also PUT to the m.tz
profile field so it's visible to others; [H6] RoomInsights single-pass
min/max (was Math.min(...spread) stack overflow); [H7/H8] mod-log labels.
- 🟡 [P6/P7] favorites collapse+filter, [P8] charCount reset, [P9] DM preview
refresh on decrypt; theming [T-P1] lazy decorations, [T-P2] drop the redundant
always-on body animation, [T-P4] live useReducedMotion, [T-P5] decoration key.
- NATIVE-CINNY LAW: notification presets + Powers permissions use folds icons.
DEFERRED: [H5] invite-QR is fetched from api.qrserver.com (third-party leak);
local generation needs a bundled QR lib (not added). tsc/eslint/prettier clean,
build OK, 677 tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same treatment as the seasonal themes: split the 502-line chatBackground.ts
Record into one premium module per background under lotus/backgrounds/ (each
exposes a tuned dark + light ChatBgVariants), one Opus agent per background
against a shared brief. chatBackground.ts now assembles DARK/LIGHT from the
modules; getChatBg is unchanged. Carbon + Aurora are kept inline as-is (user
favorites); none stays the empty layer.
Every redesign: layered oklch palettes, seamless tiling with worked-out tile
math (integer-multiple periods; edge-wrapping inline-SVG data-URIs for
circuit/hexgrid/waves/herringbone/chevron/tactical), independently-tuned
dark+light (not a recolor), and low "felt-not-read" opacity so chat text stays
WCAG-AA legible. The 5 animated backgrounds (rain, star drift, grid pulse,
aurora flow, fireflies) each colocate a vanilla-extract keyframe .css.ts,
animate only background-position for a jump-free loop, and — since getChatBg
strips animation for reduced-motion — render a finished static frame too.
Redesigned: blueprint, stars, topographic, herringbone, crosshatch, chevron,
polka, triangles, plaid, tactical, circuit, hexgrid, waves, neon, anim-rain,
anim-stars, anim-pulse, anim-aurora, anim-fireflies.
Gates: tsc clean, ESLint clean, Prettier clean, build OK, 551 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- avatarDecorations: resolve the decoration CDN base from VITE_DECORATION_CDN at
runtime, falling back to the DECORATION_CDN literal (kept intact so the sync
script + tests still parse it). Lets a deploy repoint the CDN without a code
edit. Guarded for the tsx test runner (import.meta.env undefined there).
- LOTUS_BUGS: close N127 — the denoise dev-injection gap dissolved with the A7
cutover (no getUserMedia shim is injected anymore; denoise is in-source in the
EC fork), so there is nothing to inject in dev.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve the eslint/prettier failures from the previous commit (non-blocking
in CI, but real): drop the banned `void` operator on fire-and-forget
transport.send().catch() calls, prefix the now-unused _denoiseNativeNS
param, and run prettier on the touched files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch to @lotusguild/element-call-embedded@0.20.1-lotus.1 (our self-built
fork) and turn on the source-level features it adds:
- #1 denoise CUTOVER: in-source ML denoise (lotusDenoiseSource=1) replaces
the build-time getUserMedia shim — removed the shim injection from
vite.config.js (denoise/ assets still shipped; the processor loads them).
Survives reconnects (fixes A7).
- #2 call-state: CallEmbed consumes io.lotus.call_state; useCallSpeakers /
useRemoteAllMuted prefer it over scraping EC's DOM (DOM fallback kept;
empty payloads ignored).
- #4 focus: CallControl.focusCameraParticipant sends io.lotus.focus_participant
(works during screenshare), replacing the DOM tile-click hack.
- #5 theming: lotusTransparent=1 (native transparent background).
- #6 decorations: LotusDecorationPusher sends each member's decoration URL
via io.lotus.decorations -> rendered on in-call tiles.
#3 soundboard / #7 quality ship dormant (EC-ready; no host UI sends them yet).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- N124: denoise shim cleanup() now disconnects the noise gate AudioWorkletNode
(var-scoped, guarded), releasing the gate processor thread instead of leaking
it on every getUserMedia within a session.
- N125: denoise-status postMessage now targets the parent origin (derived from
the parentUrl widget param via new URL(...).origin, falling back to this
frame's origin) instead of broadcasting with '*'.
- N128: patch-folds.mjs fails hard (process.exit(1)) when the patch target is
missing, so an unpatched folds can't silently ship. The idempotent
"already applied" path still exits 0 (verified by re-run).
- N120: the avatar-decoration CDN URL is now single-sourced in
avatarDecorations.ts (DECORATION_CDN); syncDecorations.mjs extracts it by
regex (can't import across the build/app boundary) and fails hard if renamed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- chatBackground.ts: remove animRainGlowKeyframe and animGridBrightnessKeyframe
from LIGHT anim-rain and anim-pulse definitions (these were removed from the
import and from DARK variants in the previous session but the LIGHT variants
were missed, leaving stale references that would cause a build error)
- ProfileDecoration.tsx: increase decoration grid gap 20→36 (visual gap was
only 4px due to 8px image overflow beyond each 52×52 button), fix paddingBottom
4→8 and add paddingRight:8 to prevent edge clipping
- LOTUS_BUGS.md: correct bug #8 root cause (CSP, not lazy-loading), add
bugs #9 (grid spacing) and #10 (Windows taskbar badge)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings dropdowns (Bug #3):
- Add reusable SettingsSelect component using Menu+PopOut+FocusTrap — exact
same pattern as Message Layout, so all dropdowns look consistent
- Replace raw <select> for Seasonal Theme, UI Font, AFK Timeout, and
Join & Leave Sounds with SettingsSelect
Animated chat backgrounds bleeding onto content (Bug #6 / #7):
- Remove filter:brightness() and opacity animations from chatBackground.ts
(animRainGlowKeyframe, animGridBrightnessKeyframe, animFirefliesGlowKeyframe,
animFirefliesBlinkKeyframe). These were applied to the Page element which
caused ALL descendants (messages, composer) to flash in sync.
Also created a CSS stacking context on Page that pushed SeasonalEffect
(position:fixed; z-index:9997) behind the animated background layer.
- Only backgroundPosition / backgroundSize animations remain — safe, do not
affect descendants, and do not create stacking contexts.
- Remove now-unused animation keyframe imports from chatBackground.ts.
Voice ringing in persistent rooms (Bug #5):
- Narrow the ringing condition from (Invite|Knock|Restricted) to only Invite,
matching exactly the rooms where the call button is visible.
- Add room.isCallRoom() early-exit so m.join_rule:call rooms never ring.
Avatar decoration images not loading (Bug #8):
- Change loading="lazy" → loading="eager" in DecorationPreviewCell.
Lazy loading does not reliably trigger for images inside nested overflow
scroll containers (the settings panel scroll area), so images never loaded.
Docs: LOTUS_BUGS.md updated with root cause and resolution for all 5 new bugs.
Docs: LOTUS_TODO.md adds P5-35/P5-36 (deferred desktop notification/jump list).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Point DECORATION_CDN at Lotus Nextcloud WebDAV share instead of external
avatardecoration.com; all 99 APNG files are self-hosted and served via
direct DAV URL (no CORS issue for <img> elements)
- Add onError handler to AvatarDecoration.tsx to silently hide the overlay
if a file is missing or the CDN is unreachable
- Rewrite scripts/syncDecorations.mjs: now sends HTTP HEAD requests to the
live Nextcloud CDN (batches of 16 in parallel) and removes catalog entries
for files that return non-2xx; empty categories are pruned automatically.
Workflow: delete files from Nextcloud → run `npm run sync:decorations` →
commit the updated avatarDecorations.ts. No local files needed.
- Add public/decorations/ to .gitignore; delete the 85 MB local APNG cache
that was downloaded during development (files live on Nextcloud now)
- Add sync:decorations script to package.json
- Update LOTUS_FEATURES.md, LOTUS_TODO.md (P5-13 + P5-14 ✓), README.md
with avatar decoration documentation and catalog sync workflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
256×256 APNG overlays from avatardecoration.com (open CORS CDN).
Stored in the user's Matrix profile as io.lotus.avatar_decoration via
MSC4133 so all Lotus Chat users see each other's decorations.
- avatarDecorations.ts: curated catalog of 110 original-IP decorations
across 9 categories (Gaming, Cyber, Space, Fantasy, Elements,
Japanese, Nature, Spooky, Cozy)
- useAvatarDecoration: per-user profile fetch with module-level cache
and in-flight deduplication so concurrent renders for the same userId
share one HTTP request
- AvatarDecoration: position:relative wrapper that overlays the APNG
8px beyond the avatar on all sides; renders nothing when no decoration
is set (zero cost for undecorated users)
- ProfileDecoration: scrollable grid picker in Settings → Profile,
grouped by category with live preview; Save button appears only when
the selection differs from what's saved
- Applied at all five avatar display sites: message timeline, members
drawer, knock list, @mention autocomplete, notifications inbox
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 11 CSS-only seasonal overlays (Halloween, Christmas, New Year, Autumn,
April Fool's, Lunar New Year, Valentine's Day, St. Patrick's Day, Earth Day,
Deep Space, Retro Arcade) with date-based auto-detection and a manual override
dropdown in Settings → Appearance → Seasonal Theme. All themes respect
prefers-reduced-motion. SeasonalEffect mounts at z-index 9997 in App.tsx.
Also rewrites all 5 animated chat background keyframes for smoother, more
organic motion: Digital Rain gains a phosphor glow flicker; Star Drift now
loops each layer by exactly its own tile size (no more seam); Grid Pulse adds
an independent brightness oscillation at a prime period; Aurora Flow drives
all four gradient layers through distinct paths; Fireflies adds glow-pulse and
opacity-blink animations at prime periods for unsynchronised bioluminescence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ESLint: UploadCardRenderer.tsx had two separate imports from
../../utils/matrix — merged tryDeleteMxcContent into the existing
import statement. Removed now-unnecessary eslint-disable directive from
chatBackground.ts (the _anim prefix already suppresses the rule).
Glassmorphism: the Scroll inside SidebarNav had variant="Background"
which set a solid backgroundColor on the entire scrollable area,
completely hiding the sidebar's semi-transparent glass + backdrop-filter.
Fix: pass variant={undefined} when glassmorphismSidebar is on so the
inner scroll area is transparent and the blur effect is visible through
it. The document.body background (set by the previous useEffect fix)
now shows through the frosted glass as intended.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5 new CSS-only animated backgrounds in the chat background picker:
- Digital Rain: two-layer vertical stripe scroll with parallax (wide
stripes at 8s, narrow at 4s via single keyframe with split positions)
- Star Drift: three-layer radial-gradient star field drifting diagonally
- Grid Pulse: neon grid lines that expand/contract (backgroundSize keyframe)
- Aurora Flow: large radial gradient bands sweeping across 200% canvas
- Fireflies: three layers of glowing dots drifting across the viewport
All use vanilla-extract keyframes (GPU-composited transforms/positions,
no canvas, no JS timers). prefers-reduced-motion is respected in
getChatBg() by stripping the animation property at call time. A "Pause
Background Animations" toggle in Settings → Appearance provides an
in-app override for the same purpose.
BG labels de-duplicated ("Digital Rain", "Star Drift", "Aurora Flow")
to avoid the duplicate "Stars" and "Aurora" entries that had appeared.
LIGHT anim-fireflies background corrected from near-black #0a0a10 to
warm white #fffdf0. Four unused keyframe exports removed from
Animations.css.ts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prettier: auto-formatted 103 files to fix baseline. Prettier check in CI
is now a hard gate (removed continue-on-error).
Brotli: installed libnginx-mod-http-brotli-filter/static. Enabled in nginx
with brotli_static on for pre-compressed assets and comp_level 6.
Sentry releases: deploy script now exports VITE_APP_VERSION=<git-short-sha>
before building so each Sentry release maps to an exact commit.
CI also passes github.sha as VITE_APP_VERSION.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WelcomePage: use official Lotus.png instead of generated SVG
- Hex Grid background: proper pointy-top hexagons via SVG data URI (was
just triangles from linear-gradient trick)
- Boot sequence: Matrix-specific messages (TLS cert, E2EE Olm/Megolm,
cross-signing, media proxy, /help hint)
- Terminal mode CSS: nav right border, header bottom glow, kbd TDS key
style, abbr cyan underline, time amber color, img hover cyan outline,
explicit body color anchor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>