The runtime path was `./` plus the asset's path from the package root,
which is right for every chunk the build emits today, all of which sit
at that root, and wrong for one emitted under a directory. Vite names
the referencing chunk, so the path is now computed relative to it.
The sound loader kept a branch for sounds inlined as data URLs, with a
comment saying the component build produces them. No build does any
more, and no host would inline a sound of this size, so the branch and
its tests go.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The effects state's timeline through preparing, settling and attached,
and that a failed trial build withholds the effects for the session while
keeping the choice.
- The footer offers nothing once the pipeline fails to build, and passes
on the wait as the state's single wait field.
- The call leaves the camera out of the join while the pipeline is
prepared, publishes it once that is known, and turns on a camera turned
off before the processor was chosen; the lobby opens no camera meanwhile.
Both view models are driven by one state subject.
- The transformer's trial build answers whether a segmenter can be made.
- End to end, a browser whose pipeline cannot be built sees the effects
withheld.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A segmenter that can't be built, with the model unreachable or a GPU
its delegate refuses, took the camera down with the pipeline: the SDK
fails the track it was attaching to, and the lobby muted camera and
microphone, on every load while the choice was kept.
- One is now built on trial when an effect is first wanted, before the
pipeline is attached to anything, on the CPU where the GPU refuses.
Where it can't be built, the options are disabled with the existing
notice for the rest of the session. The choice is kept: a browser
update may fix what failed.
- The view model takes that verdict from the pipeline's state rather
than
asking the SDK, so both say the same, and it no longer builds a WebGL
context of its own to ask.
- Until the trial is over, the lobby opens no camera and the call makes
no
camera track, so neither starts without the effect.
Rebased onto main, where the Publisher is gone and the call view model
publishes the camera through the SDK:
- CallViewModel takes videoProcessorPreparing$. While it is true, the
camera's mute handler waits before making a track, and the join leaves
the camera out of its requests (initialPublishRequests); the view
model
publishes it once it is known whether the pipeline builds. The
Publisher's test became CallViewModel tests.
Cherry-picked from spec/background-effects/12-unbuildable-pipeline onto
toger5/background-effect/camera-preview, where the camera menu's preview
takes the camera from the footer's caller rather than from a track
synced
on the background-effects state. The footer factories take the
pipeline's
state again, as this commit needs its verdict and not only the settling
flag; the synced camera and the preview's wrapper type are not brought
along.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The component module was 18 MB, of which 15 MB was MediaPipe's runtime
as a base64 data URL, with the segmentation model, every sound and the
background pictures inlined the same way: a library build inlines every
asset unless its import says `no-inline`. Every host downloaded all of
it before the first call, whether or not a background effect was ever
switched on, and a host with a Content Security Policy without
`script-src data:` could not run effects at all, since MediaPipe
injects its loader as a script tag.
The imports now say `no-inline`, and the build renders each reference
as `new URL("./assets/…", import.meta.url)`, which webpack 5, Vite and
Rollup follow as an asset of the host's own; the `./` is spelled out
because a bare `assets/…` is read as a package name. The module is
2.3 MB. The runtime is fetched when an effect is first switched on and
cached on its own. MediaPipe's files come through the package's
`exports` rather than a path into node_modules.
MediaPipe's JavaScript (160 kB) stays in the module: LiveKit's track
processors import it statically, so a dynamic import moves nothing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A cross on the tile's top-right corner removes an added background,
critical-coloured at rest and named by its tooltip, as design draws it.
The one in force offers none, since taking it away would leave nothing
chosen; the view model refuses it too.
- The cross sits beside the item rather than in it, which would take the
press first and choose the tile, and isn't a control of its own, so the
menu keeps one keyboard model: Delete or Backspace on the tile does the
same, and the tile says so with aria-keyshortcuts.
- Removed from IndexedDB and its picture released. End to end on
Chromium and Firefox, a background removed by Delete is still gone
after a reload.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Update ghcr.io/element-hq/element-web:develop Docker digest to e063472
* Fix tests
* Dismiss further toasts
* Close even more toasts
* Yet more toast fixes
* Toast fixes all over
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
- Five of one's own at most, so the grid is whole: at the limit the add
tile goes, and it comes back once one is removed. The store holds the
limit too.
- An added image is refused if it isn't an image, is animated, or can't
be decoded; reduced to 1920px on its longest edge; and laid on an opaque
ground, since a transparent pixel would be a hole in the background.
- Animation is read from the file, as a still and an animated WebP share
a type. Chromium has no selected track until the decoder's tracks are
ready; without waiting for them an animated GIF got through.
- A refusal says why, in place of the notice, and is scrolled into view.
It can be dismissed, is gone once the menu closes, and the next shows
even if it reads the same. What was in force stays.
- End to end on Chromium and Firefox, from what the device then keeps: an
animated GIF is refused with its message, a 4000x3000 image is kept at
1920x1440, and a half transparent one is kept opaque. All three fail
with the preparation bypassed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- An add tile after the pictures opens the file picker, and the image
chosen joins the backgrounds on offer without being put on; it is in
force once chosen, like a shipped one. The tile is a command rather
than a choice, so a menu item beside the radios, and it isn't offered
where effects can't run.
- Kept in IndexedDB on this device and never sent anywhere, behind a
small interface, so the unit tests use an in-memory store; the
IndexedDB store itself is tested against fake-indexeddb, a new dev
dependency.
- The picker takes the focus, which the menu reads as a click elsewhere,
so the menu holds itself open until a file is chosen or the picker is
cancelled.
- A chosen image the device no longer keeps, as after its storage is
cleared, falls back to no effect once the device has said what it
keeps.
- Switching is checked between any two kinds of effect. End to end on
Chromium and Firefox: an added image shows in the preview once chosen;
once it is in force, no request carries the file or the copy kept,
which a deliberate upload fails; chosen, it is in force again after a
reload, gone once storage is cleared, and can be added again.
- Kept as given here; what can be added is checked in the commit after.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- A remembered effect this browser can't run is cleared rather than kept,
as the menu already showed no effect in force there.
- A camera turned off and on again with an effect chosen gets the same
pipeline as its track arrives, neither rebuilt nor switched again.
- End to end, on Chromium and Firefox: the effect chosen before a call is
still chosen, and worn, in the next call after leaving; and a second
tab still shuts the first down with the existing "Opened in another
tab" error while an effect runs.
- Switching camera and sharing a screen are checked by hand: they need
two cameras and a screen, which the fake devices don't give yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The e2e check held back the model download at the join and required
one, since the pipeline used to be built again for the call's track.
The join now takes the lobby's running pipeline over, so no download
happens, and the check says so.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 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>