7048 Commits
Author SHA1 Message Date
Lotus CIandClaude Opus 4.8 e36aef8aa3 fix(lotus): in-call mobile UI fixes (EC audit wave-2)
CI / Build embedded bundle (push) Successful in 44s
CI / Publish to Gitea npm registry (push) Has been skipped
3-agent survey of the in-call UI at phone width + 2-agent review of the
staged diff. All changes are mobile-gated CSS (mobile-first @media on
Compound tokens), so desktop rendering is provably unchanged.

- CallFooter: wrap the control row and tighten gap/padding at <=500px so
  the buttons (incl. the destructive hangup) can never be clipped by the
  grid's overflow-x:hidden. A loudspeaker button was added to the bar
  without a shed rule, pushing 6-7 lg buttons past the viewport at
  320-500px.
- OneOnOnePortraitLayout: give the 1:1 self-view the same safe-area-aware
  inset as SpotlightExpandedLayout so it clears the home indicator /
  floating footer (was a bare 16px inset). Phone-only layout.
- GridTile: enlarge the always-visible camera-flip control to a 44px touch
  target on coarse pointers (was ~28px).
- ReactionToggleButton: enlarge reaction-picker emoji buttons to 44px at
  <=420px (were ~32px); five still fit a 360px drawer row.
- Tabs: scroll the horizontal tab row on the inline axis (overflow-x) so
  the Settings tabs don't clip in a phone drawer.
- SpotlightLandscapeLayout: shrink the 180px filmstrip rail to 132px on
  short landscape phones (max-height:400px) so the spotlight isn't a sliver.

Gates: tsc 0, prettier clean, affected vitest pass. No TS changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 01:08:43 -04:00
Lotus CIandClaude Opus 4.8 0ffe247929 fix(lotus): Wave-1 audit fixes (EC1–EC6)
CI / Build embedded bundle (push) Successful in 1m0s
CI / Publish to Gitea npm registry (push) Has been skipped
- EC1: lotusQuality — track + clearTimeout the 500ms settle re-apply per room
  (was leaking a timer that fired on torn-down rooms).
- EC2/EC3: lotusQuality + lotusAudioInject drive off vm.allConnections$ instead
  of the remote-gated livekitRoomItems$ (were no-ops when alone), matching
  lotusDenoise.
- EC4: lotusDecorations resets its roster to {} on teardown so a decoration from
  a previous call can't render on a shared user in the next one.
- EC5: hoisted a stable useSyncExternalStore subscribe fn (was re-subscribing
  every tile render).
- EC6: lotusFocus only sets the spotlight when the userId field is present
  (a partial payload no longer clears the pin).

tsc clean. Needs a republish to ship.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 20:13:01 -04:00
Lotus CIandClaude Opus 4.8 02666c0c04 feat(lotus): io.lotus.set_deafen action — deafen remote audio at the source
CI / Build embedded bundle (push) Successful in 39s
CI / Publish to Gitea npm registry (push) Has been skipped
New toWidget action { deafened, screenshareAudioMuted } that sets each remote
RemoteParticipant.setVolume per source (Microphone + ScreenShareAudio), applied
to existing participants + re-applied to late joiners via
RoomEvent.ParticipantConnected (subscribed through vm.livekitRoomItems$). Closure-
scoped state, matching the sibling lotus modules; the cinny host re-sends on join
so a fresh call never inherits stale deafen state.

Replaces cinny's brittle iframe-DOM <audio>.muted hack (which broke on EC
re-render / late tracks). Folded into unpublished 0.20.1-lotus.2.

Note: injected/soundboard audio (Track.Source.Unknown) is not silenced — the
livekit-client setVolume type only accepts Microphone|ScreenShareAudio.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:12:08 -04:00
Lotus CIandClaude Opus 4.8 d71d8d6799 chore(lotus): fix stale denoise flag docs + prep 0.20.1-lotus.2
CI / Build embedded bundle (push) Successful in 40s
CI / Publish to Gitea npm registry (push) Has been skipped
The in-source ML denoiser is gated on lotusDenoiseSource (see lotusDenoise.ts
gate), not the legacy build-time shim flag lotusDenoise=ml. Correct the two
stale references (lotusDenoise.ts JSDoc + InCallView.tsx comment) to
lotusDenoiseSource=1.

Bump the embedded package to 0.20.1-lotus.2 for the next publish and update the
CI comment: the checked-in version now tracks the intended release, while a
pushed tag still wins (npm version "$TAG" overwrites at publish time).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:56:28 -04:00
Lotus CIandClaude Opus 4.8 98fbdbd5cf fix(lotus-audio-inject): close double-publish window on rapid inject actions
playInjectedClip only registered its cleanup (and thus became abortable by a
later clip's replace-mode loop) AFTER publishing. Two inject actions fired in
quick succession could both pass their fetch/decode/publish awaits before
either was registered, so both tracks got published.

Register a synchronous placeholder abort BEFORE the first await: it aborts the
in-flight fetch and flips an `aborted` flag checked after every await, so a
newer clip cancels the older one during the vulnerable window. The real
cleanup replaces the placeholder once the track is live, and if we were
superseded mid-publish we tear the just-published track down immediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:56:13 -04:00
Lotus CIandClaude Opus 4.8 bb3fb7e573 refactor(lotus-spotlight): extract manual-override into src/lotus/lotusSpotlight.ts
Remove the rebase hazard from CallViewModel: upstream's spotlightSpeaker$
auto-selection had been renamed to autoSpotlightSpeaker$ and an inline
manual-override + screenshare-coexistence block was spliced into
spotlightAndPip$. Both diverge from upstream and would conflict on every
rebase.

Restore spotlightSpeaker$ to its byte-for-byte upstream form and move the
[lotus #4] override into a pure wrapper, overrideSpotlight$(), invoked at a
single call point in spotlightAndPip$. Behaviour is unchanged: identical to
upstream while manualSpotlightUserId$ is null (the default), and preserves the
"pin a participant" and "focus camera during screenshare" (#4 / A5) rules when
the host sends io.lotus.focus_participant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:56:03 -04:00
Lotus CIandClaude Fable 5 0aef1c5fe2 feat(lotus-audio-inject): one soundboard clip at a time (replace mode)
CI / Build embedded bundle (push) Successful in 35s
CI / Publish to Gitea npm registry (push) Has been skipped
playInjectedClip now stops any in-flight clip (via the existing idempotent
cleanup) before starting a new one, so rapid taps replace rather than
overlap/stack — no track leak. The host also debounces the button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 23:21:50 -04:00
Lotus CIandClaude Opus 4.8 940d71da92 feat(lotus-denoise): AGC off for ML tier + init/build leak hardening
CI / Build embedded bundle (push) Successful in 2m34s
CI / Publish to Gitea npm registry (push) Has been skipped
AEC/AGC audit fix + two hardening items from the engine review.

- Add an `autoGainControl` capture param (UrlParams -> CallViewModel ->
  ConnectionFactory audioCaptureDefaults), mirroring echoCancellation/
  noiseSuppression. Defaults true (unchanged); the host sets it false only for
  the ML tier so the browser's auto gain control doesn't fight the in-source ML
  denoiser (pumping). Echo cancellation stays on. Tests cover the URL parse and
  the audioCaptureDefaults wiring.
- L1: init() now closes the owned AudioContext on a build failure (was orphaned;
  browsers cap live contexts, so repeated failures could exhaust them).
- L2: buildGraph() disposes its partially-built nodes on failure (disposeGraph
  previously only cleaned the prior graph).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 00:46:39 -04:00
Lotus CIandClaude Opus 4.8 6ab52d9926 feat(lotus-denoise): quality — dry/wet attenuation floor, gate-after-ML, softer DFN
CI / Build embedded bundle (push) Successful in 1m38s
CI / Publish to Gitea npm registry (push) Has been skipped
Track-B audio-quality changes to reduce the "robotic/underwater" artifact.

- Dry/wet attenuation floor (default 0.15 ≈ -16 dB) blends a little of the raw
  mic under the denoised signal so suppression can't fully collapse the noise
  floor between words (the main cause of the RNNoise "underwater"/pumping
  sound). Applied ONLY to the low-latency flat models (RNNoise/Speex); DTLN/DFN
  add algorithmic latency that would comb-filter an undelayed dry mix, so they
  rely on their own level instead. Tunable via `lotusDenoiseFloor`.
- Noise gate now runs AFTER the ML model, not before — gating the raw signal
  fed hard-zeroed frames into the model and tuned the threshold on pre-denoise
  levels.
- DeepFilterNet 3 noiseReductionLevel 80 -> 60: full strength was the main
  "over-processed" contributor; 60 keeps voice natural.

Defaults are conservative and tunable; final values are meant to be dialed in
with real-call A/B listening.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 00:16:23 -04:00
Lotus CIandClaude Opus 4.8 9a4e9bf7da fix(lotus-denoise): reliability — never-silent watchdog, resume timeout, cache + activation
Track-A robustness fixes from the engine review; no quality/model changes.

- H1: auto-resume the AudioContext on `statechange` if it suspends mid-call
  (mobile backgrounding / audio interruption). Previously the dest node emitted
  digital silence with no recovery — a silent mute of the sender.
- H2: `resumeCtx()` races `resume()` against a timeout. A suspended context can
  only resume on a user gesture; the action can arrive via postMessage, so a
  bare `await resume()` inside LiveKit's track-change lock could hang and
  deadlock all later mute/unmute/device-switch. Now it proceeds and the H1
  watcher heals it.
- M1: don't cache a REJECTED wasm fetch — a transient blip during a reconnect
  used to permanently disable denoise for the session. Evict on failure.
- M2: activate denoise off `allConnections$` (local participant's connections)
  instead of `livekitRoomItems$`, which excludes the local participant and only
  surfaces rooms with a remote member — so denoise now also runs when you're
  alone and no longer couples to a remote-render concern.
- Context lifecycle: `closeContext()` removes the state watcher before closing;
  `ensureContext()` closes a half-initialised context on any failure (no leak).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 23:27:44 -04:00
Lotus CIandClaude Opus 4.8 a4309f3e0c lotus: don't call AudioContext.setSinkId with an undefined device id
CI / Build embedded bundle (push) Successful in 56s
CI / Publish to Gitea npm registry (push) Has been skipped
audioOutputId comes from `useMediaDevices().audioOutput.selected$?.id`, which is
undefined until a device is selected. On the Tauri desktop webview the observable
emits undefined first, so setSinkId(undefined) threw "The provided value is not
of type 'AudioSinkOptions'" on every call join (repeatedly). Guard on a string
(the default device is the empty string, still valid).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 18:39:50 -04:00
Lotus CIandClaude Opus 4.8 e2ce8f5e3a lotus(#1): gate in-source denoise on dedicated lotusDenoiseSource flag
CI / Build embedded bundle (push) Successful in 46s
CI / Publish to Gitea npm registry (push) Has been skipped
The host already sets lotusDenoise=ml and injects its getUserMedia shim;
reusing that flag would double-process audio the moment this fork ships.
Gate the in-source engine on lotusDenoiseSource=1 instead, so the fork is
inert on deploy and the host cuts over explicitly (set the flag + drop the
shim) when ready.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:49:11 -04:00
Lotus CIandClaude Opus 4.8 e903f98bbe lotus(#1): make denoise asset base absolute (fixes DTLN/DFN load)
CI / Build embedded bundle (push) Successful in 42s
CI / Publish to Gitea npm registry (push) Has been skipped
Review found native dynamic import() of the DTLN/DeepFilterNet ESM
resolves "./denoise/…" against the bundled JS chunk's URL (-> /assets/…)
not the document, so those two models 404'd and silently fell back to raw
mic in the default config. Resolve the asset base to an absolute
same-origin href against the document; addModule()/fetch() accept absolute
too, so all three load paths stay consistent. (rnnoise/speex were
unaffected since addModule resolves against the document.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:37:51 -04:00
Lotus CIandClaude Opus 4.8 78350a21f4 lotus(#1): implement all 4 denoise models; fix gate name + rates
CI / Build embedded bundle (push) Successful in 1m20s
CI / Publish to Gitea npm registry (push) Has been skipped
Faithful port of cinny's proven pipeline into the TrackProcessor, closing
protocol gap F3 (host offers rnnoise/speex/dtln/deepfilternet; only the
first two existed in-source).

- Fix real bug: gate worklet registers as "noise-gate" (hyphenated), not
  "noiseGate" — the gated path would have failed to construct the node.
- Per-model sample rate: DTLN runs at 16kHz, others 48kHz (worklets don't
  resample); verify the context actually got the rate, else fall back.
- resume() a suspended context (host postMessage isn't a gesture).
- DTLN via dynamic-imported @workadventure helper (bypassUntilReady);
  DeepFilterNet via dynamic-imported ESM + DeepFilterNet3Core pointed at
  the self-hosted base. Same-origin base (kept from the C1 fix) makes these
  dynamic imports safe.
- Prefer SIMD rnnoise.wasm with non-SIMD fallback; cache wasm per URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:28:10 -04:00
Lotus CIandClaude Opus 4.8 29592fbb18 lotus(#1): fix restart-silence (A7), 48kHz ctx; protocol + CI hardening
CI / Build embedded bundle (push) Successful in 1m20s
CI / Publish to Gitea npm registry (push) Has been skipped
Denoise deep-review (CRITICAL): restart() read opts.audioContext, which
LiveKit does NOT pass on restart — so reconnect (the A7 scenario) and mic
device-switch threw after stopping the old track, leaving the mic SILENT
(A7 reintroduced). Fix:
- Processor owns a dedicated 48kHz AudioContext (sapphi worklets require
  48kHz; H1), reused across restart, closed on destroy.
- restart() never throws and never leaves a stopped track on the sender:
  builds the new graph first, then disposes the old; on failure degrades
  to RAW mic audio rather than silence.
- Cache wasm per URL (no re-fetch each reconnect); gate threshold default
  -45 and accept an explicit 0 (M2); document the cross-repo asset contract.

Protocol audit:
- Non-silent warning when an unsupported denoise model (dtln/deepfilternet)
  is requested instead of silent rnnoise fallback (F3).
- Correct the call_state enum comment (immediate error-reply, not 10s) (F2).

Build/CI audit:
- Stamp VITE_APP_VERSION in CI; document the vX.Y.Z-lotus.N version scheme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:15:59 -04:00
Lotus CIandClaude Opus 4.8 b8543c3fe1 lotus(security): harden denoise base, audio-inject, decorations
CI / Build embedded bundle (push) Successful in 1m8s
CI / Publish to Gitea npm registry (push) Has been skipped
Holistic security audit findings:
- C1 (CRITICAL): force lotusDenoiseBase to same-origin before it reaches
  audioWorklet.addModule()/fetch — a crafted call-link param could
  otherwise load attacker JS/WASM as a worklet processing the live mic.
  Non-same-origin/malformed values fall back to bundled ./denoise/.
- H1 (HIGH): gate audio-inject behind explicit lotusAudioInject=1 (still
  acks the action so no transport hang) — it publishes under the local
  user's identity, so it must not be silently armed for every call.
- M1 (MED): cap the decoration roster at 512 entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:00:40 -04:00
Lotus CIandClaude Opus 4.8 39b57db3b2 lotus(#1): ML denoise as a first-class audio TrackProcessor (fixes A7)
CI / Build embedded bundle (push) Successful in 58s
CI / Publish to Gitea npm registry (push) Has been skipped
Implements RNNoise/Speex noise suppression as a LiveKit audio
TrackProcessor attached to the local mic track, replacing the host's
build-time getUserMedia monkeypatch. Because EC re-attaches the processor
on every (re)publish (LocalTrackPublished), denoise now survives EC's
mid-call reconnect — the root cause of A7 "mic dead after reconnect".
Reuses the worklet/wasm assets already shipped under ./denoise/ (no new EC
dependency); model/gate configured via lotusDenoise/lotusModel/lotusGate
URL params. Additive: no-op unless lotusDenoise=ml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:54:46 -04:00
Lotus CIandClaude Opus 4.8 33d0e98eb0 lotus(#6): render decoration in MediaView; fix store lifecycle
CI / Build embedded bundle (push) Successful in 43s
CI / Publish to Gitea npm registry (push) Has been skipped
Review found in-call tiles use MediaView->Avatar, not TileAvatar, so the
decoration never rendered in-call (CRITICAL). Move the overlay into
MediaView, gated on the avatar's own visibility (!(video && videoEnabled))
so it never floats over live video; revert the TileAvatar changes.
Also ref-count the io.lotus.decorations registration (one shared handler,
no double-reply) and stop clearing the map on teardown so a transient
remount doesn't drop decorations (HIGH/MED).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:45:34 -04:00
Lotus CIandClaude Opus 4.8 70358d442b lotus(#6): render avatar decorations on in-call tiles
CI / Build embedded bundle (push) Failing after 12m11s
CI / Publish to Gitea npm registry (push) Has been skipped
Adds io.lotus.decorations (toWidget): the host pushes a userId->image-URL
map and EC overlays the profile decoration on each tile avatar
(TileAvatar), keyed by userId, with a useSyncExternalStore-backed store.
Makes A6 first-class in-call instead of absent. URLs are validated
https/blob. Additive: no-op unless the host sends decorations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:38:45 -04:00
Lotus CIandClaude Opus 4.8 7d792c33bf lotus(#7): fix screenshare cap per review
- Apply the encoding patch to ALL simulcast layers, not just encodings[0]:
  screenshare publishes simulcast (VP8), so the full-res layer (the real
  bandwidth hog) was left uncapped (CRITICAL).
- Re-apply on TrackUnmuted/restart + a 500ms settle, since LiveKit's
  refreshSenderEncodings() overwrites our caps on replaceTrack (device/
  source switch, processor toggle) without firing LocalTrackPublished (HIGH).
- Clamp values to sane ranges so a typo can't brick the encoder (MED).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:38:45 -04:00
Lotus CIandClaude Opus 4.8 22be058108 lotus(#5): native transparent-background + Lotus theme hooks
CI / Build embedded bundle (push) Successful in 45s
CI / Publish to Gitea npm registry (push) Has been skipped
Adds body.lotus-transparent (lotusTransparent=1) so the host wallpaper
shows through the call natively, retiring cinny's injected
`html,body{background:none!important}` hack; and body.lotus-theme
(lotusTheme=1) as a source-level Compound-token override hook for the
Lotus/TDS palette (driven by the existing setTheme channel / URL flags).
Additive: no-op without the flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:30:57 -04:00
Lotus CIandClaude Opus 4.8 10e6ba46e2 lotus(#3): harden audio-inject per review
CI / Build embedded bundle (push) Successful in 3m22s
CI / Publish to Gitea npm registry (push) Has been skipped
- resume() the AudioContext (host postMessage isn't a gesture) so the clip
  isn't silent; warn if it stays suspended (HIGH).
- Close the AudioContext on decode failure (no context leak) (MED).
- Abort in-flight clips on teardown (unmount/vm-change/leave) so audio
  doesn't keep blasting to peers (MED).
- Stop the cloned MediaStreamTrack when a room publish fails (MED).
- Validate url is https/blob and fetch with credentials:omit, mode:cors
  (MED security).
- Guard against NaN clip duration; fix stale enum doc comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:26:45 -04:00
Lotus CIandClaude Opus 4.8 6d739db8b8 lotus(#7): audio/screenshare quality controls via widget action
Adds io.lotus.set_quality (toWidget): caps mic audio bitrate and
screenshare bitrate/framerate via RTCRtpSender.setParameters (no
republish). Settings are sticky and re-applied on LocalTrackPublished so
they survive mute/unmute and reconnects. These encoding controls lived in
EC's module scope, unreachable from the host against the prebuilt bundle.
Additive: no-op unless the host sends the action.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:24:57 -04:00
Lotus CIandClaude Opus 4.8 2ab9427c09 lotus(#4): pin camera into spotlight during screenshare (A5)
CI / Build embedded bundle (push) Successful in 51s
CI / Publish to Gitea npm registry (push) Has been skipped
Review found the manual pin only chose among cameras when no screenshare
was active; during a screenshare the screenshare won the spotlight and the
pinned camera was demoted to an ignored PiP. Apply the override at the
spotlightAndPip$ level: when a pin is explicitly set, surface that camera
in the spotlight alongside the shared screen. No manual pin = unchanged.

Note: a pin persists if the pinned user briefly leaves and rejoins; the
host clears it via focus_participant{userId:null} (by design).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:21:14 -04:00
Lotus CIandClaude Opus 4.8 c73eec0781 lotus(#3): soundboard audio injection via widget action
CI / Build embedded bundle (push) Successful in 43s
CI / Publish to Gitea npm registry (push) Has been skipped
Adds io.lotus.inject_audio (toWidget): mixes a soundboard clip into the
call so other participants hear it. Publishes the clip as a separate
Unknown-source LiveKit track (rendered by MatrixAudioRenderer) rather than
splicing into the mic track, so the denoise pipeline is untouched; the
track is unpublished when the clip ends (with a 30s safety cap). This is
the real call-audio injection that was impossible against the prebuilt EC
bundle. Additive: no-op unless the host sends the action.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:18:43 -04:00
Lotus CIandClaude Opus 4.8 0f90600a59 lotus(#2): address review — ack contract, rate, param precedence
CI / Build embedded bundle (push) Successful in 1m18s
CI / Publish to Gitea npm registry (push) Has been skipped
- Document that io.lotus.call_state is request/response and the host must
  ack it (cinny listenAction replies {}) to avoid 10s-timeout churn (H1).
- Throttle 150ms -> 250ms to reduce widget traffic (M1).
- lotusParam: hash fragment wins over query, matching EC's ParamParser (L1).
- Fix the misleading "opaque" id comment; id is userId:deviceId (L2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:14:42 -04:00
Lotus CIandClaude Opus 4.8 9d7784b9bc lotus(#4): focus-participant spotlight via widget action
Adds io.lotus.focus_participant (toWidget): the host can pin a participant
to the spotlight by Matrix user id (or clear with userId:null), via a
manual override injected into CallViewModel.spotlightSpeaker$. Replaces
cinny's fragile DOM .click() tile-selector focus hack. Extracts the action
enum into lotusActions.ts (no circular import) and allow-lists Lotus
toWidget actions in initializeWidget. Additive: no-op unless the host
sends the action.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:10:16 -04:00
Lotus CIandClaude Opus 4.8 444af5f7ac lotus(#2): stream per-participant call state over widget API
CI / Build embedded bundle (push) Successful in 1m45s
CI / Publish to Gitea npm registry (push) Has been skipped
Opt-in (lotusCallState=1) bridge that emits io.lotus.call_state with each
participant's speaking/audio/video state, so the Lotus host can drive
speaking rings / mute badges / PiP from real events instead of scraping
EC's rendered DOM. Exposes vm.userMedia$ on the public CallViewModel.
Additive: no-op without the flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:05:20 -04:00
Lotus CIandClaude Opus 4.8 39377fb6e1 ci: drop actions/upload-artifact@v4 (incompatible with Gitea runner)
CI / Build embedded bundle (push) Successful in 1m20s
CI / Publish to Gitea npm registry (push) Has been skipped
upload-artifact@v4 needs GitHub's artifact backend, which the Gitea
act_runner doesn't implement — it failed the build job with exit 1 even
though build:embedded + smoke-check passed. The publish job rebuilds from
source, so the artifact was only a convenience.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:49:15 -04:00
Lotus CIandClaude Opus 4.8 b1fed78283 ci: stage embedded dist into embedded/web/dist before publish
CI / Build embedded bundle (push) Failing after 46s
CI / Publish to Gitea npm registry (push) Has been skipped
build:embedded outputs to repo-root dist/; the publish job and smoke-check
expected embedded/web/dist (the publish template's files entry), which was
never created in CI — so a tagged publish would fail its smoke-check or ship
an empty tarball. Copy dist -> embedded/web/dist in both jobs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:45:20 -04:00
Lotus CIandClaude Opus 4.8 229b54b3b7 Lotus fork: rename embedded package + add Gitea CI
CI / Build embedded bundle (push) Failing after 1m9s
CI / Publish to Gitea npm registry (push) Has been skipped
- embedded/web/package.json: publish as @lotusguild/element-call-embedded
  from our Gitea fork (repository URL updated).
- .gitea/workflows/ci.yml: build the embedded bundle on PR/push to lotus;
  publish to the Gitea npm registry on a v* tag. Linux-only (web bundle).

Based on upstream element-call v0.20.1 (2d74c48).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:16:45 -04:00
Valere FedronicandGitHub 2d74c48151 Merge pull request #4016 from element-hq/valere/backport/several_click_to_unmute_bug
[backport] - Fix several click to unmute bug (feat(mute): add syncing state and disable toggle during async mute)
v0.20.1
2026-06-08 09:50:58 +02:00
Valere FedronicandValere 1bf2a0917b Merge pull request #4013 from element-hq/valere/fix_repeated_click_to_unmute
feat(mute): add syncing state and disable toggle during async mute
2026-06-05 13:32:48 +02:00
RobinandGitHub c021fc1548 Merge pull request #3995 from element-hq/toger5/fix-double-leave-sound
Fix play of second leave sound
2026-06-02 14:46:43 +02:00
Timo K 692a55c84c fix race 2026-06-02 14:06:28 +02:00
Timo K c5ffdea370 lint 2026-06-02 13:56:16 +02:00
Timo K 9117b40e7b fix tests 2026-06-02 13:52:43 +02:00
TimoandGitHub 25a6cefa34 Merge pull request #3905 from manfrommedan/fix/promise-withresolvers-polyfill
Add Promise.withResolvers polyfill for older WebViews (Chrome < 119)
2026-06-02 19:43:12 +08:00
RobinandGitHub 399b7d83da Merge pull request #3988 from element-hq/renovate/livekit-client
Update dependency livekit-client to v2.19.0
2026-06-02 11:50:10 +02:00
TimoandGitHub e2128776b3 Merge pull request #3935 from element-hq/toger5/contribut-issue-first
Contributing.md explain "issue first" contribution approach
2026-06-02 17:35:56 +08:00
TimoandGitHub 327a0ad9b0 Merge pull request #3951 from element-hq/valere/improve_error_message
Improve error messages for sfu auth problems
2026-06-02 17:33:56 +08:00
Robin 47326c28d3 Fix type error 2026-06-02 11:33:17 +02:00
RobinandGitHub 084fa20b3d Merge pull request #4003 from element-hq/device-switch-fixes
Show the right labels in device switcher menus
2026-06-02 11:29:28 +02:00
Timo K e05af09c28 Add test 2026-06-02 11:27:59 +02:00
Robin cec78a383d Fix footer stories 2026-06-02 11:14:53 +02:00
RobinandGitHub faa34aeaaa Merge pull request #3987 from element-hq/renovate/compound
Update dependency @vector-im/compound-design-tokens to v10.2.0
2026-06-02 10:48:44 +02:00
Robin 5fed562db2 Request full device names when device switchers are open 2026-06-02 10:36:57 +02:00
Robin 164765cd27 Show the right fallback labels in device switcher menus 2026-06-02 10:15:40 +02:00
renovate[bot]andGitHub 489492d9b9 Update dependency livekit-client to v2.19.0 2026-06-02 06:50:03 +00:00
renovate[bot]andGitHub bec34ee360 Update dependency @vector-im/compound-design-tokens to v10.2.0 2026-06-02 06:49:28 +00:00