Commit Graph

1457 Commits

Author SHA1 Message Date
Ovgodd 6e17c6533c ♿️(frontend) use i18n strings for screen share wheel zoom shortcuts
Use i18n strings for displaying screen share zoom shortcuts in the UI controls.
2026-09-22 16:21:00 +02:00
Ovgodd 27e32c0370 ♿️(frontend) add keyboard navigation to screen share zoom toolbar
Arrows move between controls instead of panning, w/ en/fr/nl/de hint update.
2026-09-22 16:21:00 +02:00
Cyril 6d4403d4fa ♻️(frontend) refactor screen share zoom pan with useMove
use react-aria useMove for pan, zoom/pan refs for DOM updates,
2026-09-22 16:21:00 +02:00
Cyril 37ae308825 ♿️(frontend) add wheel zoom shortcut hints to screen share controls
Show Ctrl/Cmd+scroll zoom shortcut in tooltips, aria, SR hints, w/ en/fr/nl/de.
2026-09-22 16:21:00 +02:00
Cyril 16fd2dc4e8 💄(frontend) improve screen share zoom toolbar sizing and containment
Slightly enlarge toolbar controls while clipping hover states
inside the pill, so buttons no longer overflow the bar.
2026-09-22 16:21:00 +02:00
Cyril 9791a8a3b2 💄(frontend) use distinct expand/collapse icons for fullscreen actions
Replace fullscreen icons with expand-diagonal-line and collapse-diagonal-line
2026-09-22 16:21:00 +02:00
Cyril 5b0dece79b 🌐(frontend) add i18n keys for screen share zoom controls
English and French labels, SR announcements and pan navigation hint.
2026-09-22 16:21:00 +02:00
Cyril 96135a0263 ✨(frontend) add zoomable screen share video component
Wraps VideoTrack with zoom/pan, keyboard nav and screen reader announcements.
2026-09-22 16:21:00 +02:00
Cyril ce2e2a4d64 ✨(frontend) add ScreenShareZoomControls toolbar component
Bottom-right toolbar with zoom, fit-to-window and fullscreen buttons.
2026-09-22 16:21:00 +02:00
Cyril e5dc9c2f15 ✨(frontend) add useScreenShareZoom hook for zoom and pan
Manages zoom level, pan offset, wheel zoom, drag-to-pan and keyboard panBy.
2026-09-22 16:21:00 +02:00
kaelvar e97eab9b5e 🐛(frontend) apply the saved reception resolution when joining a meeting
`VideoResolutionSubscription` applied the saved reception resolution on
`RoomEvent.TrackPublished`. livekit-client does not raise that event for cameras
that were already sending when the local participant joined, so a user who had
chosen Low definition still received High definition from everyone already in
the meeting, and Low definition only from whoever joined after them. Nothing in
the UI showed the discrepancy: the setting kept displaying Low definition.

Apply the preference to the publications we already know about when the effect
runs, and keep listening on `TrackPublished` — which stays the earliest point to
cap a camera that starts after us — plus `TrackSubscribed`, which is the first
event raised for the cameras that were already sending.

That initial pass also covers a change of preference mid-call, which
`VideoTab.updateExistingRemoteVideoQuality` was doing separately. Removed, it is
now the same code path for joining and for changing the setting.

The three entry points overlap on purpose; the `publication.videoQuality` guard
makes the repeats free. It reads as High definition when nothing was ever
requested, so the default case costs no signal round trip either.

Fixes #1606.
2026-09-22 15:07:47 +02:00
tanguy chenier 771f58c0aa 🐛(frontend) play the waiting room notification sound on every arrival
The waiting room has its own sound in notifications.mp3, and nothing could play
it: the sprite is named "waiting" while triggerNotificationSound passes a
NotificationType, and howler returns without playing when the sprite id is
unknown. ParticipantWaiting was also absent from the sound settings, so the
check on the store would have refused it first. The toast borrowed the
participant joined sound instead.

The sound was tied to the waiting list going from empty to non empty, so a
second person arriving while someone was still waiting was silent, which is the
case the issue describes.

Name the sprite after the notification type, register the type in the settings,
and sound every arrival, detected on the participant ids so that an admission
and an arrival between two refreshes do not cancel each other out.

closes #1705
2026-09-17 17:06:06 +02:00
lebaudantoine b723b7bb62 🐛(frontend) fix file permissions in the Docker image
Incorrect file permissions in the frontend Docker image caused
problems when running the project, and were surfaced by @briquet
while setting it up with Podman.

Adjust the ownership and permissions applied during the build so
the image works cleanly under Docker and Podman alike.
2026-09-15 00:01:22 +02:00
snyk-bot d85123d0c5 ⬆(frontend) upgrade humanize-duration from 3.33.2 to 3.34.1
Snyk has created this PR to upgrade humanize-duration from 3.33.2 to 3.34.1.

See this package in npm:
humanize-duration

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-09-14 15:40:56 +02:00
Miguel Victoria cfdf1c2f92 ✨(backend) add default video codec on apiConfig struct
Co-authored-by: David <60177543+davd-gzl@users.noreply.github.com>
2026-09-14 09:03:05 +02:00
snyk-bot eda66640df ⬆️(frontend) upgrade posthog-js from 1.414.0 to 1.418.10
Snyk has created this PR to upgrade posthog-js from 1.414.0 to 1.418.10.

See this package in npm:
posthog-js

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-09-14 07:38:10 +02:00
snyk-bot 3fa05ea785 ⬆️(frontend) upgrade react-i18next from 17.0.10 to 17.0.12
Snyk has created this PR to upgrade react-i18next from 17.0.10 to 17.0.12.

See this package in npm:
react-i18next

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-09-14 07:03:25 +02:00
lebaudantoine 30b68052e1 ⚡️(frontend) defer loading the Crisp script until idle
Load the Crisp JavaScript module only once the frontend is idle,
instead of during the initial page load.

Keeps the critical path lighter and prevents Crisp from competing
with the app's own bootstrap for network and CPU on slow devices.
2026-09-13 00:03:55 +02:00
leo e336122cfa 💬(frontend) clarify video recording wording
Video recording from transcription panel did not explicitly
mention video, leading to confusion from some users. Make
wording more explicit.
2026-09-09 20:03:36 +02:00
lebaudantoine e0ab7f191f 📈(frontend) include LiveKit SIDs in the connection analytics event
Attach the LiveKit SIDs (room and participant) to the connection
analytics event.

Makes it easier to debug problematic sessions and to correlate a
room session with the corresponding LiveKit logs.
2026-09-09 13:38:44 +02:00
lebaudantoine 7565ede0a7 🔖(minor) bump release to 1.31.0 2026-09-08 00:45:01 +02:00
lebaudantoine 1a15e9f44e ✨(frontend) align feedback buttons with rating card
Match the button row width to the rating card (100%, max 410px) and
make both buttons share it equally so their edges line up with the card.
2026-09-07 23:55:05 +02:00
lebaudantoine 7838d8acfe 🐛(frontend) refetch waiting participants when the lobby becomes disabled
When the lobby is disabled mid-meeting (e.g. the room is switched to
public), the waiting participants list stopped being refetched, so
the previously cached list stayed visible with stale data.

Trigger a refetch in that case as well, so the list is cleared and
the moderator UI no longer shows waiting participants for a lobby
that is no longer active.
2026-09-07 23:30:27 +02:00
lebaudantoine 3bb388b937 ✨(backend) sort waiting participants by their arrival time
Highlighted by a suggestion from @florent, the waiting participant
list was not sorted, so moderators could see participants in an
arbitrary order.

Add an explicit `entered_at` attribute on each waiting participant,
so the list can be sorted by arrival time. Participants are now
shown in a stable order of arrival, both across polls and across
moderators.
2026-09-07 23:30:27 +02:00
lebaudantoine e1cc8105db 💄(frontend) position the login hint dynamically next to the button
Compute the position of the login hint at render time so it is
always displayed close to the login button, regardless of the
button's placement or the current viewport size.
2026-09-07 20:12:57 +02:00
lebaudantoine 7844dfcc12 📈(frontend) track missing lobby participant on accept/reject
When a moderator accepts or rejects a lobby entry that no longer
exists, emit a tracking event so we can measure how often it
happens.

This signal will help tune the lobby polling interval: too many
"not found" events means the moderator side is working from a stale
list. Keep raising the error to the client on top of tracking it,
so the frontend still surfaces the issue (its current handling of
this case is still incomplete).
2026-09-07 20:12:57 +02:00
lebaudantoine 67e7d382e3 ⚡️(frontend) add trailing slash on the /me endpoint call
The `/me` endpoint was called without a trailing slash, so every
request was going through a 301 redirect before hitting the actual
endpoint.

This endpoint is called by every user at least once per session, so
based on the logs, avoiding the redirect should cut the volume of
requests hitting it by around 10%.
2026-09-07 20:12:57 +02:00
lebaudantoine 164ac8d948 ⚡️(frontend) increase lobby polling interval on both sides
Increase the polling interval used by the lobby feature, on both the
waiting participant side and the moderator side.

The goal is to reduce the volume of requests the lobby generates,
trading a bit of data freshness for better performance.

It will de facto reduce pressure on the backend.

We will observe the impact in production, and revisit these
intervals if the delays turn out to be too aggressive.
2026-09-07 20:12:57 +02:00
lebaudantoine e3deb37fbe 🔒️(frontend) upgrade base image to 1.30.4-alpine3.24
Bump the frontend base image to `1.30.4-alpine3.24`, which picks up
fixes for the CVEs listed below and lets us drop the individual
dependency pins that were only there to address earlier known CVEs.

Address the following HIGH severity CVEs in libuuid / util-linux,
reported by Trivy. Bumping to 2.41.6-r1 (bundled in the new base
image) covers all of them:

* CVE-2026-53612 — TOCTOU in mount post-mount ownership/mode
  changes.
* CVE-2026-53613 — TOCTOU in mount via ancestor directory swap.
* CVE-2026-53614 — SUID mount(8) nosuid/noexec bypass via
  LIBMOUNT_FORCE_MOUNT2.
* CVE-2026-76642 — failed external mount helper still runs
  privileged X-mount post-hooks.
* CVE-2026-78408 — nsenter --join-cgroup leaks root cgroup
  migration authority (fixed in 2.41.6-r1).
* CVE-2026-78410 — restricted bind mounts do not pin the source,
  allowing X-mount.owner/group/mode escalation.
2026-09-07 16:40:03 +02:00
lebaudantoine 33929324d0 🐛(frontend) keep feedback buttons on one line for fr/es/en
A minor layout regression appeared when switching to the Marianne
font: the feedback buttons wrapped onto two lines instead of
staying on one.

Adjust the layout so the buttons stay on a single line regardless
of the font in use.
2026-09-07 16:40:03 +02:00
lebaudantoine adc74f846c 🐛(frontend) center Avatar initials with a font-aware cap-height ratio
The previous implementation centered initials by measuring `<text>`
with `getBBox()`, which returns the font's advance-width by
ascent-to-descent band, not the ink of the glyphs. On fonts with an
asymmetric band, initials rendered off-center. Marianne is a
particularly clear case: ascent 1131 / descent 256 puts the band
center 87.5/1000 above the caps' optical center, so every avatar
sat ~4.6 viewBox units (~1.5–1.8 px) too low. A follow-up rewrite
using canvas `actualBoundingBox*` metrics fixed it but pulled in a
runtime measurement rig (shared canvas, ref, state, layout effect,
`fonts.load` + `loadingdone`, plus combining-mark stripping) just
to place two uppercase letters.

Since initials are always uppercased, optical centering has a
closed form: `baseline = center + capHeight/2`. Real-world text
fonts have cap heights in a narrow ~0.66–0.73 em band (Marianne is
0.70), so:

    translateY(calc(var(--avatar-cap-height, 0.7) * 0.5em))

is exact for the stock font and within ~0.4 px for any plausible
replacement. No JS, SSR-safe, no first-paint jump, no font-loading
race. Accents float above the cap box instead of dragging the
letter down.

The single font-dependent number remaining (cap height) is exposed
as a CSS variable, so self-hosters overriding the font can override
it next to the font itself, or leave the default. Once
`text-box: trim-both cap alphabetic` ships broadly, even the
variable can go.
2026-09-05 10:43:22 +02:00
lebaudantoine 78ba03a52b 🐛(frontend) restore automatic lower-hand on speaking
Following the re-rendering optimization refactoring, the automatic
lower-hand feature broke: the way `isSpeaking` was read no longer
made sense once we limited how often components in the app
re-render.

Fix the detection so the raised hand is again lowered automatically
when the participant starts speaking, without relying on frequent
re-renders.
2026-09-05 00:29:50 +02:00
lebaudantoine d80d31897c 🔒️(frontend) fix HIGH CVEs in libexpat 2.8.2-r0
Address the following HIGH severity CVEs in libexpat 2.8.2-r0,
reported by Trivy:

* CVE-2026-66046
* CVE-2026-76641
2026-09-02 15:05:01 +02:00
kaelvar 63a7751072 ✨(frontend) add 1080p sending resolution option
The sending resolution selector stopped at 720p while `VideoPresets` already
exposes `h1080` (1920x1080), so publishers on a good uplink could not make use
of the capacity they had. Add "Very high definition (1080p)" above the existing
entries, translated in the five supported locales.

The default stays `h720`, so nothing changes unless a user goes and picks the
new entry. Being explicit about what that costs, since 1080p roughly doubles a
publisher's uplink: this is a per-user choice, and an instance operator has no
way today to decline it. Whether that warrants a server-side setting alongside
the existing `ApiConfig` flags is a call for maintainers — happy to add one if
you want it, rather than change the API contract unasked in a frontend PR.

While here, make the option list harder to get wrong. Resolutions now come from
a single `VIDEO_RESOLUTIONS` tuple that `VideoResolution` derives from, the
selector items are built by mapping over it against a
`Record<VideoResolution, string>` of labels — so a resolution cannot be added
to one and forgotten in the other — and a persisted value that is not in the
tuple falls back to `h720` instead of reaching `VideoPresets[...]` as
`undefined`, since `loadUserChoices` spreads localStorage without validating
it.

Known limitation, unchanged by this patch: `restartTrack` passes the resolution
as an `ideal` constraint, so a camera that cannot reach the selected height
degrades silently. That is already true of 720p on a 480p webcam; 1080p is the
first step where the gap is the common case rather than the edge one.
2026-09-02 15:05:01 +02:00
kaelvar 1ac1778521 🐛(frontend) keep the sending resolution picked while the camera is off
`handleVideoResolutionChange` did all of its work inside `if (videoTrack)`,
including `saveVideoPublishResolution`. With the camera off there is no camera
publication, so choosing a resolution did nothing at all: it was neither applied
nor recorded, while the selector went on showing the value the user had just
picked. Turning the camera back on then published at the old resolution, and so
did the next session.

Found on a self-hosted instance: a user set the sending resolution with the
camera off, turned it back on, and the publisher kept sending 720p. Nothing in
the UI suggested the choice had been dropped.

Persist the choice first and unconditionally, then restart the track only when
there is one to restart.

Persisting alone is not enough within a session. `roomOptions` is only read by
`new Room(...)`, so a store update never reaches a room that is already built.

Sync the VideoDeviceControl with the userChoiesStore resolution, as we did for
the device id and the processor configuration.

The early return is the honest shape here: with no live track there is nothing
to await, and the defaults above already cover what happens next.
2026-09-02 00:11:43 +02:00
lebaudantoine 21c57bffb4 🧑‍💻(devx) add a WebRTC stats and network throttling devtool
Introduce an in-app devtool that monitors WebRTC statistics in
real time and lets developers simulate various network scenarios,
including constraining the uplink and downlink bandwidth.

Makes it much easier to reproduce and investigate connectivity or
quality issues locally without depending on external tools.

The code was AI generated, and might contain some smell.
It's only enabled in dev, and not included in the production
build. Feel free to enhance it as needed.
2026-09-01 22:05:53 +02:00
lebaudantoine 86797d004c 🔖(minor) bump release to 1.30.0 2026-09-01 13:59:43 +02:00
moustique82 80d37595ad ✨(frontend) expose publish permissions in MediaStateObserver
The component exposes the local microphone and camera state so external
tools automating the frontend can read it. It does not expose whether
publication is allowed, so such a tool cannot tell a muted microphone from
one it is not permitted to unmute, and ends up offering a control that
silently does nothing.

Expose canPublishMicrophone and canPublishCamera in the event detail and as
data attributes, reusing the useCanPublishTrack hook that already gates the
web client controls. The permissions are part of the effect dependencies so
a mid-meeting permission change emits the event.
2026-08-31 16:55:36 +02:00
Cyril a1e7978348 ♿️(frontend) announce Escape close hint on side panels
Show "(Escape)" in close tooltip, add aria-describedby for SR keyboard hint.
2026-08-31 15:57:59 +02:00
Cyril 4fa044fa3e ♿️(frontend) close side panel with Escape key
useEscapeToClose: close panel on Escape, restore focus, let chat input bubble
2026-08-31 15:57:59 +02:00
snyk-bot 02714c869a ⬆️(frontend) upgrade @fontsource/opendyslexic from 5.2.5 to 5.3.0
Snyk has created this PR to upgrade @fontsource/opendyslexic from 5.2.5 to 5.3.0.

See this package in npm:
@fontsource/opendyslexic

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-08-31 14:37:44 +02:00
snyk-bot 826cfe0c62 ⬆️(frontend) upgrade @fontsource-variable/lexend from 5.2.11 to 5.3.0
Snyk has created this PR to upgrade @fontsource-variable/lexend from 5.2.11 to 5.3.0.

See this package in npm:
@fontsource-variable/lexend

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-08-31 14:10:02 +02:00
Louis-Jean Teitelbaum ab62ae71ea 🐛(frontend) fix chat text-area bug
Whenever a character was typed, the caret would jump
to the end of the textarea. Fixes #1656.
2026-08-31 13:59:39 +02:00
Miguel Victoria 3991235903 More natural language on interface 2026-08-31 13:59:21 +02:00
Villaquiranm a20a0a6b38 🌐(i18n) add Spanish language support
Add Spanish to the language selection system on both trees, with 872
frontend phrases and 145 backend ones translated from the French source.

The frontend to backend language map gains it too, so a Spanish browser
does not write an empty language to the account on every load.
2026-08-31 13:59:21 +02:00
snyk-bot 564b3dc595 ⬆️(frontend) upgrade posthog-js from 1.409.5 to 1.414.0
Snyk has created this PR to upgrade posthog-js from 1.409.5 to 1.414.0.

See this package in npm:
posthog-js

See this project in Snyk:
https://app.eu.snyk.io/org/lasuite-dinum-default/project/96ea03d8-8d09-493d-86bf-363f274e129e?utm_source=github&utm_medium=referral&page=upgrade-pr
2026-08-31 13:56:44 +02:00
leo 0a0cdae896 ✨(agent) support Voxtral realtime as inference engine
The Kyutai open-source model turned out not to be production-ready:
it caused disruptions in the production environment, especially on
long-running meeting sessions.

Switch to the Voxtral realtime model, which looks like a
credible competitor and behaves much better in our setup.

For now, the code handling the Voxtral realtime API lives directly
in the project. It could be extracted into an open-source package
later.

See PR #1277 for the full details of the implementation, proposed
by @cameldev.
2026-08-28 12:18:52 +02:00
lebaudantoine f1d3799434 🔖(minor) bump release to 1.29.0 2026-08-25 23:22:29 +02:00
lebaudantoine 943b81676b ✨(frontend) let authenticated users manage the lobby on trusted rooms
Frontend counterpart of the trusted-lobby backend feature: on
`trusted` rooms, any authenticated participant sees the waiting
notification and can accept or deny entry requests, not only admins
and owners.

Gating moves from role to capability: `useCanManageLobby` mirrors
the backend permission and derives from `useRoomData()`. Room
metadata is already synced into the query cache, so an access-level
change mid-meeting recomputes the capability on every client with no
new sync mechanism. It is only a UI gate; the backend re-checks
everything per request and fails closed.

Fetching moves into a single room-level `LobbyProvider`: the hook
was previously instantiated by two components and only worked
because React Query deduplicated their queries. The provider owns
one query and an explicit state machine - ways in (connection
established, ParticipantWaiting broadcast, panel opened, rights
regained while the panel is open) all arm and fetch; ways out
(rights lost, server 401/403) disarm and clear the cached list so
nothing stale can render.

Polling is tiered by audience since managers grow from a few admins
to potentially the whole room: 1s when acting (panel open),
10s when the notification is shown, and zero when the list is empty -
decided in the refetchInterval callback because structural sharing
suppresses data-keyed effects on identical empty responses. A quiet
room costs nothing; fetches are triggered by uncorrelated human
events, never synchronized across the room (the rights-regained
trigger is panel-gated for this reason).
2026-08-25 22:48:47 +02:00
lebaudantoine c02d54b6ff ⚡️(frontend) apply frugal constraint to the active meeting audio track
Apply the `VOICE_AUDIO_CONSTRAINTS` to the audio track used during
the active meeting to reduce bandwidth usage.

* Originally proposed by trummerschlunk to reduce bandwidth, but
  previously restricted to the join screen preview.
* Backport the standard voice constraints (48 kHz sample rate,
  mono channel, 16-bit sample size) to the active call session, as
  requested by the BBBA team.
2026-08-25 22:33:42 +02:00