Commit Graph
896 Commits
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 49dec686f1 feat(call): screenshare from inside the frame where delegation is missing; host stops reading the call frame (#43)
CI / Build & Quality Checks (push) Successful in 4m32s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 12s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 16m32s
Firefox, Safari and the WebKitGTK desktop app can't hand the user's click to
the call frame (no Capability Delegation), and getDisplayMedia needs it. The
host used to click EC's hidden footer button through the DOM instead, which
dies with same-origin. Now (pins element-call-embedded 0.25.0-lotus.20):

- those engines get `lotusFrameScreenshare`, the fork shows EC's own
  screenshare button in the frame, and the call bar and status bar hide
  theirs once controls_state reports `frameScreenshare`; the
  screenshare-audio mute stays;
- the room's call policy is pushed with io.lotus.set_frame_screenshare, so
  the frame button hides where the server would refuse a share, like ours;
- Chromium keeps the delegated io.lotus.set_screenshare from the host bar.

Removed the fallbacks for forks older than lotus.14, which read or clicked
EC's DOM: the screenshare/layout/settings/reactions/leave button lookups
and their MutationObservers, the frame-window hotkey binding, and the
speaking/muted tile scrape in useCallSpeakers (io.lotus.call_state is the
only source now). getCallDocument is gone; the host's only handle on the
frame is postMessage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-26 22:04:54 -04:00
Lotus CIandClaude Opus 5.5 502b9dfd83 feat(call): stop injecting CSS and key listeners into the call frame (#43)
CI / Build & Quality Checks (push) Successful in 3m5s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 9s
CI / Trigger Desktop Build (push) Successful in 8s
CI / Playwright smoke (e2e) (push) Successful in 12m42s
Pins @lotusguild/element-call-embedded 0.25.0-lotus.19.

- Styles: the fork now hides its own footer (`lotusHostControls=1`) and
  sets its root color-scheme from the theme, so the host no longer
  injects `#lotus-ec-styles` or sets inline styles on EC's DOM. The two
  other injected rules matched nothing in EC 0.25 (dead). The
  transparent background was already the fork's (`lotusTransparent`).
- Hotkeys: PTT / deafen keys pressed with focus inside the call frame now
  arrive as `io.lotus.hotkey` (the host sends the codes via
  `io.lotus.set_hotkeys`), instead of listeners on the frame's window.
  The window binding stays only for a fork that doesn't report `hotkeys`.
- Fixes (with lotus.19): pressing the deafen key M with focus in the call
  also hit EC's own "M = toggle mic" shortcut, so the first press turned
  the mic ON instead of deafening — even in push-to-talk mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-26 14:35:44 -04:00
Lotus CIandClaude Opus 5.5 024edbf546 feat(call): screenshare over the widget API with Capability Delegation (#43)
CI / Build & Quality Checks (push) Successful in 2m50s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 11s
CI / Trigger Desktop Build (push) Successful in 13s
CI / Playwright smoke (e2e) (push) Successful in 16m14s
The call bar's Start/Stop Screenshare no longer clicks EC's hidden button
on Chromium (incl. WebView2): it sends io.lotus.set_screenshare with
postMessage `{ delegate: "display-capture" }` inside the user's click, so
the frame can call getDisplayMedia on engines that require the click.
matrix-widget-api has no postMessage options, so its sendInternal is
swapped for that one synchronous send; delegation needs the frame's real
origin, not `*`.

Firefox, Safari and WebKitGTK (Linux desktop) have no delegation and
still click EC's button (needs same-origin, which is still on). Gated on
the fork reporting `screenshareAction` in controls_state.

Pins @lotusguild/element-call-embedded 0.25.0-lotus.17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-26 13:27:42 -04:00
Lotus CIandClaude Opus 5.5 06bb896a00 chore(deps): element-call 0.25.0-lotus.16 (#146)
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 12s
CI / Trigger Desktop Build (push) Successful in 12s
CI / Playwright smoke (e2e) (push) Successful in 9m38s
io.lotus.mic_level for the mute-button level meter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-25 12:14:29 -04:00
Lotus CIandClaude Opus 5.5 e91b5fe10e chore(deps): element-call 0.25.0-lotus.15 (element-call #36)
CI / Build & Quality Checks (push) Successful in 1m39s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 9s
CI / Trigger Desktop Build (push) Successful in 11s
CI / Playwright smoke (e2e) (push) Successful in 13m14s
Each person's call volume is now remembered across calls, reloads and
reconnects (per Matrix user, local only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-25 10:09:12 -04:00
Lotus CIandClaude Opus 5.5 c0e8334f03 chore(deps): element-call 0.25.0-lotus.14 (#43)
CI / Build & Quality Checks (push) Successful in 1m47s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 6s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 10m5s
Layout / settings / reactions over the widget API plus the
io.lotus.controls_state report the call bar now uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-24 22:24:06 -04:00
Lotus CIandClaude Opus 5.5 d9e7f77402 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.13
CI / Build & Quality Checks (push) Successful in 3m54s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 16s
CI / Trigger Desktop Build (push) Successful in 3s
CI / Playwright smoke (e2e) (push) Canceled after 15m41s
Brings the plain-language "Couldn't connect to voice" dialog with a
Try again button (replaces "Failed to connect to Livekit server
(Reason: ServerUnreachable)").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-23 21:11:11 -04:00
jaredandClaude Opus 5 738067cfc6 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.12
CI / Build & Quality Checks (push) Successful in 2m52s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 6s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Canceled after 2m31s
Brings io.lotus.screenshare_notice (EC #39) and the screenshare tile's
audio-state glyph (EC #38).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-23 15:15:25 -04:00
jaredandClaude Opus 5 81777ddfe1 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.11
Brings io.lotus.set_audio_output for the call-bar output switch (#119).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-20 15:38:14 -04:00
jaredandClaude Opus 5 6363939654 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.10
CI / Build & Quality Checks (push) Canceled after 0s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Secret scan (gitleaks) (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
Brings io.lotus.call_summary for the hangup readout (#143).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-20 14:14:28 -04:00
jaredandClaude Opus 5 6aa77552b8 ci(a11y): axe-core gate + accessibility-tree snapshots (#222)
CI / Build & Quality Checks (push) Successful in 1m38s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 9s
CI / Playwright smoke (e2e) (push) Successful in 8m52s
e2e/a11y.spec.ts runs @axe-core/playwright (WCAG 2.x A/AA) over the login
page, room timeline + composer, message options menu, thread panel, user
settings and room settings, failing on critical/serious findings other
than colour contrast (reported, not gated: generated avatar colours and
portal false positives). Aria snapshots of the composer, message menu,
thread panel and settings nav catch lost names/roles/live regions.

Burned down what the first run found:
- NavItem: callers' aria-selected is not valid on a div (axe critical);
  it now drives data-selected for styling and aria-current="page".
- Composer placeholder at 0.5 opacity was ~2.3:1; now P300.
- Voice-limit and explore custom-limit number inputs had no label.
- Thread panel is an <aside aria-label="Thread">; the settings modal is a
  role=dialog; the settings sections are a <nav>; the message action
  menu carries data-message-menu + a label.

Also allows WebKit's CI wording for the well-known probe ("Could not
connect … Connection refused") that failed run #2003.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-20 13:25:57 -04:00
jaredandClaude Opus 5 b69099a862 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.9
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 3m59s
Brings speakingWhileMuted in io.lotus.call_state for the 'You're muted' nudge (#117).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 23:17:21 -04:00
jaredandClaude Opus 5 821760131c chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.8
Picks up the fork change that shows the SFU token service's refusal reason
("Can't join this call — This voice channel is full.") instead of the generic
OPEN_ID_ERROR page. Verified headless with a routed 403 on /sfu/get.
(lotus.6/7 were tagged but failed CI lint; lotus.8 is the published one.)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 17:09:44 -04:00
jaredandClaude Opus 5 68ce13f081 chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.4
CI / Build & Quality Checks (push) Successful in 1m42s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 2m3s
Picks up the fork fix where "Mute Screenshare Audio" was undone whenever the
sharer stopped and re-shared (EC's own volume controls wrote volume 1 through
the same setter). The mute now rides the renderer's `muted` prop like deafen
does. Docs: fork feature table gains the deafen/screenshare-audio row; the
soundboard + quality rows are marked verified end-to-end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 00:16:49 -04:00
jaredandClaude Opus 5 e9d07513d2 merge: upstream cinny v4.12.7 into lotus
Brings the 4.12.7 fixes: "%" sign wrapping below the composer input and
emoji autocompletion overwriting the preceding element. The release's
security half (sanitize-html, react-router-dom) was already applied in
91def3ad (we are on newer versions than upstream ships).

Resolved: kept our package.json/lockfile (the fork is ahead on every
dependency — React 19, router 7, i18next 26…), version 4.12.7-lotus, our
README/CONTRIBUTING, our "Lotus Chat vN" branding via pkg.version, and
the deleted GitHub-only workflows stay deleted. Note upstream announces
it is replacing matrix-js-sdk with its own SDK and pausing PRs; future
syncs will get harder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-15 13:07:32 -04:00
Krishan 8967c13878 chore: Release v4.12.7 (#3105)
Release v4.12.7
2026-09-16 01:55:15 +10:00
renovate[bot] fcca7c21fb fix(deps): update dependency folds to v2.7.2 (#3103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-16 01:35:33 +10:00
jaredandClaude Opus 5 df64b9ca56 chore(call): bump @lotusguild/element-call-embedded to 0.25.0-lotus.3 (mono mic capture)
CI / Build & Quality Checks (push) Successful in 2m52s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 9s
CI / Trigger Desktop Build (push) Successful in 10s
CI / Playwright smoke (e2e) (push) Successful in 2m25s
Firefox captured stereo audio interfaces as stereo and published them
left-only with browser processing off; the fork now requests mono mic
capture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-14 21:30:42 -04:00
jaredandClaude Opus 5 81a6d9c9ed fix(call): serve RTC transports to Element Call over MSC4515 — calls work again
CI / Build & Quality Checks (push) Successful in 2m6s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 9s
CI / Trigger Desktop Build (push) Successful in 10s
CI / Playwright smoke (e2e) (push) Successful in 1m42s
Since the upstream v0.25.0 merge, Element Call in widget mode no longer
reads .well-known for its LiveKit transport; it asks the HOST via the
widget API (org.matrix.msc4515.get_rtc_transports, capability
org.matrix.msc4515.rtc_transports). cinny never granted the capability
nor implemented WidgetDriver.getRtcTransports(), so discovery returned
nothing and every join failed with "Call is not supported"
(MISSING_MATRIX_RTC_TRANSPORT) — the [LocalMembership] Multiple
Transport Errors line in the browser console.

- matrix-widget-api 1.17.0 -> 1.18.0 (adds MSC4515; also changes the
  sendDelayedEvent driver signature, adapted — parent delay ids were
  removed from the draft).
- Grant MSC4515RtcTransports in getCallCapabilities.
- CallWidgetDriver.getRtcTransports(): homeserver /rtc/transports
  (MSC4143) first, then the .well-known org.matrix.msc4143.rtc_foci list
  (what matrix.lotusguild.org advertises today). Unit-tested.

Server side needs no change: the well-known already carries the livekit
focus and the JWT service answers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-14 19:11:41 -04:00
jaredandClaude Opus 5 57ebf4481b chore(call): bump @lotusguild/element-call-embedded to 0.25.0-lotus.2
CI / Build & Quality Checks (push) Canceled after 11s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Secret scan (gitleaks) (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
Second CI-published fork release: focus works in grid/1:1 and clears on
leave, decoration ring sized/hidden correctly and CDN-pinned, denoise
prefetch + fallback + suspend-while-muted, screenshare cap as a real
budget, soundboard refuses while muted, shared clip AudioContext,
call_state at ≤2/s, transparent-theme contrast guard, plus the
request_state / denoise_state fromWidget actions the host now handles.
Verified: bundle in dist/public/element-call carries all eight
io.lotus.* actions; boot check passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-13 01:28:05 -04:00
jaredandClaude Opus 5 0cb0f91e43 test(e2e): Playwright smoke test — boot tier always, E2EE composer tier with credentials
Boot tier (runs against vite preview of dist/): login screen renders with
no page or console errors, sw.js is served and registers, the bundled
Element Call mounts in a frame with no failed asset requests. E2EE tier
(skipped without E2E_HOMESERVER/E2E_USER/E2E_PASSWORD): password login,
create an encrypted room, send text, attach a compressed JPEG, and assert
at the network level that every send is m.room.encrypted with no
plaintext body/url/file — the regression test #6/#7/#11 lacked.
Secrets and local usage documented in LOTUS_TESTING.md.

Fixes #90

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-13 00:56:23 -04:00
jaredandClaude Opus 5 91def3ad34 chore(deps): clear npm audit — pdfjs-dist 6, react-router-dom, sanitize-html, katex, uuid
npm audit --omit=dev went from 11 advisories (6 high) to 0:
- pdfjs-dist 5.7.284 -> 6.3.289 (GHSA-hq66-cqwq-w95j, arbitrary JS on
  opening a malicious PDF; the client has an inline PDF viewer). Only API
  change for us: getDocument() no longer accepts a bare string.
- react-router-dom 7.15.1 -> 7.18.3 (open redirect, DoS, RSC advisories)
- sanitize-html 2.17.4 -> 2.17.7 (javascript: URI bypasses; not
  exploitable in our config per the audit, bumped anyway)
- katex 0.16.11 -> 0.16.47 (\htmlData attribute validation)
- uuid under @giphy/js-util overridden to >= 11.1.1 (giphy imports v4,
  which is unchanged)
- npm audit fix for transitive brace-expansion / js-yaml / nanoid / postcss

Verified: tsc clean, 1000 tests pass, production build succeeds and
dist/pdf.worker.min.js is still copied from the new package.

Fixes #24

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 20:00:56 -04:00
jaredandClaude Opus 5 9a85a48704 chore(call): bump @lotusguild/element-call-embedded to 0.25.0-lotus.1
CI / Build & Quality Checks (push) Successful in 1m25s
CI / Trigger Desktop Build (push) Successful in 6s
First CI-published fork release: upstream v0.25.0 base plus the audit
fixes (deafen via global output mute, denoise dead-mic fallback, single
denoise processor, strict widget origin check, quality-cap clearing,
call_state standalone guard) — LotusGuild/element-call #1 #2 #10 #11
#15 #31. Verified locally: the bundle lands in dist/public/element-call/
with all six io.lotus.* actions present.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 19:18:21 -04:00
renovate[bot] c143b30060 fix(deps): update dependency react-router-dom to v6.30.6 [security] (#3099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-10 17:08:39 +10:00
renovate[bot] 9a49bf4661 fix(deps): update dependency sanitize-html to v2.17.7 [security] (#3091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-10 17:04:33 +10:00
Krishan 33f4ba3674 chore: Release v4.12.6 (#3053) 2026-08-01 21:57:30 +10:00
renovate[bot]andKrishan 90837f7230 fix(deps): update dependency sanitize-html to v2.17.6 [security] (#3051)
* fix(deps): update dependency sanitize-html to v2.17.5 [security]

* update to latest version

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
2026-08-01 21:51:49 +10:00
renovate[bot] 730a748aca fix(deps): update dependency folds to v2.7.1 (#3049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-27 21:10:41 +10:00
jaredandClaude Opus 4.8 17bd50cc4e feat(crypto): QR-code device verification (alongside emoji SAS)
CI / Build & Quality Checks (push) Successful in 11m7s
CI / Trigger Desktop Build (push) Successful in 7s
B2 of the Matrix protocol-gaps roadmap, gate-green (688 tests):
- Enable QR verification methods (show/scan/reciprocate) in initMatrix.
- Extend DeviceVerification: the Ready step offers your own QR (byte-mode encode
  via qrcode), a camera 'Scan their QR code' flow, and an emoji fallback; the
  Started step routes reciprocate → a confirm step (useVerifierShowReciprocateQr)
  or SAS as before.
- New QrScanner component: getUserMedia + jsQR, handing the raw binaryData bytes
  to request.scanQRCode (BarcodeDetector is string-only, so can't be used).
- Adds qrcode + jsqr (small, pure-JS, client-only); build-verified under rolldown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 11:30:23 -04:00
jaredandClaude Opus 4.8 a899d7d3a8 fix(privacy): generate invite QR locally instead of api.qrserver.com (H5)
The Share Room QR was fetched from the third-party api.qrserver.com, leaking
which rooms a user shares (and failing offline / under strict CSP). Now rendered
locally via qrcode.react (QRCodeSVG) — no network request, works offline. Added a
white quiet-zone container so the code scans on any theme; dropped the qrError
fallback (local generation can't fail the same way). Removed api.qrserver.com
from the prod CSP img-src (matrix repo). Build verified (rolldown interop OK).
Verification steps added to LOTUS_TESTING.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:19:22 -04:00
jaredandClaude Opus 4.8 ef573376ac chore(deps): matrix-js-sdk 41.6.0-rc.0 → 41.7.0 stable
Off the release candidate onto stable: pulls matrix-sdk-crypto-wasm 18.3.1 (a
security update) + MSC4140 delayed-event auth fixes. Thread/receipt API
signatures spot-checked unchanged (sendEvent threadId overloads, sendReceipt
unthreaded arg). Gates green: tsc/build/658 tests. E2EE runtime behavior needs
the usual live smoke (send/receive in an encrypted room, call keys).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 00:19:21 -04:00
jaredandClaude Opus 4.8 96f7187031 perf(audit): emojibase lazy-split, SW precache, Prism subset, lazy images
- emojibase (~965 KB) is now fully lazy: plugins/emoji.ts loads compact data +
  shortcode maps via a memoized dynamic import (rejections reset the memo so a
  mid-deploy chunk 404 can retry); reaction labels degrade to the raw glyph
  until loaded. Consumers get FRESH array references on load (the module arrays
  populate in place — same-ref state updates would skip re-render and leave
  emoji search empty; reviewer-caught). Verified out of the eager graph.
- Service worker precaches hashed assets (workbox precacheAndRoute, 82 entries
  ~10.8 MB incl. the crypto wasm): repeat visits stop re-downloading the app.
  index.html is NOT precached — navigations stay network-first so deploys are
  picked up immediately; the media-auth fetch handler is untouched.
- ReactPrism: curated 21-language set — chunk 574 KB → 71 KB.
- Timeline inline images get loading="lazy".
- Removed dead dompurify (+types); sanitize-html is the real sanitizer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 00:19:16 -04:00
jaredandClaude Opus 4.8 ed51c39fe7 feat(messages): KaTeX math rendering (P4-4)
Renders LaTeX via spec data-mx-maths spans/divs (KaTeX render of the attr,
children as fallback) and conservative $…$ / $$…$$ text detection (escape-aware,
currency-guarded, never inside code/pre). KaTeX + CSS load lazily on first math
(ReactPrism pattern) — verified absent from the eager bundle. Sanitizer
unchanged by design (we render post-sanitize from attr/text; no incoming MathML
accepted). +14 unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 21:19:02 -04:00
jaredandClaude Opus 4.8 e80ebd35cb test: add unit-test harness (tsx + node:test) + first suite for utils/common
Addresses the "no automated test suite" gap. Chose Node's built-in test runner
via tsx rather than vitest: the project is on Vite 8.0.14, ahead of vitest's
supported Vite range, so vitest would fight peer deps. tsx is build-independent.

- `npm test` → `node --import tsx --test $(find src -name '*.test.ts')` (works on
  Node 20 local + 24 CI without relying on --test glob support).
- src/app/utils/common.test.ts: 15 tests covering the pure helpers (bytesToSize,
  time formatters, binarySearch, parseGeoUri, slash trimmers, nameInitials,
  randomStr, suffixRename, splitWithSpace, promise-settled helpers, etc.) —
  asserts actual behavior, traced from source.
- common.ts: folds import made `import type` (it's types only) so the module is
  pure and testable without loading folds/CSS.
- tsconfig excludes *.test.ts (tsx transpiles tests; eslint isn't type-aware so
  it still lints them); added an informational CI "Unit tests" step (promote to a
  hard gate by dropping continue-on-error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 08:45:22 -04:00
jaredandClaude Opus 4.8 89cf171efc call: consume self-built Element Call fork + activate Lotus features
CI / Build & Quality Checks (push) Successful in 11m5s
CI / Trigger Desktop Build (push) Successful in 25s
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>
2026-06-30 01:33:52 -04:00
jaredandClaude Opus 4.8 24d6460e4c chore: remove Sentry.io entirely
We no longer use Sentry. Removed:
- @sentry/react + @sentry/vite-plugin (package.json + lockfile)
- Sentry.init in index.tsx and the VITE_SENTRY_DSN env (.env.production)
- @sentry/vite-plugin + the SENTRY_AUTH_TOKEN sourcemap-upload path in
  vite.config.js (sourcemap now always false) and the CI env var
- Sentry.ErrorBoundary in App.tsx -> react-error-boundary's ErrorBoundary with a
  folds-native fallback (Box/Text/Button + config tokens), which also resolves
  the native-cinny audit's raw-#hex/#5865f2 fallback finding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 22:21:09 -04:00
jaredandClaude Opus 4.8 a8038bb534 chore(deps): remove unused direct lodash dependency
lodash was a direct dependency pinned to 4.18.1 but is not imported anywhere
in src/. It remains available transitively (slate-react/slate-dom/inquirer/
commitizen depend on it), so removing the direct declaration changes nothing
at runtime — it just drops an unused, oddly-pinned direct dep. (The audit's
"non-existent version" claim was a false positive: 4.18.1 resolves from the
registry with a valid integrity hash and loads correctly.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:49:40 -04:00
Krishan 5e00d517eb revert: "chore(deps): lock file maintenance" (#3006)
Revert "chore(deps): lock file maintenance (#2995)"

This reverts commit 70b8d03c02.
2026-06-28 20:12:49 +10:00
jaredandClaude Opus 4.8 c0f9867218 Merge upstream v4.12.3 (Element Call 0.20.1) into lotus
CI / Build & Quality Checks (push) Successful in 10m42s
CI / Trigger Desktop Build (push) Successful in 10s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 04:11:41 -04:00
Krishan 69515e8e81 chore: release v4.12.3 (#2996)
Release v4.12.3
2026-06-22 23:58:25 +10:00
renovate[bot] 70b8d03c02 chore(deps): lock file maintenance (#2995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-22 23:42:57 +10:00
KrishanandAjay Bura e37971456a chore(deps): update dependency element-call from 0.19.1 to v0.20.1 (#2992)
* chore(deps): update dependency @element-hq/element-call-embedded from 0.19.1 to v0.20.1

* fix reaction button click

* fix call settings button query

* fix screenshare and spotlight button state update

* fix incomming call text alignment

* prevent displaying call notification for voice rooms

* disable mic/video icon when toggle in progress

* update matrix-js-sdk

---------

Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
2026-06-20 21:20:25 +10:00
jaredandClaude Opus 4.8 04b56ffacd feat(denoise): add self-hosted DeepFilterNet 3 ML noise-suppression model
Integrate DeepFilterNet 3 (deepfilternet3-noise-filter@1.2.1) as a new
client-side denoise model id 'deepfilternet', mirroring the DTLN pattern.

The npm package ships only an ESM whose AudioWorklet processor + wasm-bindgen
glue are inlined as a string (loaded via a Blob URL — no CDN for the worklet).
Its only runtime fetches are a single-threaded df_bg.wasm and an ONNX model
tarball, which previously loaded from an external CDN. We now VENDOR both
(build/denoise-vendor/deepfilternet/v2/...) and self-host them under
denoise/deepfilternet/, overriding the package's cdnUrl so nothing hits the
upstream CDN — keeping it self-hosted / Tauri-CSP safe.

The wasm is single-threaded (no SharedArrayBuffer / atomics / imported shared
memory), so it needs no COOP/COEP cross-origin isolation and runs fine in EC's
non-isolated iframe. Runs at 48 kHz fullband. Any init/runtime failure falls
back to the raw mic, like the other models.

- vite.config.js: copy ESM + vendored wasm/model into the EC denoise dir with a
  required-asset guard that aborts the build if any entry is missing.
- build/lotus-denoise.js: 'deepfilternet' branch — dynamic-import the ESM, build
  a DeepFilterNet3Core pointed at the self-hosted base, await init, return the
  worklet node; 48 kHz; raw-mic fail-safe preserved.
- denoisePipeline.ts: 'deepfilternet' branch for the in-app tester + sampleRate.
- settings.ts: add 'deepfilternet' to DenoiseModelId + getSettings whitelist.
- lotusDenoiseUtils.ts: add the comparison-chart row.
- General.tsx: add the "DeepFilterNet 3 (beta)" dropdown option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 19:57:08 -04:00
jaredandClaude Opus 4.8 86272b6b08 fix(calls): wire DTLN ML denoise correctly via @workadventure JS API
CI / Build & Quality Checks (push) Successful in 10m25s
CI / Trigger Desktop Build (push) Successful in 6s
The prior DTLN attempt (89a2321d) broke the build (missing dep, wrong
`cinny/` asset paths) and typecheck (`'dtln'` not in DenoiseModelId), and was
wired against an API the package doesn't expose. @workadventure/noise-
suppression is not a flat AudioWorklet — it's a self-contained ES module whose
processor name is `workadventure-noise-suppression` and which resolves its own
LiteRT WASM + TFLite models via import.meta.url. Driving it by hand-rolled
addModule + processorOptions cannot work.

- Re-add @workadventure/noise-suppression@0.0.4 (package.json + lockfile).
- vite: copy the package's whole dist/ tree intact to
  denoise/workadventure/ (preserving assets/ + vendor/litert) so import.meta
  resolution works at runtime; fail the build if the entry module is missing.
- shim: for the DTLN model, dynamic-import denoise/workadventure/audio-worklet
  .js and use createNoiseSuppressionAudioWorklet(ctx, { bypassUntilReady })
  to build the node; RNNoise/Speex keep their direct flat-worklet path. Async
  init errors are logged + reported and fall back to the raw mic.
- Restore 'dtln' in DenoiseModelId (+ settings coercion), the model chart, and
  the settings dropdown, labelled "(beta)".

DTLN builds and is fully self-hosted, but its in-call audio is UNVERIFIED in
this environment — needs a real-call test. DeepFilterNet stays excluded (CDN
asset loading, incompatible with self-hosting / Tauri CSP).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:11:45 -04:00
jaredandClaude Opus 4.8 6634b2b8a2 fix(calls): make ML denoise build-honest + gate desktop trigger on CI
CI / Build & Quality Checks (push) Successful in 10m41s
CI / Trigger Desktop Build (push) Successful in 6s
Audit/repair of the multi-model denoise work so it actually builds and only
exposes working, self-hosted models.

- Complete the DTLN/DFN3 revert: uninstall @workadventure/noise-suppression
  and deepfilternet3-noise-filter (package.json + lockfile), drop the unused
  DTLN asset-copy block from vite.config.js (was shipping ~2MB of unused
  tflite/wasm), and narrow DenoiseModelId to the bundled models (rnnoise,
  speex). Coerce any retired persisted model value back to the default.
- Fix General.tsx CI typecheck failures introduced by the denoise UI: restore
  three imports the rewrite deleted (useDateFormatItems, SequenceCardStyle,
  useTauriUpdater), add the missing denoise/sound imports, and correct
  hallucinated Folds props (Text has no variant/bold; Box uses
  alignItems/justifyContent). tsc now passes with 0 errors.
- Harden the vite denoise plugin: required RNNoise/Speex/gate assets and the
  shim now fail the build loudly if missing (instead of a silent warn that
  shipped a broken ML feature), and the index.html shim injection is verified.
- CI: move the cinny-desktop submodule bump into ci.yml as a `trigger-desktop`
  job gated on `needs: build`, and delete the standalone trigger-desktop.yml.
  A failing push no longer kicks off the slow Tauri builds in parallel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 01:42:21 -04:00
jared b006f9804a feat(calls): integrate verified DTLN and DFN3 ML noise suppression models
- Verified package layouts and integration paths for @workadventure/noise-suppression (v0.0.4) and deepfilternet3-noise-filter (v1.2.1).
- Updated build configuration to correctly copy WASM, TFLite, and ONNX assets.
- Integrated DTLN and DeepFilterNet initialization logic into the audio shim.
- Enabled all four models (RNNoise, Speex, DTLN, DFN3) in Settings UI.
2026-06-16 01:06:12 -04:00
jaredandClaude Opus 4.8 5deed79b42 feat(calls): 3-tier mic noise suppression with on-device ML (P5-30)
CI / Build & Quality Checks (push) Successful in 10m33s
Trigger Desktop Build / trigger (push) Successful in 6s
Replace the boolean call noise-suppression setting with a 3-way control
(Off / Browser-native / ML beta) in Settings -> General -> Calls.

- Off: noiseSuppression=false to Element Call
- Browser-native: EC's built-in WebRTC suppressor (prior default)
- ML (beta): on-device RNNoise (@sapphi-red/web-noise-suppressor)

Element Call captures the mic inside its iframe and publishes to LiveKit,
so the host can't reach that track; LiveKit's Krisp filter is Cloud-only
(we self-host the SFU) and EC's own RNNoise PR #3892 is unmerged. The ML
tier instead injects a same-origin pre-init shim into the vendored EC
index.html (build/lotus-denoise.js, wired by the lotusDenoise vite plugin)
that patches getUserMedia and routes the captured mic through an RNNoise
AudioWorklet before LiveKit sees it -- the same post-capture pipeline as
#3892, with no EC fork/AGPL/rebase burden. Falls back to the raw mic if
setup fails; keeps echoCancellation/AGC on the raw capture.

- settings.ts: callNoiseSuppression -> 'off'|'browser'|'ml' + legacy
  boolean migration (true->browser, false->off)
- CallEmbed/useCallEmbed: tier maps to noiseSuppression param and appends
  lotusDenoise=ml (native suppressor off in ML mode)
- vite.config.js: copy RNNoise worklet/wasm + shim into the EC bundle and
  inject the shim <script> before EC's module entry
- docs: LOTUS_FEATURES.md, LOTUS_TODO.md (P5-30 done)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:29:59 -04:00
jaredandClaude Sonnet 4.6 8214387479 chore: track package-lock.json, revert gitignore change
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 23:18:15 -04:00
Krishan 80fd8863c9 chore: Release v4.12.2 (#2956)
Release v4.12.2
2026-05-23 22:10:21 +10:00
Krishan cf2810b35e chore: remove semantic-release and keep script (#2946)
* chore: remove semantic-release

* chore: keep workflow to be tag publish based

* chore: fix typo
2026-05-23 22:02:49 +10:00