Commit Graph
70330 Commits
Author SHA1 Message Date
Thore Cimbal c90ac4d175 .npmrc in den Docker-Build-Kontext kopieren
docker_web scheiterte am Tag v0.6.0-rc.1: pnpm loeste
@sorb/threadnet-call-embedded gegen registry.npmjs.org auf und bekam 404.

Die COPY-Zeile hat .npmrc noch nie mitgenommen. Bis pnpm 10 fiel das nicht
auf, weil --frozen-lockfile die im Lockfile gepinnte Tarball-URL nahm und
gar nichts aufzuloesen brauchte. Upstream bringt pnpm 11 mit
minimumReleaseAgeStrict: true - das prueft fuer jeden Lockfile-Eintrag das
Veroeffentlichungsdatum und braucht dafuer Registry-Metadaten. Damit wird der
Scope wieder aufgeloest, und ohne .npmrc gegen die falsche Registry.

Genau diesen Ablauf sagt die .npmrc selbst voraus (management#0055) - der Fix
lag nur an einer Stelle, an die der Docker-Build nie herankam.

Fuer den Scope ist kein Geheimnis noetig, der Lesezugriff ist anonym.
v0.6.0-rc.2
2026-08-19 12:00:00 +00:00
Thore Cimbal 9622bd3c3a fix(merge): three defects my conflict resolution introduced, all caught by the build
The CSS conflict boundary ran through the middle of a rule, so concatenating both
sides swallowed the closing brace of .userStatusEmoji - 27 opening braces against 26
closing ones. Restored.

The JSX was worse. Our block is a ternary: show call participants instead of the
message preview. Moving it into upstream's new RoomListItemContent carried the preview
branch along while upstream's own preview block stayed behind it, leaving a duplicated
preview and an unbalanced </div>. Removed the duplicate.

getInitials stayed behind unused in RoomListItemView after moving to
RoomListItemContent, which the compiler flagged. Removed.

None of this was visible by reading the diff; the build found all three. Both packages
compile now, and the artifact carries the dfn3 model, so the noise suppression from
#0054 came through the merge intact.
v0.6.0-rc.1
2026-08-19 12:00:00 +00:00
Thore Cimbal 88c4e155ce Merge upstream v1.12.26 - reconnect the fork to Element Web (management #0099)
The repo had no upstream ancestry: a whole tree arrived in one commit in May, so
every update meant re-applying our patches by hand onto a fresh checkout, and a file
Element moved would take our lines with it silently.

The real base was found by measuring tree distance across develop rather than trusting
the changelog: deadd548, not the v1.12.17 tag. With that set as a temporary graft, this
merge computed as a proper three-way merge - 32 conflicts instead of 1757.

Resolutions, each decided rather than defaulted:

- 24 GitHub workflows stay deleted; we build on GitLab CI.
- MImageBody.tsx is gone upstream, migrated to MVVM. Our ClamAV error label moved into
  ImageBodyViewModel.computeErrorLabel, ahead of the DecryptError branch, matching what
  VideoBodyViewModel and FileBodyViewModel already do.
- Upstream extracted the room list item body into RoomListItemContent. Our call
  participants list and its getInitials helper moved there; both sides' CSS classes and
  both sides' props are kept.
- matrix-js-sdk follows upstream at 42.2.0 - our git ref pin was a workaround for a
  stale ref, and following upstream is the point of this merge.
- Element Call stays ours. Checked before deciding: @element-hq/element-call-embedded
  is referenced nowhere in the tree, while webpack.config.ts needs
  @sorb/threadnet-call-embedded, so taking upstream's line would have deleted the noise
  suppression from #0054 without a word.

The lockfile was regenerated with pnpm 11.20.0, which upstream now requires through
devEngines. CI already runs corepack enable, and onFail: download makes it fetch that
version by itself.

Not yet accepted: this needs a build and the ClamAV functional test - send an encrypted
file, receive a rejected one - before it goes near main.
2026-08-19 12:00:00 +00:00
Thore Cimbal fa5dcc5fbc docs(fork): the import is not the v1.12.17 tag, and that changes the graft plan
Adding upstream as a second remote was meant to make updates viewable as a diff. The
first measurement contradicted what this document has claimed since August: diffing
v1.12.17 against the import commit yields 783 files, 115 of which exist only on our
side and 62 only in the tag.

The telling ones are MVVM files - TextualBodyViewModel.tsx and RovingTabIndex.tsx -
absent from the v1.12.17 tag, present in v1.12.26 and present in our import. The
changelog stops at 1.12.17 because it is only written at release time. So the base is
a develop state after the 1.12.17 release, not the tag.

That matters for the plan rather than for the code: grafting onto v1.12.17 would build
every future merge on an invented ancestry. Finding the real ancestor means searching
develop history, which a shallow tag fetch cannot do.

The remote is local configuration, so the setup is written down here instead of
committed.
2026-08-19 12:00:00 +00:00
Thore Cimbal e1e9a19f78 config(desktop): disable_custom_urls here too, not only in the web deployment
Decision sorb. The desktop client loads this config.json rather than the one the web
deployment serves, so setting it in element-values.yaml alone would have hardened the
web client and left the builds that actually get handed out untouched - the same trap
as the themes rollout.

It hides the edit button next to the server name, so the homeserver cannot be changed
through the interface, and it makes the 401/403 login error name the server instead of
staying generic.

Stated in the comment rather than discovered later: this is a surface restriction, not
a technical one. MatrixChat still accepts hs_url from the query string in the mobile
registration and registration-confirmation paths without consulting the setting. It
narrows the way to GHSA-wrcp-5v3v-3j6v, which affects everything below 1.12.22 while
we run 1.12.17, and it does not replace the upstream update that management #0099 is
actually about.

Takes effect with the next desktop build.
2026-08-19 12:00:00 +00:00
RiotRobot c43ef70b55 v1.12.26 2026-08-18 13:42:12 +00:00
RiotRobot 0cbebb0a0a Upgrade dependency to matrix-js-sdk@42.2.0 2026-08-18 13:37:03 +00:00
Thore CimbalandClaude Opus 5 240021b754 ci: build when .npmrc changes
The previous commit added no pipeline run: .npmrc is not in the changes list that
triggers one. That file decides which registry dependencies come from, so a change
to it deserves a build rather than a wave-through. This commit touches
.gitlab-ci.yml, which is on the list - so it verifies itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:00:00 +00:00
Thore CimbalandClaude Opus 5 be323edf10 build: pin the @sorb scope to rohana instead of relying on the lockfile
Nothing in this repo said where @sorb/threadnet-call-embedded comes from. It
worked only because pnpm-lock.yaml carries the full tarball URL and CI installs
with --frozen-lockfile; the first version bump drops that cover. Today that ends
in a 404 because the name is still free on npmjs - which makes our only
protection the fact that nobody has claimed @sorb there. Claim it and the same
command resolves silently against a stranger's package, in the exact moment a
fresh download looks normal.

The checked-in .npmrc closes the wrong path instead of documenting it. It holds a
URL and no secret: read access to the registry is anonymous.

Upstream's .gitignore ignores /.npmrc - reasonable where the file holds tokens,
and it would have swallowed this fix without a word. The negation is explicit and
carries the reason, so the next merge does not quietly restore the hole.

Verified in an isolated tree, not assumed: without the file pnpm resolves to
npmjs and fails; with it the scope resolves to rohana at the same integrity hash
the repo lockfile already carries; with rohana unreachable the install fails
outright rather than falling back to npmjs. pnpm install --frozen-lockfile still
passes and leaves the lockfile untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:00:00 +00:00
b0cff953ea [Backport staging] macOS Fix: Can't change homeserver on login or press the logout button on the splash/loading srceen (#34706)
Co-authored-by: David Langley <davidl@element.io>
2026-08-18 12:23:55 +01:00
77e0ab1750 [Backport staging] Cachebust languages.json (#34711)
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-08-18 12:23:40 +01:00
Thore CimbalandClaude Opus 4.8 6cdb1858a2 build(web): pull in threadnet-call-embedded 0.19.2-threadnet.12
Sender verification for the AI filter: after attaching, the fork now checks
that the RTCRtpSender actually carries the processed track and enforces the
swap if LiveKit skipped it silently - the Safari field finding where level
0-100 made no audible difference. The log line states the outcome.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.4
2026-08-17 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 7e6b08774b build(web): pull in threadnet-call-embedded 0.19.2-threadnet.11
Opens the AI noise suppression gate after the passed two-person acceptance:
checkbox and slider return to the in-call audio settings, clients with the
setting enabled get the filter again. The rollback lever for any regression is
the gate in threadnet-call, not a deployment revert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.3
2026-08-17 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 51b6691caa build(web): pull in threadnet-call-embedded 0.19.2-threadnet.10
Way B from #0054: the AI filter attaches to the local microphone track after
publication, with its own AudioContext on just that track - no webAudioMix, no
processor key in the capture defaults in any state. The feature gate stays
closed; a single test client opts in via two localStorage keys. For everyone
else this build behaves identically to .9.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.2
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 45dc53155b build(web): pull in threadnet-call-embedded 0.19.2-threadnet.9
The .8 widget broke unmuting in production (both with the AI filter on and off);
threadnet-call dcc8643 restores the upstream off-path and hard-gates the feature
until the webAudioMix decision. Verified locally end to end: the installed
package carries the model assets, webpack lands them under
webapp/widgets/element-call/assets/dfn3, and the widget index.html references
the .9 bundle (0.19.2-threadnet.9+dcc86431).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.1
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 7abe9023f9 build(web): pull in threadnet-call-embedded 0.19.2-threadnet.8
Brings the AI noise suppression (ADR-0018) into the widget we actually ship.
The .7 package built and ran fine but carried no model assets, because the
embedded Vite config had publicDir off — the filter would have been dead only
inside the widget, and nowhere else.

Verified the whole chain locally rather than trusting the green build: the
23 MB of model and wasm arrive under webapp/widgets/element-call/assets/dfn3,
which is the path the widget requests at runtime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.0
2026-08-16 12:00:00 +00:00
Thore CimbalandClaude Opus 4.8 e7b00288ed docs: add group-rules pointer (CLAUDE.md) and repo-specific AGENTS.md
Field test F-011 found four of five components carried no pointer file, so a
session landing here had no path to the group rules at all. CLAUDE.md is the
one-line pointer the check looks for; AGENTS.md links the canonical rules in the
management repo (with the Gitea mirror URL for readers outside the lab) and
otherwise carries only what is specific and easy to get wrong here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:00:00 +00:00
RiotRobot 5e76766ee2 v1.12.26-rc.0 2026-08-12 11:50:01 +00:00
RiotRobot 8c9601a229 Upgrade dependency to matrix-js-sdk@42.2.0-rc.2 2026-08-12 11:36:10 +00:00
David BakerandGitHub 5349566611 Fix layout of user status dropdown (#34668)
* Bump to compound 10.0.1

For fix to the dropdown menu layout

* Snapshot
2026-08-12 10:20:52 +00:00
David LangleyandGitHub badb35bd1f Timeline MVVM 1 - Shared TimelineView and Overlay Buttons (#34464)
* Add shared timeline overlay buttons and view contract

The TimelineOverlayButtons presentational component (unread bar, jump-to-bottom)
plus the shared TimelineView VM contract (types) and its i18n strings. Consumed
by the TimelineView virtualizer in a follow-up commit.

* Add shared TimelineView component (TanStack Virtual)

Headless TanStack-Virtual timeline driven from RoomTimelineViewModel: anchorTo:"end"
viewport stability across prepends/trims, cold-load cover, jump-to via scrollToOffset,
and visible-state reporting via the virtualizer's onChange. Adds the @tanstack/react-virtual
dependency and the virtual-core isValidAnchorItem patch (runtime + types).

* Use Compound spacing tokens in timeline overlay and clarify comments

- Use Compound spacing tokens for overlay margins/insets instead of raw px
- Drop the stale Virtuoso reference from the overlay CSS comment
- Note the real-world duration of COLD_CAP_FRAMES
- Describe each load phase in plain English

* Remove jargon and explain/expand things in simple terms.

* Use time rather than a frame count and improve description.
2026-08-12 08:40:26 +00:00
ZackandGitHub 23a666a9de Fix notification badge sizing (#34655)
* Fix notification badge sizing

* Update snapshots

* Add space panel badge regression test

* Update snapshot

* Updated test to test assert screenshot

* Update snapshot to look correct

* Update playwright test

* fix aliased issues
2026-08-12 07:04:55 +00:00
00a5546fe6 [create-pull-request] automated change (#34666)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-08-12 06:59:54 +00:00
rbondessonandGitHub 72d8d9f60f Move EventTile to shared components - #4b (#34565)
* Define the shared EventTile shell contract

* Create a functional shared shell skeleton

* Implement the thread rendering branch

* Implement notification and threads-list branches

* Implement file and default timeline branches

* Add shared stories and complete shell tests

* Adding state flags, stories and styling

* Correct DOM order for IRC branch

* Refine EventTileView Storybook shell and fixtures

* Fix the room story for modern layout

* Fix Bubble layout

* Fix IRC layout

* Fix ThreadsList and Thread

* Fix File and Notification

* Cleanup of stories and styling

* Fix lint errors

* Added baseline screenshots

* Rename to avoid knip errors

* Avoid the knip error

* Fix test fail on click

* Use separate div wrappers for slots instead of relying on cloning components.

* Renamed names for slot classes

* Changed to conditional state classes

* Change layout and shape variants to classes

* Clean up and reorder for better readability

* Fix Sonar issues

* Fix Sonar issues
2026-08-12 06:53:40 +00:00
7711207393 Keep a file's extension when it is renamed in the desktop save dialog (#34601)
Electron's save dialog offers only "All Files" unless it is handed filters,
so someone who replaced "photo.jpg" with "photo" got a file with no extension
at all and no way to open it. The browser does not do this: it offers the
file's own type, and puts the extension back when the name arrives without
one.

The download now names its type before the dialog goes up, and the "Save
image as..." context menu passes the same filters. A file with no extension
to preserve is left alone rather than being given a meaningless filter, and
"All Files" stays on the list so anyone who really wants an extensionless
name can still pick it.

Tests: a download with an extension gets filters, one without does not, and
the context menu path is covered as well.

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-08-11 16:40:18 +00:00
hayyaksiandGitHub 400bf348ef Update the pinned message banner when a pinned message is edited (#34631)
* Update the pinned message banner when a pinned message is edited

The pinned events are fetched from a memo keyed on the pinned event ids, and editing a
pinned message leaves those ids untouched. Nothing invalidated the memo, so the banner
and the pinned messages card went on rendering the original text while the timeline
showed the edit.

Edits landing on a pinned event now invalidate it. An edit to any other event is
ignored, so an active room does not refetch the pinned set on every message.

* Address review: apply pinned edits in place instead of refetching

The counter that forced the memo to recompute was never read by the memo, so the
dependency array had to carry a variable that meant nothing to the computation.
The edit event is already in hand when the timeline fires, so holding onto it
rather than a tally gives the memo something it can actually consume.

Applying that edit to the fetched copy also removes a round-trip: the pinned set
was being fetched again purely to pick up content the client already had. It has
to run in an effect rather than in the memo because replacing an event notifies
whatever is rendering it, and that must not happen during a render.

The tests now assert the content the hook hands back rather than the number of
fetches, which is what the banner actually shows.
2026-08-11 16:38:35 +00:00
91e8f3bdae Keep a copy of the audio buffer so the WAV fallback can run (#34481)
* Keep a copy of the audio buffer so the WAV fallback can run

decodeAudioData detaches the buffer it is given, so the catch block handed an
already detached buffer to decodeOgg and it threw "Cannot perform Construct on
a detached ArrayBuffer" before it could re-encode. The fallback could never
run and the user saw an error instead of their voice message.

Fixes https://github.com/element-hq/element-web/issues/24904

* Assert the fallback buffer is usable instead of constructing a view for its side effect

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-08-11 16:08:59 +00:00
9e8a57c81c Use the dark code highlighting stylesheet in the dark-custom theme (#34491)
dark-custom imported highlight.js's atom-one-light palette while every other
dark theme imports atom-one-dark, so syntax-highlighted code blocks rendered
dark grey on the theme's dark background. The neighbouring import on the next
line already pulls in the dark GitHub markdown stylesheet.

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-08-11 16:04:33 +00:00
f37623f054 Update the member list invite button when power levels change (#34475)
canInvite was only recomputed on third party invite state events, so granting or
revoking the power to invite left the member list header button in its previous
enabled or disabled state until the list was reopened.

Recompute it on RoomStateEvent.Update as well, which also covers the space join
rule branch in canInviteTo(), matching RoomSummaryCardViewModel.

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-08-11 16:02:37 +00:00
hayyaksiandGitHub 0a86a6b93d Stop large audio playback when the media element ends (#34495) 2026-08-11 15:51:08 +00:00
hayyaksiandGitHub 46d55f25a2 Tell the user when registration is being rate limited (#34519)
* Tell the user when registration is being rate limited

* Disable the submit button while registration is rate limited

The warning now sits with the button it is disabling, below the server picker,
rather than above it, so it reads as the reason you cannot continue.

The rate limit message moves out of errorText into its own state field to get
there, which also leaves the position of every other registration error alone.

When the server tells us how long to wait, we ask again ourselves once that
time is up, so the form comes back without the user having to guess when a
reload is worth it.

* Set the rate limit warning to medium weight

Scoped to the registration rate limit warning so the other auth errors keep
the bold they have always had.
2026-08-11 15:39:39 +00:00
hayyaksiandGitHub 76aeb783b6 Open linkified room aliases and permalinks in the app rather than the browser (#34507) 2026-08-11 15:31:35 +00:00
Michael TelatynskiandGitHub 7897b0c71f Build docker image containing modules (#34657)
Switch to bake to simplify the building of 2 images
2026-08-11 13:03:24 +00:00
Will HuntandGitHub f69ffb2120 @element-hq/element-web-module-api 1.16.0 (#34654) 2026-08-11 10:51:11 +00:00
3a196c7722 Fix spacing in user status dropdown (#34589)
* Exploration of a virtuoso-powered emoji picker

moved to shared components

Fable generated

* fix pnpm lock

* format & fix some lint issues

* wrong import

* fix lint warning

* Fix off-by-one

and remove manual overflow adjustment: let's leave the default unless
it turns out to be necessary. Emoji should not take that long to load.

* Convert to functional component

* WIP: change to one big virtuoso scroller

* Change to use virtuoso's own onRangeChanged

and santitise category data and how it's passed around

* Convert Tabs to functional component

and put the focusing behaviour back with it just keeping track of
refs by itself.

* Absorb two line config file into main component

* Actually add the config to the main file

* Convert emoji to functional

Also make selected always defined and use useCallback.

* QuickReactions to functional component

* Non-default exports & doc

* Search to functional component

* Well it seems to work just fine now

* Use ref prop

* fix lockfile AGAIN

* lint

* Remove default export

* Remove some mx_ classnames and fix the inputRef

to make the arrow keys in the search box work (well, work as much as
they ever did).

* Remove last of the mx_ id / classnames

(except the one in the test)

* Use useMemo to memoize

* No need to export props interface (I think?)

and fix comment now we don't do the mutation stuff anymore

* Fix test

* Fix axe violations & add screenshots

* Avoid comparing dom snapshots in test

* Allow more before or after, just compare order of the ones present in both.

* Switch existing usages to new emoji picker

and kill the old one with fire

* Unused stuff

* Remove i18n strings

* Fix some tests

* Update screenshots

* Fix test

by removing the last of the weird memoized-but-mutated data structure

* Experimental custom status

* Screenshot

* snapshots

* Update button label and fix test

* Move the string somewhere more sensible than 'a11y'

* i18n lint

* Give the emojis IDs so aria-activedescendant works

* Fix more tests

* Add a small wrapper emoji picker component

This lets us easily memoize the recent emojis when the emoji picker is opened.
Also it saves a bit of boilerplate.

* Remove old emojipicker css

* Typos

Co-authored-by: David Langley <davidl@element.io>

* Use compound constants

* Rethemendex

* Test for custom status

* Use catalog version for emojibase

* Add comments

* More comments

* Fix comment

* More comments

* more comments (and make them uniform)

* More comments

* Fix pnpm lock again

* Another comment

* Add option to hide quick reactions (and preview) bar

* Fix test

to use emoji not in the quick reactions bar

* Apply button types to new version

* Add comment

* Disable screenshot

as per comment

* Fix hover / background / border styles

* Screenshot

* Don't use the reactions row because it's gone now

* Fix spacing in user status dropdown

Updates to renderItem based Dropdown, allowing the item in the dropdown
to be customised.

Requires https://github.com/element-hq/compound-web/pull/538 (and
will be required to update to the version of compound it gets
released in).

* Update to new compound

* snapshots

* Fix tests

---------

Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
Co-authored-by: David Langley <davidl@element.io>
2026-08-10 15:20:29 +00:00
Florian DurosandGitHub 3a7be0d3a9 Update room list documentation (#34359) 2026-08-10 14:19:19 +00:00
David BakerandGitHub fef1f0e0dd Show & clear your own on-a-call status (#34613)
* Show & clear your own on-a-call status

The user menu / settings now reflects your own on-a-call status
and pressing the 'clear' button will clear both m.status and m.call,
whichever are set.

* Update function name

* Fix tests

And make the clear status function simpler by just throwing if either fails.

* Fix more tests
2026-08-10 14:13:42 +00:00
hayyaksiandGitHub 0b264a482e Reverse a confirmed autocomplete with a single undo (#34635)
Confirming a completion produces two model updates for one keystroke: the
completion replaces the range and closes the autocomplete, and then the wrapper
closes it again. The model reported that second close like any other change, and
since it carries no input type and no diff the history manager treats it as bulk
input and always pushes, leaving two identical states on the undo stack.

The model now ignores a close which has nothing to replace and arrives once the
autocomplete is already gone. Dismissing with escape, and the fallback close when
there was no selection to confirm, both happen while it is still open and are
unaffected.

Tests: a single undo restores the typed text after a completion, and the
redundant close causes no further render.
2026-08-10 14:01:38 +00:00
613ee3c324 feat(widget): Rtc transports discovery for widgets (#34393)
* feat(widget): Rtc transports discovery for widgets

* fix linter

* update matrix-widget-api and js-sdk

* fix eslint-plugin-element-call path

* update widget-api: Parent-id delegation is deprectated

---------

Co-authored-by: Robin <robin@robin.town>
2026-08-10 13:45:13 +00:00
d54cddcb1d Enable some more oxlint rules (#34193)
* Enable some more oxlint rules

* Iterate

* Iterate

* Update packages/shared-components/src/room/timeline/event-tile/EventTileView/E2ePadlock/E2ePadlock.tsx

Co-authored-by: Hubert Chathi <hubert@uhoreg.ca>

---------

Co-authored-by: Hubert Chathi <hubert@uhoreg.ca>
2026-08-10 12:25:55 +00:00
hayyaksiandGitHub 4807ff1568 Allow a notification keyword to start with a dot (#34574)
Both notification settings tabs stored a keyword under a push rule whose id
was the keyword itself. Homeservers reserve the rule ids beginning with a
dot for the rules they define, and Synapse refuses to create any other, so
saving a keyword such as "...push complete" failed outright and the whole
save was reported as an error.

The rule id is an internal name that the user never sees — both tabs list
keywords by the rule's pattern — so it is the id that gives way. It now
drops the leading dots while the pattern keeps the keyword exactly as it
was typed, which is what is matched against messages. Two keywords can
want the same id that way, so a number is appended when one is taken,
which also keeps "banana" from overwriting the rule for ".banana".

Tests: the shared reconciler and the older tab both store a dotted keyword
under an accepted id, and two keywords differing only by a leading dot get
an id each.
2026-08-10 11:24:15 +00:00
Michael TelatynskiandGitHub 127ae519c5 Deflake Vitest tests (#34570)
* Deflake Vitest MatrixChat test

* Update MatrixChat.test.tsx

* Stabilise further

* Stabilise further

* Stabilise init
2026-08-10 10:23:56 +00:00
62b5cc65db Custom user status (#34386)
* Exploration of a virtuoso-powered emoji picker

moved to shared components

Fable generated

* fix pnpm lock

* format & fix some lint issues

* wrong import

* fix lint warning

* Fix off-by-one

and remove manual overflow adjustment: let's leave the default unless
it turns out to be necessary. Emoji should not take that long to load.

* Convert to functional component

* WIP: change to one big virtuoso scroller

* Change to use virtuoso's own onRangeChanged

and santitise category data and how it's passed around

* Convert Tabs to functional component

and put the focusing behaviour back with it just keeping track of
refs by itself.

* Absorb two line config file into main component

* Actually add the config to the main file

* Convert emoji to functional

Also make selected always defined and use useCallback.

* QuickReactions to functional component

* Non-default exports & doc

* Search to functional component

* Well it seems to work just fine now

* Use ref prop

* fix lockfile AGAIN

* lint

* Remove default export

* Remove some mx_ classnames and fix the inputRef

to make the arrow keys in the search box work (well, work as much as
they ever did).

* Remove last of the mx_ id / classnames

(except the one in the test)

* Use useMemo to memoize

* No need to export props interface (I think?)

and fix comment now we don't do the mutation stuff anymore

* Fix test

* Fix axe violations & add screenshots

* Avoid comparing dom snapshots in test

* Allow more before or after, just compare order of the ones present in both.

* Switch existing usages to new emoji picker

and kill the old one with fire

* Unused stuff

* Remove i18n strings

* Fix some tests

* Update screenshots

* Fix test

by removing the last of the weird memoized-but-mutated data structure

* Experimental custom status

* Screenshot

* snapshots

* Update button label and fix test

* Move the string somewhere more sensible than 'a11y'

* i18n lint

* Give the emojis IDs so aria-activedescendant works

* Fix more tests

* Add a small wrapper emoji picker component

This lets us easily memoize the recent emojis when the emoji picker is opened.
Also it saves a bit of boilerplate.

* Remove old emojipicker css

* Typos

Co-authored-by: David Langley <davidl@element.io>

* Use compound constants

* Rethemendex

* Test for custom status

* Use catalog version for emojibase

* Add comments

* More comments

* Fix comment

* More comments

* more comments (and make them uniform)

* More comments

* Fix pnpm lock again

* Another comment

* Add option to hide quick reactions (and preview) bar

* Fix test

to use emoji not in the quick reactions bar

* Apply button types to new version

* Add comment

* Disable screenshot

as per comment

* Fix hover / background / border styles

* Screenshot

* Don't use the reactions row because it's gone now

* Trim overly verbose comment

---------

Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
Co-authored-by: David Langley <davidl@element.io>
2026-08-10 10:21:14 +00:00
RobinandGitHub bb2083423b Fix mistaken reference to element_call.disable in docs (#34647)
The actual name that is respected by the app is 'disable', not 'disabled'.
2026-08-10 09:44:38 +00:00
a9e3b6c2fc [create-pull-request] automated change (#34646)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-08-10 09:26:55 +00:00
22b0758bfb Round the room preview dialog like everything around it (#34597)
The preview shown for a room you have not joined — the one carrying the
join and ask-to-join buttons — has 4px corners, where the room preview
card it stands in place of, the panel form of the same bar, and the
surfaces beside it all have 8px. Sitting among them it reads as sharp,
which is what was reported.

Nothing else in the rule changes, and the panel form already had the right
value, so the two forms of the bar now agree with each other as well.

Co-authored-by: David Baker <dbkr@users.noreply.github.com>
2026-08-10 09:24:26 +00:00
ZackandGitHub ce74b5f614 Refactor NotificationBadge to shared MVVM (#33843)
* Refactor NotificationBadge to shared MVVM

* Added snapshot images

* Replace legacy NotificationBadge with adapter

* Fix Eslint

* Use regular NotificationBadge prop names

* Remove legacy NotificationBadge selectors

* Update TAC badge Playwright selectors

* Rename NotificationBadgeAdapter to NotificationBadge

Addresses review feedback: the component is the app-facing badge, not an
adapter, so name it NotificationBadge.

* Make NotificationBadgeView isClickable a required boolean

Addresses review feedback: there is no semantic difference between
`isClickable: false` and `isClickable: undefined` (both render the
non-interactive variant), so the field should be a plain boolean. The
view model always provides it.

* Drop redundant comparison guards in NotificationBadge view model setters

Addresses review feedback: Snapshot.merge already compares each field
with Object.is and only emits when something actually changed, so the
manual equality guards in each setter were redundant. Recompute the
snapshot unconditionally and let merge dedupe.

* Resolve notification badge labels inside NotificationBadgeView

Addresses review feedback: static UI labels belong in the view, not the
snapshot. The view now resolves the knock and unsent-message strings via
useI18n, and the snapshot exposes a showUnsentTooltip boolean instead of
a prebuilt tooltipLabel (and drops knockLabel entirely). Adds the
room|knock_sent and notifications|message_didnt_send keys to the
shared-components i18n catalogue so they resolve in tests and storybook.

* Remove unused translation because of shared components movement

* Fix typescript issue

* Fix lint issue

* Remove unused notification badge count setting

* Pass notification badge class names explicitly
2026-08-10 08:32:23 +00:00
Thore Cimbal d3bd5b20c2 feat(desktop): give the desktop client a way to obtain its logs
The desktop variant had no bug_report_endpoint_url at all, so the whole section stayed hidden and a desktop user could not produce logs for a report. Setting local shows a Download logs button that writes them to the device and uploads nothing.

Matches what the web client now serves. Takes effect with the next desktop build.

Refs axion1337.chat/ThreadNet-Web#9
2026-08-09 12:00:00 +00:00
Thore Cimbal 3cb43f5149 ci: refuse to overwrite an already published release image
Moving a tag retriggers docker_web, which would rebuild a published release from old source against today's base images and dependencies and push it over the existing one. On 2026-08-09 that nearly happened to v0.4.0 while the rewritten tags were force-pushed; it was averted only because the protected registry variables were unavailable in that window, so docker login failed first.

The job now aborts if the target tag already exists in the registry. latest-ci stays overwritable on purpose - it names the newest state, not a version, and carries no promise.

Closes axion1337.chat/ThreadNet-Web#14
2026-08-09 12:00:00 +00:00
Valere FedronicandGitHub a73f8c389b fix test using private field (#34618)
* fix test using private field

* review: use readable name
2026-08-08 14:28:29 +00:00