- The standalone app at five window sizes, from a large desktop to a
phone-shaped 360x640, on Chromium and Firefox: the camera menu stays
inside the window, which is the call area there, and the last effect can
still be scrolled to and chosen.
- As a component in a host page, and as a widget in Element Web: a
picture chosen reaches the preview, so the pipeline's model and
WebAssembly load from those builds, and in a small container the last
effect is reached and chosen.
- Compound portals the menu to the page and takes no container for it, so
there it has none of the component's scoped styles, as the microphone
menu has none.
- The colour checks the three share live in playwright/utils.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- A host in the call records every frame of every video track it receives
from the moment the track arrives. A guest with a picture chosen joins,
or turns the camera on in the call from off at joining or from off in
the call; none of the guest's frames may be closer to the camera image
than to the picture. A guest with no effect fails it.
- The preview's track stopping destroys the shared pipeline, so the call's
track builds it again. That build is held back as long as the slower
route takes, and publishing still waits for it; each check that holds
the model back also checks that it did.
- The first build survives the preview camera turned off and on while it
runs, which is what running builds one at a time is for.
- The camera's colour is taken once the preview shows it; taken earlier it
was black, which let frames of the room pass as the picture. The camera
checks wait for the call itself, as the lobby has a camera button of
its own.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The desktop rule from #4059 goes: now that the menu says where effects
run slowly, using them there is the user's choice. A phone on the fast
route held 99% of its frame rate and was refused; a desktop on the slow
one held 73% and was allowed.
- Asks @livekit/track-processors directly; the wrapper module goes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The first effect chosen in a session shows a spinner on its own tile,
in place of the tick, until a frame carrying it is drawn. Later changes
show nothing, having nothing to prepare.
- Ended by the frame, not the promises, which resolve while the segmenter
is still being built. The transformer reports the first frame that went
through the segmenter; one passed through untouched doesn't count.
- The pipeline publishes the wait with its state, and both footer view
models take it as an input.
- The lobby tests' provider mock gains the observable hook, built once: a
fresh observable per render rebuilt the footer on every render.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Where the browser has no MediaStreamTrackProcessor, effects draw each
frame through a canvas: slower, and the first build stalls the page.
They stay offered and choosable there, with a notice under the grid
that says so and describes the group.
- Decided in the view model, beside whether effects can run at all, so
the lobby and the call give the same answer.
- The notice stays within the menu's width however long the sentence,
which the story checks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The camera menu's blur toggle becomes a Background effects section
under the cameras: no effect, blur and the two shipped backgrounds as
one radio choice of tiles, three across. It scrolls with the device
list, so it stays reachable in a short call, and choosing keeps the
menu open. The lobby gets it too, as both render the same footer.
- The footer's view model gives the options, the notice and the actions;
the footer only names them.
- Each tile is Compound's MenuItem as a menuitemradio, styled as a tile.
Compound makes it an item of the dropdown, so the arrow keys walk the
tiles with the device rows, and takes its tap in the phone drawer. A
tile that can't be chosen has no select handler, so the keyboard passes
over it.
- Where the browser can't run effects the tiles are shown, disabled and
explained by a notice the group is described by. No effect stays
choosable and shows as in force. The notice is in the scrolling list,
so a short call doesn't push the menu out of it.
- Section headings are raised, or the positioned tiles scroll over the
one they sit under; raised within the list only, so the menu's frame
stays over them. Tiles clip with overflow: clip; with hidden they are
scroll containers, which focus scrolls into view without their border.
- End to end on Chromium and Firefox: a shipped background chosen in the
lobby shows in the preview, and None after Blur draws nothing, counted
in WebGL draws (Blur: about 1400 a second on Chromium, 2300 on
Firefox).
- The Quick Audio Menu's test of the camera menu keeps its name and now
finds blur among the effects rather than as a toggle.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- A catalogue of what can be chosen: no effect, blur, and two shipped
backgrounds, the app's own gradients flattened onto its canvas colour.
Their ids are what a saved choice stores, so they are named for the
pictures.
- One setting for the choice, typed as the ids an effect can have and
defaulting to blur for anyone who had it on. A stored value we no
longer ship, or one that isn't even a string, reads as no effect: it
comes from storage another build or a hand may have written.
- The pipeline follows it, switching in place to a picture as readily as
to blur, and the camera menu's blur toggle reads and writes it.
- So does Settings' blur checkbox: a picture reads as blur off, and
checking it replaces the picture with blur. The old on/off setting is
only read, to carry blur over. First tests for the Settings modal,
which fail with the checkbox on the old setting.
- The transformer's own init loads a picture chosen before it was built,
as the base init does and it had not: the first effect always is, and
the picture was drawn black.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Brings design's blur icon, which the background effects grid uses.
- Of the icons the release redrew, guest and zoom-in, Element Call uses
neither, and none of the icons it removed are used here.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- AppViewModel creates them, next to its media devices, as review asked;
BackgroundEffectsProvider takes them as a prop rather than building
them in an effect.
- The component build, which has no AppViewModel of its own, now makes one
in the effect that made its media devices, so both builds get their
state the same way.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The pipeline's switches run through reconcile, which already runs them
one at a time and skips those overtaken, in place of a queue of our own.
- A failed switch is logged and the next still runs, now tested.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- BackgroundEffects is an interface, built by createBackgroundEffects,
following the convention for state holders, as review asked.
- Same behaviour: the body moves from the constructor unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- One name, as review asked: TrackProcessorContext becomes
BackgroundEffectsContext, with BackgroundEffectsProvider,
BackgroundEffectsState, useBackgroundEffects,
useBackgroundEffectsState$ and useSyncBackgroundEffects.
- The state's Behavior is backgroundEffectsState$ wherever it is passed,
in place of trackProcessorState$, trackerProcessorState$ and
processorState$.
- Renames only: nothing changes in behaviour.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The one support check asks for a desktop again, as #4059 had the
pipeline do: menu and pipeline still agree, so a phone is neither
offered blur nor refused it.
- Lifting it moves to the PR that tells the user where effects run
slowly, so it is their choice to make.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The Vite transform wrapper takes Vite's own types, with no cast.
- The pipeline's options are compared with deepCompare, not as JSON.
- Renames `attached` to `enable` and shortens the pipeline's doc comment.
- Drops a comment that blamed an ended track on the pipeline's queue.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The menu asked the browser; the pipeline asked the browser and for a
desktop. On a phone whose browser can run them, blur was offered and
then refused: the toggle moved and the video did not change.
- Both now ask one function, and it asks the browser alone. The platform
was the wrong measure: a phone on the fast route held 99% of its frame
rate and was refused, a desktop on the slow one held 73% and was
allowed.
- The in-call device switcher is still withheld on a phone; that is the
device menu's own rule and is unchanged.
- The provider tests run as a phone, and fail with the desktop test back.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- One pipeline, shared by the pre-join preview and the call, attached the
first time an effect is wanted and never detached after; turning blur
off switches it to a disabled mode in place. Main dropped the processor
whenever blur went off, and a pipeline taken off the camera is destroyed
and re-primed, so each time blur came back one unprocessed frame went
out. Built once, the priming frame is spent once.
- Still driven by the existing blur setting: nothing a user sees changes.
- Its logic is BackgroundEffects in src/state/, which the provider only
builds, so the tests drive it without rendering anything.
- Disabled drops the blur radius too. The SDK's disabled mode keeps it,
and with a radius every frame is still segmented and thrown away; with
neither a radius nor a picture, frames pass untouched.
- Switches run one at a time and skip any overtaken, so an earlier,
slower switch can't land after a later choice.
- Builds, rebuilds and teardowns of the pipeline run one at a time
(OneStepPipeline). The preview's and the call's tracks attach and stop
it independently, and the SDK's wrapper holds one track's streams: two
builds at once overwrite each other's, and a stop during a build does
nothing.
- Every camera track starts with the pipeline on: the room's capture
defaults follow it, and a camera turned off before the first effect is
replaced on turning it on, since a stopped track can't take one. One
attached after publishing lets the room through while it is built.
- BlurBackgroundTransformer becomes BackgroundEffectTransformer: it also
sits disabled, and its init applies that state as the SDK's would.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- nodePolyfills rewrites any module that mentions process, and MediaPipe's
WASM loader does. The loader is a classic script that sets
self.ModuleFactory; as a module it sets nothing, so under pnpm dev the
segmenter failed with "ModuleFactory not set". Blur on main fails the
same way there. Production is unaffected: the loader ships as a static
asset.
- The plugin's transform now skips that loader. Checked with main's own
BlurBackgroundTransformer: it initialises with this and fails without.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Compound 10.3.0 bounds a menu by the space it has and scrolls what
doesn't fit, within a boundary the app passes. The menus pass the call
area, 16px in from its sides, rather than measuring its width.
- The 296px width is the menu's own style. Compound gives that style only
to the dropdown, so menuIsDrawer, which copied its phone rule, goes.
- Compound draws the menu's frame over its content, so nothing that
scrolls can hide it in Safari: the list, headings and meter no longer
keep a border width clear of it, and the checks that they did go.
- The device list is Compound's scroll area, so it fades out at the
foot with Compound's chevron while there is more below.
- The microphone menu's meter sticks at that foot, with rows scrolling
under it as before, so the list has no fade above it: Compound's is a
mask fixed to the list's edge, while a bounce at the top carries the
meter with the rows. The band under the meter is filled, or rows would
show there. The edge fades from where the chevron starts, so a bounce
fades the meter out before it reaches the chevron. A row reached by
keyboard stays above the meter.
- The list's height is still its share of the call.
- The component harness leaves the host's stylesheets unscoped, Compound's
among them, as a host does: scoped, they never reached the menu, which is
portalled out of Element Call's root.
- Its specs check only Element Call's own document styles on the host
page, and find the device list within Compound's scroll area.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The camera and microphone menus were as wide as their longest device
name, so a long one widened the menu over more of the call: 340px for a
long speaker name. Both are now 296px, as design sets them, and a long
name wraps.
- Narrower only where the call area is, so the menu still fits inside it:
268px in a 300px call. Taken from the observation of the call area that
already bounds the list's height.
- Held only for the dropdown. On Android and iOS Compound renders the menu
as a drawer as wide as the screen; menuIsDrawer follows its rule.
- Once the list is long enough to scroll, Safari paints it above the
outline the menu is framed with, and the frame vanished down both sides
of it: 384px of it on the many-devices microphone menu, in WebKit. The
list now keeps a border width clear of the frame, so the headings and
the meter inside it no longer need their own.
- Guarded with a long speaker name, which fails at 340px on main's sizing;
a unit test run as a phone, which fails with the width held there; and
the many-devices story's inset, which fails on the old stylesheet, since
the stories run in Chromium, which never showed the Safari bug.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
I learned that makeKeyDelay has been deprecated in matrix-js-sdk ever since the switch to the new encryption manager, so this config option has no effect.