From fa9de52a94ebde25abdee9adf135244931ebcd8c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 27 Jan 2025 10:07:35 +0000 Subject: [PATCH 001/211] Initial runtime Modules work --- modules/README.md | 2 + modules/opendesk/README.md | 1 + modules/opendesk/element-web/package.json | 26 ++++++++++++ modules/opendesk/element-web/src/index.ts | 29 +++++++++++++ modules/opendesk/element-web/tsconfig.json | 8 ++++ modules/opendesk/element-web/vite.config.ts | 45 +++++++++++++++++++++ 6 files changed, 111 insertions(+) create mode 100644 modules/README.md create mode 100644 modules/opendesk/README.md create mode 100644 modules/opendesk/element-web/package.json create mode 100644 modules/opendesk/element-web/src/index.ts create mode 100644 modules/opendesk/element-web/tsconfig.json create mode 100644 modules/opendesk/element-web/vite.config.ts diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000000..4464820095 --- /dev/null +++ b/modules/README.md @@ -0,0 +1,2 @@ +Example and modules we maintain live here. +Each module may have multiple components, e.g. element-web + synapse, which are structured in subdirectories therein. diff --git a/modules/opendesk/README.md b/modules/opendesk/README.md new file mode 100644 index 0000000000..a5fd105d0a --- /dev/null +++ b/modules/opendesk/README.md @@ -0,0 +1 @@ +Example module to load all the nordeck opendesk legacy modules & deprecated customisations as a single modern module. diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json new file mode 100644 index 0000000000..33838f0eea --- /dev/null +++ b/modules/opendesk/element-web/package.json @@ -0,0 +1,26 @@ +{ + "name": "nordeck-plugin", + "private": true, + "version": "0.0.0", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "devDependencies": { + "@element-hq/element-web-module-api": "^0.1.0", + "@nordeck/element-web-guest-module": "^2.0.0", + "@nordeck/element-web-opendesk-module": "^0.5.0", + "@nordeck/element-web-widget-lifecycle-module": "^1.0.1", + "@nordeck/element-web-widget-toggles-module": "^0.1.0", + "@types/node": "^22.10.7", + "@types/react": "^18", + "@vitejs/plugin-react": "^4.3.4", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "rollup-plugin-external-globals": "^0.13.0", + "typescript": "^5.7.3", + "typescript-plugin-css-modules": "^5.1.0", + "vite": "^6.0.11", + "vite-plugin-node-polyfills": "^0.23.0" + } +} diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts new file mode 100644 index 0000000000..a84736686e --- /dev/null +++ b/modules/opendesk/element-web/src/index.ts @@ -0,0 +1,29 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; +import NordeckOpendeskModule from "@nordeck/element-web-opendesk-module"; +import GuestModule from "@nordeck/element-web-guest-module"; +import WidgetLifecycleModule from "@nordeck/element-web-widget-lifecycle-module"; +import WidgetTogglesModule from "@nordeck/element-web-widget-toggles-module"; +import { ComponentVisibilityCustomisations } from "@nordeck/element-web-guest-module/customisations/ComponentVisibility"; + +class OpendeskModule implements Module { + public static readonly moduleApiVersion = "^0.1.0"; + + public constructor(private api: Api) {} + + public async load(): Promise { + this.api._registerLegacyModule(NordeckOpendeskModule); + this.api._registerLegacyModule(GuestModule); + this.api._registerLegacyModule(WidgetLifecycleModule); + this.api._registerLegacyModule(WidgetTogglesModule); + this.api._registerLegacyComponentVisibilityCustomisations(ComponentVisibilityCustomisations); + } +} + +export default OpendeskModule satisfies ModuleFactory; diff --git a/modules/opendesk/element-web/tsconfig.json b/modules/opendesk/element-web/tsconfig.json new file mode 100644 index 0000000000..3847e92f35 --- /dev/null +++ b/modules/opendesk/element-web/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "declaration": true + }, + "include": ["src"] +} diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts new file mode 100644 index 0000000000..019aa4e2e0 --- /dev/null +++ b/modules/opendesk/element-web/vite.config.ts @@ -0,0 +1,45 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "element-web-plugin-opendesk-nordeck", + fileName: "index", + formats: ["es"], + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + rollupOptions: { + external: ["react", "@matrix-org/react-sdk-module-api"], + }, + }, + plugins: [ + react(), + nodePolyfills({ + include: ["events"], + }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], + define: { + process: { env: {} }, + }, +}); From 1c51ab2b14128bf743ea4650748135b14ddb97b2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 11:52:33 +0000 Subject: [PATCH 002/211] Iterate --- modules/opendesk/element-web/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 33838f0eea..605cde94b8 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -19,7 +19,6 @@ "react-dom": "^18.0.0", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "typescript-plugin-css-modules": "^5.1.0", "vite": "^6.0.11", "vite-plugin-node-polyfills": "^0.23.0" } From 49ccaa5e0010b40c800c9fa93d637f623109d6b6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 13:13:06 +0000 Subject: [PATCH 003/211] Iterate --- modules/opendesk/element-web/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 605cde94b8..128b7b2fc5 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -6,6 +6,9 @@ "sideEffects": [ "**/*.css" ], + "scripts": { + "lint:ts": "tsc --noEmit" + }, "devDependencies": { "@element-hq/element-web-module-api": "^0.1.0", "@nordeck/element-web-guest-module": "^2.0.0", From 30f1b612099ad2357e8e506247780177fd89e142 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 13:23:58 +0000 Subject: [PATCH 004/211] Iterate --- modules/opendesk/element-web/tsconfig.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/opendesk/element-web/tsconfig.json b/modules/opendesk/element-web/tsconfig.json index 3847e92f35..8f4f6b8ace 100644 --- a/modules/opendesk/element-web/tsconfig.json +++ b/modules/opendesk/element-web/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { - "outDir": "lib", - "declaration": true + "outDir": "lib" }, "include": ["src"] } From 3b8921a692585d855f47d6acf09a64c11bd53fd5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 17:38:52 +0000 Subject: [PATCH 005/211] Iterate --- modules/opendesk/element-web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 128b7b2fc5..8bdf06f063 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -7,6 +7,7 @@ "**/*.css" ], "scripts": { + "prepare": "vite build", "lint:ts": "tsc --noEmit" }, "devDependencies": { From 646f3da6380e42646d6b3b21f1d4682db083b15f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 08:35:40 +0000 Subject: [PATCH 006/211] Iterate --- modules/opendesk/element-web/src/index.ts | 63 ++++++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index a84736686e..fa6f544390 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -7,10 +7,37 @@ Please see LICENSE files in the repository root for full details. import { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; import NordeckOpendeskModule from "@nordeck/element-web-opendesk-module"; -import GuestModule from "@nordeck/element-web-guest-module"; +import GuestModule, { + assertValidGuestModuleConfig, + GUEST_MODULE_CONFIG_KEY, + GUEST_MODULE_CONFIG_NAMESPACE, + shouldShowComponent as shouldShowComponentShared, +} from "@nordeck/element-web-guest-module"; import WidgetLifecycleModule from "@nordeck/element-web-widget-lifecycle-module"; import WidgetTogglesModule from "@nordeck/element-web-widget-toggles-module"; -import { ComponentVisibilityCustomisations } from "@nordeck/element-web-guest-module/customisations/ComponentVisibility"; + +declare module "@element-hq/element-web-module-api" { + interface Config { + [GUEST_MODULE_CONFIG_NAMESPACE]: { + [GUEST_MODULE_CONFIG_KEY]: { + guest_user_homeserver_url: string; + skip_single_sign_on?: boolean; + guest_user_prefix?: string; + }; + }; + } +} + +declare global { + interface Window { + // XXX: temporary hack until we rewrite everything in modern modules + mxMatrixClientPeg: { + safeGet(): { + getSafeUserId(): string; + }; + }; + } +} class OpendeskModule implements Module { public static readonly moduleApiVersion = "^0.1.0"; @@ -18,11 +45,33 @@ class OpendeskModule implements Module { public constructor(private api: Api) {} public async load(): Promise { - this.api._registerLegacyModule(NordeckOpendeskModule); - this.api._registerLegacyModule(GuestModule); - this.api._registerLegacyModule(WidgetLifecycleModule); - this.api._registerLegacyModule(WidgetTogglesModule); - this.api._registerLegacyComponentVisibilityCustomisations(ComponentVisibilityCustomisations); + const { api } = this; + + api._registerLegacyModule(NordeckOpendeskModule); + api._registerLegacyModule(GuestModule); + api._registerLegacyModule(WidgetLifecycleModule); + api._registerLegacyModule(WidgetTogglesModule); + api._registerLegacyComponentVisibilityCustomisations({ + // XXX: the following is cribbed out of `@nordeck/element-web-guest-module` due to + // it making imports incompatible with workspaces + /** + * Determines whether or not the active MatrixClient user should be able to use + * the given UI component. If shown, the user might still not be able to use the + * component depending on their contextual permissions. For example, invite options + * might be shown to the user but they won't have permission to invite users to + * the current room: the button will appear disabled. + * @param {UIComponent} component The component to check visibility for. + * @returns {boolean} True (default) if the user is able to see the component, false + * otherwise. + */ + shouldShowComponent(component): boolean { + const config = api.config.get(GUEST_MODULE_CONFIG_NAMESPACE)?.[GUEST_MODULE_CONFIG_KEY] ?? {}; + assertValidGuestModuleConfig(config); + + const myUserId = window.mxMatrixClientPeg.safeGet().getSafeUserId(); + return shouldShowComponentShared(config, myUserId, component); + }, + }); } } From bab388854ca467c357a1d19d6191b832816b7c68 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 09:02:46 +0000 Subject: [PATCH 007/211] Iterate --- modules/opendesk/element-web/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index fa6f544390..5c0e746c8d 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; import NordeckOpendeskModule from "@nordeck/element-web-opendesk-module"; import GuestModule, { assertValidGuestModuleConfig, @@ -16,6 +15,8 @@ import GuestModule, { import WidgetLifecycleModule from "@nordeck/element-web-widget-lifecycle-module"; import WidgetTogglesModule from "@nordeck/element-web-widget-toggles-module"; +import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; + declare module "@element-hq/element-web-module-api" { interface Config { [GUEST_MODULE_CONFIG_NAMESPACE]: { From abc38414458bcf3856374b69b137fa5bcb0fb9b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 10:48:35 +0000 Subject: [PATCH 008/211] Iterate --- modules/example/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 modules/example/README.md diff --git a/modules/example/README.md b/modules/example/README.md new file mode 100644 index 0000000000..e69de29bb2 From b8aba36f092f74bb6142cfa5a8749bbb1d0c2574 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 30 Jan 2025 10:58:23 +0000 Subject: [PATCH 009/211] Wire up tests & sonarcloud --- modules/example/README.md | 0 modules/opendesk/element-web/package.json | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 modules/example/README.md diff --git a/modules/example/README.md b/modules/example/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 8bdf06f063..ca9e7b3f2d 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -8,7 +8,8 @@ ], "scripts": { "prepare": "vite build", - "lint:ts": "tsc --noEmit" + "lint:ts": "tsc --noEmit", + "test": "echo no tests yet" }, "devDependencies": { "@element-hq/element-web-module-api": "^0.1.0", From 386073c0e84ceb4f943d622ac3183f94ceb90ec8 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Tue, 4 Feb 2025 09:21:06 -0500 Subject: [PATCH 010/211] Merge commit '01af446be7cbd63b38f2fe35c2c9a25fac4fdef8' as 'modules/restricted-guests/synapse' --- .../restricted-guests/synapse/.dockerignore | 3 + .../restricted-guests/synapse/CHANGELOG.md | 45 ++++ modules/restricted-guests/synapse/Dockerfile | 6 + modules/restricted-guests/synapse/README.md | 93 ++++++++ modules/restricted-guests/synapse/mypy.ini | 2 + .../restricted-guests/synapse/package.json | 21 ++ .../restricted-guests/synapse/pyproject.toml | 10 + .../synapse/scripts/run_in_venv.js | 58 +++++ modules/restricted-guests/synapse/setup.cfg | 48 +++++ .../synapse/synapse_guest_module/__init__.py | 17 ++ .../synapse/synapse_guest_module/config.py | 23 ++ .../synapse_guest_module/guest_module.py | 177 +++++++++++++++ .../guest_registration_servlet.py | 93 ++++++++ .../synapse_guest_module/guest_user_reaper.py | 142 ++++++++++++ .../synapse/synapse_guest_module/py.typed | 0 .../synapse/tests/__init__.py | 134 ++++++++++++ .../synapse/tests/test_guest_module.py | 202 ++++++++++++++++++ .../tests/test_guest_registration_servlet.py | 91 ++++++++ .../synapse/tests/test_guest_user_reaper.py | 128 +++++++++++ modules/restricted-guests/synapse/tox.ini | 37 ++++ 20 files changed, 1330 insertions(+) create mode 100644 modules/restricted-guests/synapse/.dockerignore create mode 100644 modules/restricted-guests/synapse/CHANGELOG.md create mode 100644 modules/restricted-guests/synapse/Dockerfile create mode 100644 modules/restricted-guests/synapse/README.md create mode 100644 modules/restricted-guests/synapse/mypy.ini create mode 100644 modules/restricted-guests/synapse/package.json create mode 100644 modules/restricted-guests/synapse/pyproject.toml create mode 100644 modules/restricted-guests/synapse/scripts/run_in_venv.js create mode 100644 modules/restricted-guests/synapse/setup.cfg create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/__init__.py create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/config.py create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/guest_module.py create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/py.typed create mode 100644 modules/restricted-guests/synapse/tests/__init__.py create mode 100644 modules/restricted-guests/synapse/tests/test_guest_module.py create mode 100644 modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py create mode 100644 modules/restricted-guests/synapse/tests/test_guest_user_reaper.py create mode 100644 modules/restricted-guests/synapse/tox.ini diff --git a/modules/restricted-guests/synapse/.dockerignore b/modules/restricted-guests/synapse/.dockerignore new file mode 100644 index 0000000000..5b69719716 --- /dev/null +++ b/modules/restricted-guests/synapse/.dockerignore @@ -0,0 +1,3 @@ +# ignore all files except the build folder +* +!/synapse_guest_module/*.py diff --git a/modules/restricted-guests/synapse/CHANGELOG.md b/modules/restricted-guests/synapse/CHANGELOG.md new file mode 100644 index 0000000000..0bf2ec3720 --- /dev/null +++ b/modules/restricted-guests/synapse/CHANGELOG.md @@ -0,0 +1,45 @@ +# @nordeck/synapse-guest-module + +## 2.0.0 + +### Major Changes + +- 5539790: Guest users are now only allowed in Ask to Join rooms + +## 1.0.0 + +### Major Changes + +- a05ce45: First stable release. + +## 0.3.1 + +### Patch Changes + +- 344b7bf: Update the repository name + +## 0.3.0 + +### Minor Changes + +- 27680cd: Provide a dedicated registration endpoint to not interfere with the original + registration endpoint. +- 997f47b: Deactivate guest users after a configured expiration time. + +## 0.2.0 + +### Minor Changes + +- 00c92e0: Don't include guest users in the search results. + +## 0.1.0 + +### Minor Changes + +- b43b066: Disable certain homeserver-wide actions (create room, invite user) for guest users. + +## 0.0.1 + +### Patch Changes + +- Add initial version. diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile new file mode 100644 index 0000000000..3941d9511e --- /dev/null +++ b/modules/restricted-guests/synapse/Dockerfile @@ -0,0 +1,6 @@ +FROM debian:bookworm-slim + +WORKDIR /src +ADD synapse_guest_module /src/synapse_guest_module + +CMD ["cp", "-r", "/src/synapse_guest_module", "/modules"] diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md new file mode 100644 index 0000000000..3fb51aacba --- /dev/null +++ b/modules/restricted-guests/synapse/README.md @@ -0,0 +1,93 @@ +# Synapse Guest Module + +A [pluggable synapse module](https://matrix-org.github.io/synapse/latest/modules/index.html) to restrict the actions of guests. + +**Features:** + +1. Provides an endpoint that creates temporary users with a same pattern (default: `guest-[randomstring]`). +2. The temporary users have a mandatory displayname suffix (default: ` (Guest)`) that they can't remove from their profile. +3. The temporary users are limited in what they can do (examples: create room, invite users). +4. The temporary users won't be returned by the user directory search results. +5. The temporary users are disabled after an expiration timeout (default: `24 hours`). + +## Synapse configuration + +This modules requires that the homeserver has the following configuration in their `homeserver.yaml`: + +```yaml +# Required so Element is able to show the room preview where the user can login. +allow_guest_access: true +``` + +## Module installation + +Copy the `synapse_guest_module` folder into the python modules path. +This can also be achieved by the [`PYTHONPATH` environment variable](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH). + +Add module configuration into `modules` section of `homeserver.yaml`: + +```yaml +modules: + - module: synapse_guest_module.GuestModule + config: {} +``` + +## Module configuration + +The module provides (optional) configuration options: + +- `user_id_prefix` - the prefix of the usernames that are created by this module. Default: `guest-`. +- `display_name_suffix` - the suffix added to the display name of guest users. Default: ` (Guest)`. +- `enable_user_reaper` - if true, the module disables all users that are older than the configured expiration time. Default: `true`. +- `user_expiration_seconds` - the expiration time in seconds when a guest user expires after their creation. Default: `86400` (=24 hours). + +Example configuration: + +```yaml +modules: + - module: synapse_guest_module.GuestModule + config: + # Use a german suffix + display_name_suffix: ' (Gast)' +``` + +## Production installation + +The module is not published to a python registry, but we provide a docker container that can be used as an `initContainer` in Kubernetes: + +```diff + apiVersion: apps/v1 + kind: "StatefulSet" + metadata: + name: synapse + spec: + # ... + template: + spec: ++ # The init container copies the module to he `synapse-modules` volume ++ initContainers: ++ - image: ghcr.io/nordeck/synapse-guest-module: ++ name: install-guest-module ++ volumeMounts: ++ - mountPath: /modules ++ name: synapse-modules + containers: + - name: "synapse" + image: "matrixdotorg/synapse:v1.87.0" ++ env: ++ # Tell python to read the modules from the `/modules` directory ++ - name: PYTHONPATH ++ value: /modules ++ volumeMounts: ++ # Mount the `synapse-modules` volume ++ - mountPath: /modules ++ name: synapse-modules + # ... ++ # Use a local volume to store the module ++ volumes: ++ - emptyDir: ++ medium: Memory ++ sizeLimit: 50Mi ++ name: synapse-modules + # ... +``` diff --git a/modules/restricted-guests/synapse/mypy.ini b/modules/restricted-guests/synapse/mypy.ini new file mode 100644 index 0000000000..be0671c339 --- /dev/null +++ b/modules/restricted-guests/synapse/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +strict = true diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json new file mode 100644 index 0000000000..d2f45f0442 --- /dev/null +++ b/modules/restricted-guests/synapse/package.json @@ -0,0 +1,21 @@ +{ + "name": "@nordeck/synapse-guest-module", + "version": "2.0.0", + "private": true, + "description": "A synapse module to restrict the actions of guests", + "author": "Nordeck IT + Consulting GmbH", + "license": "Apache-2.0", + "scripts": { + "clean": "echo \"Nothing to clean\"", + "build": "echo \"Nothing to build\"", + "docker:build": "docker build -t nordeck/synapse-guest-module -f Dockerfile .", + "tsc": "echo \"Nothing to tsc\"", + "lint": "echo \"Nothing to lint\"", + "types:py": "node ./scripts/run_in_venv.js tox -e check_types", + "lint:py": "node ./scripts/run_in_venv.js tox -e check_codestyle", + "lint:fix": "node ./scripts/run_in_venv.js tox -e fix_codestyle", + "test": "node ./scripts/run_in_venv.js tox -e py", + "depcheck": "echo \"Nothing to check\"", + "package": "yarn docker:build" + } +} diff --git a/modules/restricted-guests/synapse/pyproject.toml b/modules/restricted-guests/synapse/pyproject.toml new file mode 100644 index 0000000000..827968917c --- /dev/null +++ b/modules/restricted-guests/synapse/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.isort] +profile = "black" +known_first_party = [ + "synapse_guest_module", + "tests" +] diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.js b/modules/restricted-guests/synapse/scripts/run_in_venv.js new file mode 100644 index 0000000000..12146be693 --- /dev/null +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.js @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Nordeck IT + Consulting GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// A script to run a command in a python virtual environment that is stored in +// `/.venv`. If the virtual environment doesn't exist yet, it will +// be created. +// +// Example: $ node ./run_in_venv.js python --version + +const fs = require('fs'); +const path = require('path'); +const child_process = require('child_process'); + +const cwd = path.resolve(__dirname, '..'); +const venvPath = path.resolve(__dirname, '../../../.venv'); +const venvRelativeToCwd = path.relative(cwd, venvPath); + +function run(command) { + return new Promise((resolve) => { + const proc = child_process.spawn( + `source ${venvRelativeToCwd}/bin/activate && ${command}`, + [], + { cwd, stdio: 'inherit', shell: true }, + ); + + proc.on('close', (code) => { + console.log('command terminated:', code); + resolve(); + }); + }); +} + +async function main() { + if (!fs.existsSync(venvPath) || !fs.lstatSync(venvPath).isDirectory()) { + child_process.execSync(`python3 -m venv ${venvRelativeToCwd}`, { cwd }); + await run('pip install tox'); + await run('pip install -e ."[dev]"'); + } + + const command = process.argv.slice(2).join(' '); + + await run(command); +} + +main(); diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg new file mode 100644 index 0000000000..a810e40931 --- /dev/null +++ b/modules/restricted-guests/synapse/setup.cfg @@ -0,0 +1,48 @@ +[metadata] +name = synapse_guest_module +description = A synapse module to restrict the actions of guests +long_description = file: README.md +long_description_content_type = text/markdown +version = 2.0.0 + +classifiers = + License :: OSI Approved :: Apache Software License + + +[options] +packages = + synapse_guest_module +python_requires = >= 3.7 +install_requires = + attrs + + +[options.package_data] +synapse_guest_module = py.typed + + +[options.extras_require] +dev = + # for tests + matrix-synapse + tox + twisted + aiounittest + # for type checking + mypy == 1.10.0 + pydantic == 2.4.2 + # for linting + black == 22.3.0 + flake8 == 4.0.1 + isort == 5.9.3 + + +[flake8] +# see https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes +# for error codes. The ones we ignore are: +# W503: line break before binary operator +# W504: line break after binary operator +# E203: whitespace before ':' (which is contrary to pep8?) +# E501: Line too long (black enforces this for us) +# (this is a subset of those ignored in Synapse) +ignore=W503,W504,E203,E501 diff --git a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py new file mode 100644 index 0000000000..1b9ff4fa47 --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from synapse_guest_module.guest_module import GuestModule + +__all__ = ["GuestModule"] diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py new file mode 100644 index 0000000000..b522f56e0f --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -0,0 +1,23 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import attr + + +@attr.s(frozen=True, auto_attribs=True) +class GuestModuleConfig: + user_id_prefix: str + display_name_suffix: str + enable_user_reaper: bool + user_expiration_seconds: int diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py new file mode 100644 index 0000000000..51af4fdc00 --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -0,0 +1,177 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from typing import Any, Dict, Literal, Tuple, Union + +from synapse.module_api import ( + NOT_SPAM, + ModuleApi, + ProfileInfo, + UserProfile, + errors, + run_as_background_process, +) +from synapse.module_api.errors import ConfigError +from synapse.types import UserID + +from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.guest_registration_servlet import GuestRegistrationServlet +from synapse_guest_module.guest_user_reaper import GuestUserReaper + +logger = logging.getLogger("synapse.contrib." + __name__) + + +class GuestModule: + def __init__(self, config: GuestModuleConfig, api: ModuleApi): + self._api = api + self._config = config + + self.registration_servlet = GuestRegistrationServlet(config, api) + self._api.register_web_resource( + "/_synapse/client/register_guest", self.registration_servlet + ) + self._api.register_third_party_rules_callbacks( + on_profile_update=self.profile_update + ) + self._api.register_spam_checker_callbacks( + user_may_create_room=self.callback_user_may_create_room, + user_may_invite=self.callback_user_may_invite, + user_may_join_room=self.callback_user_may_join_room, + check_username_for_spam=self.callback_check_username_for_spam, + ) + + # Start the user reaper + self.reaper = GuestUserReaper(api, config) + if config.enable_user_reaper: + run_as_background_process( + "guest_module_reaper_bg_task", + self.reaper.run, + bg_start_span=False, + ) + + @staticmethod + def parse_config(config: Dict[str, Any]) -> GuestModuleConfig: + """Parse the module configuration""" + + user_id_prefix = config.get("user_id_prefix", "guest-") + if not isinstance(user_id_prefix, str): + raise ConfigError("Config option 'user_id_prefix' must be a string") + + display_name_suffix = config.get("display_name_suffix", " (Guest)") + if not isinstance(display_name_suffix, str): + raise ConfigError("Config option 'display_name_suffix' must be a string") + + enable_user_reaper = config.get("enable_user_reaper", True) + if not isinstance(enable_user_reaper, bool): + raise ConfigError("Config option 'enable_user_reaper' must be a bool") + + user_expiration_seconds = config.get( + "user_expiration_seconds", + 24 * 60 * 60, + ) + if not isinstance(user_expiration_seconds, int): + raise ConfigError( + "Config option 'user_expiration_seconds' must be a number" + ) + + return GuestModuleConfig( + user_id_prefix, + display_name_suffix, + enable_user_reaper, + user_expiration_seconds, + ) + + async def profile_update( + self, + user_id: str, + new_profile: ProfileInfo, + by_admin: bool, + deactivation: bool, + ) -> None: + """Is called whenever a profile is updated. We check that a guest user + always contains the configured suffix (default ` (Guest)`) and add it if + it is missing. + """ + user_is_guest = user_id.startswith("@" + self._config.user_id_prefix) + if user_is_guest: + new_profile_display_name = ( + "" if new_profile.display_name is None else new_profile.display_name + ) + guest_display_name_not_valid = not new_profile_display_name.endswith( + self._config.display_name_suffix + ) + if guest_display_name_not_valid: + user_id_1 = UserID.from_string(user_id) + guest_display_name = ( + new_profile_display_name.strip() + self._config.display_name_suffix + ) + await self._api.set_displayname(user_id_1, guest_display_name) + + async def callback_user_may_create_room( + self, + user_id: str, + ) -> bool: + """Returns whether this user is allowed to create a room. Guest users + should not be able to do that. + """ + user_is_guest = user_id.startswith("@" + self._config.user_id_prefix) + return not user_is_guest + + async def callback_user_may_invite( + self, + inviter: str, + invitee: str, + room_id: str, + ) -> bool: + """Returns whether this user is allowed to invite someone into a room. + Guest users should not be able to to that. + """ + user_is_guest = inviter.startswith("@" + self._config.user_id_prefix) + return not user_is_guest + + async def callback_user_may_join_room( + self, user_id: str, room_id: str, is_invited: bool + ) -> Union[ + Literal["NOT_SPAM"], errors.Codes, Tuple[errors.Codes, Dict[str, Any]], bool + ]: + """Returns whether this user is allowed to join a room. Guest users + should only be able to do that if the room is Ask to Join (knock). + """ + user_is_guest = user_id.startswith("@" + self._config.user_id_prefix) + if not user_is_guest or is_invited: + return NOT_SPAM + + join_rules_events = await self._api.get_state_events_in_room( + room_id, [("m.room.join_rules", None)] + ) + if join_rules_events is None or len(list(join_rules_events)) == 0: + return errors.Codes.BAD_STATE + + for event in join_rules_events: + join_rule = event.get("content", {}) + is_knock = join_rule.get("join_rule").startswith("knock") + if user_is_guest and is_knock: + return NOT_SPAM + + return errors.Codes.FORBIDDEN + + async def callback_check_username_for_spam(self, user_profile: UserProfile) -> bool: + """Returns whether this user should appear in the user directory. Since + we prefer to not invite guests into normal rooms, we hide them here. + """ + user_is_guest = user_profile["user_id"].startswith( + "@" + self._config.user_id_prefix + ) + return user_is_guest diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py new file mode 100644 index 0000000000..d3ec94c4ab --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -0,0 +1,93 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import secrets +import string +from typing import Any, Dict, Tuple + +from synapse.module_api import ( + DirectServeJsonResource, + ModuleApi, + parse_json_object_from_request, +) +from twisted.web.server import Request + +from synapse_guest_module.config import GuestModuleConfig + +logger = logging.getLogger("synapse.contrib." + __name__) + + +class GuestRegistrationServlet(DirectServeJsonResource): + """The `POST /_synapse/client/register_guest` endpoints provides an endpoint + to register a new guest user. It requires the `displayname` property and + returns an object that matches the `AccountAuthInfo` of the + `@matrix-org/react-sdk-module-api`. + """ + + def __init__( + self, + config: GuestModuleConfig, + api: ModuleApi, + ): + super().__init__() + self._api = api + self._config = config + + async def _async_render_POST(self, request: Request) -> Tuple[int, Dict[str, Any]]: + """On POST requests, generate a new username for a guest, check that it + doesn't exist yet, append the suffix to the displayname, create the user, + create a device, and return the session data to the caller. + """ + + json_dict = parse_json_object_from_request(request) + + displayname = json_dict.get("displayname") + if not isinstance(displayname, str) or len(displayname.strip()) == 0: + return 400, {"msg": "You must provide a 'displayname' as a string"} + + # make sure the regex is unique + for _ in range(10): + random_string = "".join( + secrets.choice(string.ascii_lowercase + string.digits) + for _ in range(32) + ) + + localpart = self._config.user_id_prefix + random_string + + # make sure the user-id does not exist yet + if await self._api.check_user_exists( + self._api.get_qualified_user_id(localpart) + ): + continue + + logger.info("Register guest with user %s", localpart) + user_id = await self._api.register_user( + localpart, displayname.strip() + self._config.display_name_suffix + ) + + device_id, access_token, _, _ = await self._api.register_device(user_id) + + logger.debug("Registered user %s", user_id) + + res = { + "userId": user_id, + "deviceId": device_id, + "accessToken": access_token, + "homeserverUrl": self._api.public_baseurl, + } + + return 201, res + + return 500, {"msg": "Internal error: Could not find a free username"} diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py new file mode 100644 index 0000000000..9fd6c59877 --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -0,0 +1,142 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import time +from typing import List + +from synapse.module_api import DatabasePool, LoggingTransaction, ModuleApi + +from synapse_guest_module.config import GuestModuleConfig + +logger = logging.getLogger("synapse.contrib." + __name__) + + +class GuestUserReaper: + def __init__(self, api: ModuleApi, config: GuestModuleConfig): + self._api = api + self._config = config + self.reaper_user = f"{config.user_id_prefix}reaper" + + async def run(self) -> None: + logger.info("User cleanup job started") + + await self._api.sleep(5.0) # Wait for Synapse to start properly + + while True: + logger.debug("Run deactivation loop") + + try: + await self.deactivate_expired_guest_users() + except Exception as e: + logger.error("Error in the user deactivation: %s", e) + + await self._api.sleep(60.0) + + async def deactivate_expired_guest_users(self) -> None: + """Deactivate all users that are older than the specified expiration + interval. This uses the admin API to disable the user. + """ + + def get_expired_users(txn: LoggingTransaction) -> List[str]: + sql = """ + SELECT name + FROM users + WHERE name != ? + AND name LIKE ? + AND deactivated = 0 + AND creation_ts < ?; + """ + + # date operations are database-specific (postgres, sqlite, ...) + expire_ts_seconds = int(time.time() - self._config.user_expiration_seconds) + txn.execute( + sql, + ( + self._api.get_qualified_user_id(self.reaper_user), + f"@{self._config.user_id_prefix}%:{self._api.server_name}", + expire_ts_seconds, + ), + ) + expired_users_rows = txn.fetchall() + + return [row[0] for row in expired_users_rows] + + expired_users: List[str] = await self._api.run_db_interaction( + "guest_module_get_expired_users", + get_expired_users, + ) + + if len(expired_users) > 0: + logger.info("Deactivate %d users", len(expired_users)) + + token = await self.get_admin_token() + + for user_id in expired_users: + logger.debug("Deactivate user %s", user_id) + + url = f"http://localhost:8008/_synapse/admin/v1/deactivate/{user_id}" + + try: + await self._api.http_client.post_json_get_json( + uri=url, + post_json={}, + headers={"Authorization": ["Bearer {}".format(token)]}, + ) + except Exception as e: + logger.error('Failed to delete user "%s": %s', user_id, e) + + async def get_admin_token(self) -> str: + """Create a new admin user in synapse so the module can call the admin + api. If no user or login session exists, we create new ones. + """ + + def get_access_token_txn(txn: LoggingTransaction) -> str | None: + tokens = DatabasePool.simple_select_onecol_txn( + txn, + table="access_tokens", + keyvalues={ + "user_id": self._api.get_qualified_user_id(self.reaper_user), + }, + retcol="token", + ) + + if len(tokens) > 0 and isinstance(tokens[0], str): + return tokens[0] + else: + return None + + token: str | None = await self._api.run_db_interaction( + "guest_module_get_access_token", + get_access_token_txn, + ) + + if token is not None: + return token + + if not await self._api.check_user_exists(self.reaper_user): + logger.info( + 'Register new administrator user "%s"', + self.reaper_user, + ) + + await self._api.register_user(self.reaper_user, admin=True) + + logger.info('Register new device for administrator user "%s"', self.reaper_user) + + _, access_token, _, _ = await self._api.register_device( + self._api.get_qualified_user_id(self.reaper_user) + ) + + return access_token diff --git a/modules/restricted-guests/synapse/synapse_guest_module/py.typed b/modules/restricted-guests/synapse/synapse_guest_module/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py new file mode 100644 index 0000000000..b642bd56ef --- /dev/null +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -0,0 +1,134 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sqlite3 +from asyncio import Future +from typing import Any, Awaitable, Callable, Tuple, TypeVar +from unittest.mock import Mock + +from synapse.http.client import SimpleHttpClient +from synapse.module_api import ModuleApi + +from synapse_guest_module import GuestModule + +RV = TypeVar("RV") +TV = TypeVar("TV") + + +class SQLiteStore: + """In-memory SQLite store. We can't just use a run_db_interaction function that opens + its own connection, since we need to use the same connection for all queries in a + test. + """ + + def __init__(self) -> None: + self.conn = sqlite3.connect(":memory:") + + async def run_db_interaction( + self, desc: str, f: Callable[..., RV], *args: Any, **kwargs: Any + ) -> RV: + cur = CursorWrapper(self.conn.cursor()) + try: + res = f(cur, *args, **kwargs) + self.conn.commit() + return res + except Exception: + self.conn.rollback() + raise + + +class CursorWrapper: + """Wrapper around a SQLite cursor.""" + + def __init__(self, cursor: sqlite3.Cursor) -> None: + self.cur = cursor + + def execute(self, sql: str, args: Any) -> None: + self.cur.execute(sql, args) + + @property + def rowcount(self) -> Any: + return self.cur.rowcount + + def fetchone(self) -> Any: + return self.cur.fetchone() + + def fetchall(self) -> Any: + return self.cur.fetchall() + + def __iter__(self) -> Any: + return self.cur.__iter__() + + def __next__(self) -> Any: + return self.cur.__next__() + + +def make_awaitable(result: TV) -> Awaitable[TV]: + """ + Makes an awaitable, suitable for mocking an `async` function. + This uses Futures as they can be awaited multiple times so can be returned + to multiple callers. + This function has been copied directly from Synapse's tests code. + """ + future = Future() # type: ignore + future.set_result(result) + return future + + +def get_qualified_user_id(username: str) -> str: + return f"@{username}:matrix.local" + + +async def register_user(localpart: str, admin: bool = False) -> str: + return f"@{localpart}:matrix.local" + + +def create_module() -> Tuple[GuestModule, Mock, SQLiteStore]: + store = SQLiteStore() + _setup_db(store.conn) + + client = Mock(spec=SimpleHttpClient) + client.post_json_get_json.return_value = make_awaitable(None) + + # Create a mock based on the ModuleApi spec, but override some mocked functions + # because some capabilities are needed for running the tests. + module_api = Mock(spec=ModuleApi) + module_api.http_client = client + module_api.server_name = "matrix.local" + module_api.public_baseurl = "https://matrix.local:1234/" + module_api.run_db_interaction.side_effect = store.run_db_interaction + module_api.get_qualified_user_id.side_effect = get_qualified_user_id + module_api.check_user_exists.return_value = make_awaitable(False) + module_api.register_user.side_effect = register_user + module_api.register_device.return_value = make_awaitable( + ("DEVICEID", "syn_registered_token", None, None) + ) + + # If necessary, give parse_config some configuration to parse. + config = GuestModule.parse_config( + { + "enable_user_reaper": False, + } + ) + + module = GuestModule(config, module_api) + + return module, module_api, store + + +def _setup_db(conn: sqlite3.Connection) -> None: + conn.execute("CREATE TABLE access_tokens(user_id text, token text)") + conn.execute( + "CREATE TABLE users(name text, deactivated smallint, creation_ts bigint)" + ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py new file mode 100644 index 0000000000..fefc669641 --- /dev/null +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -0,0 +1,202 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import aiounittest +from synapse.module_api import ProfileInfo, UserProfile +from synapse.module_api.errors import ConfigError +from synapse.types import UserID + +from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.guest_module import GuestModule +from tests import create_module + + +class GuestModuleTest(aiounittest.AsyncTestCase): + async def test_parse_config_empty(self) -> None: + config = GuestModule.parse_config({}) + + self.assertEqual( + config, + GuestModuleConfig( + user_id_prefix="guest-", + display_name_suffix=" (Guest)", + enable_user_reaper=True, + user_expiration_seconds=24 * 60 * 60, + ), + ) + + async def test_parse_config_custom(self) -> None: + config = GuestModule.parse_config( + { + "user_id_prefix": "tmp-", + "display_name_suffix": " (Temporary)", + "enable_user_reaper": False, + "user_expiration_seconds": 100, + } + ) + + self.assertEqual( + config, + GuestModuleConfig( + user_id_prefix="tmp-", + display_name_suffix=" (Temporary)", + enable_user_reaper=False, + user_expiration_seconds=100, + ), + ) + + async def test_parse_config_fail_user_id_prefix(self) -> None: + with self.assertRaisesRegex( + ConfigError, "Config option 'user_id_prefix' must be a string" + ): + GuestModule.parse_config( + { + "user_id_prefix": 1234, + } + ) + + async def test_parse_config_fail_display_name_suffix(self) -> None: + with self.assertRaisesRegex( + ConfigError, "Config option 'display_name_suffix' must be a string" + ): + GuestModule.parse_config( + { + "display_name_suffix": 1234, + } + ) + + async def test_parse_config_fail_enable_user_reaper(self) -> None: + with self.assertRaisesRegex( + ConfigError, "Config option 'enable_user_reaper' must be a bool" + ): + GuestModule.parse_config( + { + "enable_user_reaper": "False", + } + ) + + async def test_parse_config_fail_user_expiration_seconds(self) -> None: + with self.assertRaisesRegex( + ConfigError, "Config option 'user_expiration_seconds' must be a number" + ): + GuestModule.parse_config( + { + "user_expiration_seconds": "1", + } + ) + + async def test_profile_update_no_guest(self) -> None: + module, module_api, _ = create_module() + + await module.profile_update( + "@my-user:matrix.local", + ProfileInfo(display_name="My User", avatar_url=None), + True, + False, + ) + + module_api.set_displayname.assert_not_called() + + async def test_profile_update_guest_keep(self) -> None: + module, module_api, _ = create_module() + + await module.profile_update( + "@guest-asdf:matrix.local", + ProfileInfo(display_name="My User (Guest)", avatar_url=None), + True, + False, + ) + + module_api.set_displayname.assert_not_called() + + async def test_profile_update_guest_add_and_trim(self) -> None: + module, module_api, _ = create_module() + + await module.profile_update( + "@guest-asdf:matrix.local", + ProfileInfo(display_name="My User ", avatar_url=None), + True, + False, + ) + + module_api.set_displayname.assert_awaited_once_with( + UserID.from_string("@guest-asdf:matrix.local"), + "My User (Guest)", + ) + + async def test_callback_user_may_create_room_no_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_user_may_create_room( + "@my-user:matrix.local", + ) + + self.assertTrue(allow) + + async def test_callback_user_may_create_room_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_user_may_create_room( + "@guest-asdf:matrix.local", + ) + + self.assertFalse(allow) + + async def test_callback_user_may_invite_no_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_user_may_invite( + "@my-user:matrix.local", + "@inviter:matrix.local", + "!room:matrix.local", + ) + + self.assertTrue(allow) + + async def test_callback_user_may_invite_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_user_may_invite( + "@guest-asdf:matrix.local", + "@inviter:matrix.local", + "!room:matrix.local", + ) + + self.assertFalse(allow) + + async def test_callback_check_username_for_spam_no_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_check_username_for_spam( + UserProfile( + user_id="@my-user:matrix.local", + display_name=None, + avatar_url=None, + ), + ) + + self.assertFalse(allow) + + async def test_callback_check_username_for_spam_guest(self) -> None: + module, _, _ = create_module() + + allow = await module.callback_check_username_for_spam( + UserProfile( + user_id="@guest-asdf:matrix.local", + display_name=None, + avatar_url=None, + ), + ) + + self.assertTrue(allow) diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py new file mode 100644 index 0000000000..ccbfdad569 --- /dev/null +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -0,0 +1,91 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +from typing import cast +from unittest.mock import ANY + +import aiounittest +from twisted.web.server import Request +from twisted.web.test.requesthelper import DummyRequest + +from tests import create_module, make_awaitable + + +class GuestUserReaperTest(aiounittest.AsyncTestCase): + async def test_async_render_POST_missing_displayname(self) -> None: + module, _, _ = create_module() + + request = cast(Request, DummyRequest([])) + request.content = io.BytesIO(b"{}") + + status, response = await module.registration_servlet._async_render_POST(request) + + self.assertEqual(status, 400) + self.assertEqual( + response, {"msg": "You must provide a 'displayname' as a string"} + ) + + async def test_async_render_POST_empty_displayname(self) -> None: + module, _, _ = create_module() + + request = cast(Request, DummyRequest([])) + request.content = io.BytesIO(b'{"displayname":" "}') + + status, response = await module.registration_servlet._async_render_POST(request) + + self.assertEqual(status, 400) + self.assertEqual( + response, {"msg": "You must provide a 'displayname' as a string"} + ) + + async def test_async_render_POST_no_free_username(self) -> None: + module, module_api, _ = create_module() + + request = cast(Request, DummyRequest([])) + request.content = io.BytesIO(b'{"displayname":"My Name"}') + + module_api.check_user_exists.return_value = make_awaitable(True) + + status, response = await module.registration_servlet._async_render_POST(request) + + self.assertEqual(status, 500) + self.assertEqual( + response, {"msg": "Internal error: Could not find a free username"} + ) + + self.assertEqual(module_api.check_user_exists.call_count, 10) + + async def test_async_render_POST_success(self) -> None: + module, module_api, _ = create_module() + + request = cast(Request, DummyRequest([])) + request.content = io.BytesIO(b'{"displayname":"My Name "}') + + status, response = await module.registration_servlet._async_render_POST(request) + + self.assertEqual(status, 201) + + self.assertRegex(response.pop("userId"), r"^@guest-[A-Za-z0-9]+:matrix.local$") + self.assertDictEqual( + response, + { + "accessToken": "syn_registered_token", + "deviceId": "DEVICEID", + "homeserverUrl": "https://matrix.local:1234/", + # "userId" was already checked by self.assertRegex and was removed from the object + }, + ) + + module_api.register_user.assert_called_with(ANY, "My Name (Guest)") diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py new file mode 100644 index 0000000000..e85a55c980 --- /dev/null +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -0,0 +1,128 @@ +# Copyright 2023 Nordeck IT + Consulting GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import time +from unittest.mock import call + +import aiounittest + +from tests import create_module, make_awaitable + + +class GuestUserReaperTest(aiounittest.AsyncTestCase): + async def test_get_admin_token_register(self) -> None: + module, module_api, _ = create_module() + + token = await module.reaper.get_admin_token() + + module_api.check_user_exists.assert_called_with("guest-reaper") + module_api.register_user.assert_called_with("guest-reaper", admin=True) + module_api.register_device.assert_called_with("@guest-reaper:matrix.local") + + self.assertEqual(token, "syn_registered_token") + + async def test_get_admin_token_create_device(self) -> None: + module, module_api, _ = create_module() + + module_api.check_user_exists.return_value = make_awaitable(True) + + token = await module.reaper.get_admin_token() + + module_api.check_user_exists.assert_called_with("guest-reaper") + module_api.register_user.assert_not_called() + module_api.register_device.assert_called_with("@guest-reaper:matrix.local") + + self.assertEqual(token, "syn_registered_token") + + async def test_get_admin_token_read_from_db(self) -> None: + module, module_api, store = create_module() + + store.conn.execute( + "INSERT INTO access_tokens VALUES ('@guest-reaper:matrix.local', 'syn_db_token')" + ) + + module_api.check_user_exists.return_value = make_awaitable(True) + + token = await module.reaper.get_admin_token() + + module_api.check_user_exists.assert_not_called() + module_api.register_user.assert_not_called() + module_api.register_device.assert_not_called() + + self.assertEqual(token, "syn_db_token") + + async def test_deactivate_expired_guest_users_success(self) -> None: + module, module_api, store = create_module() + + now = int(time.time()) + store.conn.executemany( + "INSERT INTO users VALUES (?, ?, ?)", + [ + ["@user-1:matrix.local", 0, 0], + ["@guest-reaper:matrix.local", 0, 0], + ["@guest-active:matrix.local", 0, now], + ["@guest-deactivated:matrix.local", 1, 0], + ["@guest-old-1:matrix.local", 0, 0], + ["@guest-old-2:matrix.local", 0, 0], + ], + ) + + await module.reaper.deactivate_expired_guest_users() + + self.assertEqual(module_api.http_client.post_json_get_json.await_count, 2) + + module_api.http_client.post_json_get_json.assert_has_awaits( + [ + call( + uri="http://localhost:8008/_synapse/admin/v1/deactivate/@guest-old-1:matrix.local", + post_json={}, + headers={"Authorization": ["Bearer syn_registered_token"]}, + ), + call( + uri="http://localhost:8008/_synapse/admin/v1/deactivate/@guest-old-2:matrix.local", + post_json={}, + headers={"Authorization": ["Bearer syn_registered_token"]}, + ), + ] + ) + + async def test_deactivate_expired_guest_users_with_failure(self) -> None: + module, module_api, store = create_module() + + store.conn.executemany( + "INSERT INTO users VALUES (?, ?, ?)", + [ + ["@guest-old-1:matrix.local", 0, 0], + ["@guest-old-2:matrix.local", 0, 0], + ], + ) + + module_api.http_client.post_json_get_json.side_effect = Exception("") + + await module.reaper.deactivate_expired_guest_users() + + module_api.http_client.post_json_get_json.assert_has_awaits( + [ + call( + uri="http://localhost:8008/_synapse/admin/v1/deactivate/@guest-old-1:matrix.local", + post_json={}, + headers={"Authorization": ["Bearer syn_registered_token"]}, + ), + call( + uri="http://localhost:8008/_synapse/admin/v1/deactivate/@guest-old-2:matrix.local", + post_json={}, + headers={"Authorization": ["Bearer syn_registered_token"]}, + ), + ] + ) diff --git a/modules/restricted-guests/synapse/tox.ini b/modules/restricted-guests/synapse/tox.ini new file mode 100644 index 0000000000..1410d2ee59 --- /dev/null +++ b/modules/restricted-guests/synapse/tox.ini @@ -0,0 +1,37 @@ +[tox] +envlist = py, check_codestyle, check_types + +# required for PEP 517 (pyproject.toml-style) builds +isolated_build = true + +[testenv:py] + +extras = dev + +commands = + python -m twisted.trial tests + +[testenv:check_codestyle] + +extras = dev + +commands = + flake8 synapse_guest_module tests + black --check --diff synapse_guest_module tests + isort --check-only --diff synapse_guest_module tests + +[testenv:fix_codestyle] + +extras = dev + +commands = + black synapse_guest_module tests + isort synapse_guest_module tests + + +[testenv:check_types] + +extras = dev + +commands = + mypy synapse_guest_module tests From ffb5fdaa3002448cb2a3d5492bb5b115dc2dd582 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Tue, 4 Feb 2025 13:43:51 -0500 Subject: [PATCH 011/211] synapse guest module: update copyright --- modules/restricted-guests/synapse/scripts/run_in_venv.js | 1 + .../restricted-guests/synapse/synapse_guest_module/__init__.py | 1 + modules/restricted-guests/synapse/synapse_guest_module/config.py | 1 + .../synapse/synapse_guest_module/guest_module.py | 1 + .../synapse/synapse_guest_module/guest_registration_servlet.py | 1 + .../synapse/synapse_guest_module/guest_user_reaper.py | 1 + modules/restricted-guests/synapse/tests/__init__.py | 1 + modules/restricted-guests/synapse/tests/test_guest_module.py | 1 + .../synapse/tests/test_guest_registration_servlet.py | 1 + .../restricted-guests/synapse/tests/test_guest_user_reaper.py | 1 + 10 files changed, 10 insertions(+) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.js b/modules/restricted-guests/synapse/scripts/run_in_venv.js index 12146be693..ee392ecbee 100644 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.js +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.js @@ -1,5 +1,6 @@ /* * Copyright 2023 Nordeck IT + Consulting GmbH + * Copyright 2025 New Vector Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py index 1b9ff4fa47..fc47effa9f 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index b522f56e0f..2ef607a491 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 51af4fdc00..89fb3f87de 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index d3ec94c4ab..7f9849d781 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index 9fd6c59877..c2492be9bb 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index b642bd56ef..49012b4ca7 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index fefc669641..c5d7164edf 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index ccbfdad569..e8700d2f03 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index e85a55c980..191312f380 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -1,4 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From caabc37519151a98aa9284956baa511af9d4f22f Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Tue, 4 Feb 2025 14:02:11 -0500 Subject: [PATCH 012/211] synapse guest module: update org name & docs --- .../restricted-guests/synapse/CHANGELOG.md | 42 +------------------ modules/restricted-guests/synapse/README.md | 2 +- .../restricted-guests/synapse/package.json | 6 +-- 3 files changed, 6 insertions(+), 44 deletions(-) diff --git a/modules/restricted-guests/synapse/CHANGELOG.md b/modules/restricted-guests/synapse/CHANGELOG.md index 0bf2ec3720..2d5904137d 100644 --- a/modules/restricted-guests/synapse/CHANGELOG.md +++ b/modules/restricted-guests/synapse/CHANGELOG.md @@ -1,45 +1,7 @@ -# @nordeck/synapse-guest-module - -## 2.0.0 - -### Major Changes - -- 5539790: Guest users are now only allowed in Ask to Join rooms +# @element-hq/synapse-guest-module ## 1.0.0 ### Major Changes -- a05ce45: First stable release. - -## 0.3.1 - -### Patch Changes - -- 344b7bf: Update the repository name - -## 0.3.0 - -### Minor Changes - -- 27680cd: Provide a dedicated registration endpoint to not interfere with the original - registration endpoint. -- 997f47b: Deactivate guest users after a configured expiration time. - -## 0.2.0 - -### Minor Changes - -- 00c92e0: Don't include guest users in the search results. - -## 0.1.0 - -### Minor Changes - -- b43b066: Disable certain homeserver-wide actions (create room, invite user) for guest users. - -## 0.0.1 - -### Patch Changes - -- Add initial version. +- Fork from [Nordeck's version](https://github.com/nordeck/element-web-modules/tree/main/packages/synapse-guest-module). diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 3fb51aacba..52e14ae710 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -1,6 +1,6 @@ # Synapse Guest Module -A [pluggable synapse module](https://matrix-org.github.io/synapse/latest/modules/index.html) to restrict the actions of guests. +A [pluggable synapse module](https://element-hq.github.io/synapse/latest/modules/index.html) to restrict the actions of guests. **Features:** diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index d2f45f0442..468b3f4254 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -1,14 +1,14 @@ { - "name": "@nordeck/synapse-guest-module", + "name": "@element-hq/synapse-guest-module", "version": "2.0.0", "private": true, "description": "A synapse module to restrict the actions of guests", - "author": "Nordeck IT + Consulting GmbH", + "author": "New Vector Ltd.", "license": "Apache-2.0", "scripts": { "clean": "echo \"Nothing to clean\"", "build": "echo \"Nothing to build\"", - "docker:build": "docker build -t nordeck/synapse-guest-module -f Dockerfile .", + "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", "tsc": "echo \"Nothing to tsc\"", "lint": "echo \"Nothing to lint\"", "types:py": "node ./scripts/run_in_venv.js tox -e check_types", From b36705c4f4155d4eb5320b60573aa28d36a4e64a Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 5 Feb 2025 09:43:21 -0500 Subject: [PATCH 013/211] Run lint-staged on all new files --- modules/restricted-guests/synapse/README.md | 12 ++--- .../restricted-guests/synapse/package.json | 38 ++++++++-------- .../synapse/scripts/run_in_venv.js | 44 +++++++++---------- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 52e14ae710..621a648707 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -28,8 +28,8 @@ Add module configuration into `modules` section of `homeserver.yaml`: ```yaml modules: - - module: synapse_guest_module.GuestModule - config: {} + - module: synapse_guest_module.GuestModule + config: {} ``` ## Module configuration @@ -45,10 +45,10 @@ Example configuration: ```yaml modules: - - module: synapse_guest_module.GuestModule - config: - # Use a german suffix - display_name_suffix: ' (Gast)' + - module: synapse_guest_module.GuestModule + config: + # Use a german suffix + display_name_suffix: " (Gast)" ``` ## Production installation diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 468b3f4254..7f1ef45b65 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -1,21 +1,21 @@ { - "name": "@element-hq/synapse-guest-module", - "version": "2.0.0", - "private": true, - "description": "A synapse module to restrict the actions of guests", - "author": "New Vector Ltd.", - "license": "Apache-2.0", - "scripts": { - "clean": "echo \"Nothing to clean\"", - "build": "echo \"Nothing to build\"", - "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", - "tsc": "echo \"Nothing to tsc\"", - "lint": "echo \"Nothing to lint\"", - "types:py": "node ./scripts/run_in_venv.js tox -e check_types", - "lint:py": "node ./scripts/run_in_venv.js tox -e check_codestyle", - "lint:fix": "node ./scripts/run_in_venv.js tox -e fix_codestyle", - "test": "node ./scripts/run_in_venv.js tox -e py", - "depcheck": "echo \"Nothing to check\"", - "package": "yarn docker:build" - } + "name": "@element-hq/synapse-guest-module", + "version": "2.0.0", + "private": true, + "description": "A synapse module to restrict the actions of guests", + "author": "New Vector Ltd.", + "license": "Apache-2.0", + "scripts": { + "clean": "echo \"Nothing to clean\"", + "build": "echo \"Nothing to build\"", + "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", + "tsc": "echo \"Nothing to tsc\"", + "lint": "echo \"Nothing to lint\"", + "types:py": "node ./scripts/run_in_venv.js tox -e check_types", + "lint:py": "node ./scripts/run_in_venv.js tox -e check_codestyle", + "lint:fix": "node ./scripts/run_in_venv.js tox -e fix_codestyle", + "test": "node ./scripts/run_in_venv.js tox -e py", + "depcheck": "echo \"Nothing to check\"", + "package": "yarn docker:build" + } } diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.js b/modules/restricted-guests/synapse/scripts/run_in_venv.js index ee392ecbee..472d471c32 100644 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.js +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.js @@ -21,39 +21,39 @@ // // Example: $ node ./run_in_venv.js python --version -const fs = require('fs'); -const path = require('path'); -const child_process = require('child_process'); +const fs = require("fs"); +const path = require("path"); +const child_process = require("child_process"); -const cwd = path.resolve(__dirname, '..'); -const venvPath = path.resolve(__dirname, '../../../.venv'); +const cwd = path.resolve(__dirname, ".."); +const venvPath = path.resolve(__dirname, "../../../.venv"); const venvRelativeToCwd = path.relative(cwd, venvPath); function run(command) { - return new Promise((resolve) => { - const proc = child_process.spawn( - `source ${venvRelativeToCwd}/bin/activate && ${command}`, - [], - { cwd, stdio: 'inherit', shell: true }, - ); + return new Promise((resolve) => { + const proc = child_process.spawn(`source ${venvRelativeToCwd}/bin/activate && ${command}`, [], { + cwd, + stdio: "inherit", + shell: true, + }); - proc.on('close', (code) => { - console.log('command terminated:', code); - resolve(); + proc.on("close", (code) => { + console.log("command terminated:", code); + resolve(); + }); }); - }); } async function main() { - if (!fs.existsSync(venvPath) || !fs.lstatSync(venvPath).isDirectory()) { - child_process.execSync(`python3 -m venv ${venvRelativeToCwd}`, { cwd }); - await run('pip install tox'); - await run('pip install -e ."[dev]"'); - } + if (!fs.existsSync(venvPath) || !fs.lstatSync(venvPath).isDirectory()) { + child_process.execSync(`python3 -m venv ${venvRelativeToCwd}`, { cwd }); + await run("pip install tox"); + await run('pip install -e ."[dev]"'); + } - const command = process.argv.slice(2).join(' '); + const command = process.argv.slice(2).join(" "); - await run(command); + await run(command); } main(); From 124068294e2fb9010a9651e2039bc09b8ad16f9f Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 5 Feb 2025 10:48:53 -0500 Subject: [PATCH 014/211] synapse guest module: add to yarn workspaces This fixes a false-positive "missing file" check in knip --- modules/restricted-guests/synapse/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 7f1ef45b65..05cfd61802 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -6,11 +6,13 @@ "author": "New Vector Ltd.", "license": "Apache-2.0", "scripts": { + "prepare": "echo \"Nothing to prepare\"", "clean": "echo \"Nothing to clean\"", "build": "echo \"Nothing to build\"", "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", "tsc": "echo \"Nothing to tsc\"", "lint": "echo \"Nothing to lint\"", + "lint:ts": "echo \"Nothing to lint:ts\"", "types:py": "node ./scripts/run_in_venv.js tox -e check_types", "lint:py": "node ./scripts/run_in_venv.js tox -e check_codestyle", "lint:fix": "node ./scripts/run_in_venv.js tox -e fix_codestyle", From 88f098a2f7ee4fd04474e888352ae9642f940559 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 5 Feb 2025 15:21:53 -0500 Subject: [PATCH 015/211] Use CI job for only synapse guest module Also remove jobs for E2E & changeset, which aren't set up in this repo --- modules/restricted-guests/synapse/README.md | 2 +- modules/restricted-guests/synapse/pyproject.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 621a648707..5db9f5c867 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -66,7 +66,7 @@ The module is not published to a python registry, but we provide a docker contai spec: + # The init container copies the module to he `synapse-modules` volume + initContainers: -+ - image: ghcr.io/nordeck/synapse-guest-module: ++ - image: ghcr.io/element-hq/synapse-guest-module: + name: install-guest-module + volumeMounts: + - mountPath: /modules diff --git a/modules/restricted-guests/synapse/pyproject.toml b/modules/restricted-guests/synapse/pyproject.toml index 827968917c..8ba1e996ca 100644 --- a/modules/restricted-guests/synapse/pyproject.toml +++ b/modules/restricted-guests/synapse/pyproject.toml @@ -8,3 +8,6 @@ known_first_party = [ "synapse_guest_module", "tests" ] +known_third_party = [ + "synapse" +] From 88f578a51bd347d7da53037aa0253d49a44b5f1e Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 6 Feb 2025 10:23:37 -0500 Subject: [PATCH 016/211] synapse guest module: replace ADD with COPY --- modules/restricted-guests/synapse/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile index 3941d9511e..baaab8c2ca 100644 --- a/modules/restricted-guests/synapse/Dockerfile +++ b/modules/restricted-guests/synapse/Dockerfile @@ -1,6 +1,6 @@ FROM debian:bookworm-slim WORKDIR /src -ADD synapse_guest_module /src/synapse_guest_module +COPY synapse_guest_module /src/synapse_guest_module CMD ["cp", "-r", "/src/synapse_guest_module", "/modules"] From 5fe56e414c804b414ce564b5d69c5655c78d18a1 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 6 Feb 2025 10:28:36 -0500 Subject: [PATCH 017/211] synapse guest module: use minimal container image --- modules/restricted-guests/synapse/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile index baaab8c2ca..80d9b87f12 100644 --- a/modules/restricted-guests/synapse/Dockerfile +++ b/modules/restricted-guests/synapse/Dockerfile @@ -1,4 +1,5 @@ -FROM debian:bookworm-slim +ARG DEBIAN_VERSION_NUMERIC=12 +FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC} WORKDIR /src COPY synapse_guest_module /src/synapse_guest_module From c6c04d399f5f2a8a33928ac04b39daf1933bf631 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 7 Feb 2025 08:38:58 -0500 Subject: [PATCH 018/211] Convert run_in_venv Node script to Bash The work done by the script is simpler to achieve in Bash, and avoids a potential mis-parsing of arguments. --- .../restricted-guests/synapse/package.json | 8 +-- .../synapse/scripts/run_in_venv.js | 59 ------------------- .../synapse/scripts/run_in_venv.sh | 36 +++++++++++ 3 files changed, 40 insertions(+), 63 deletions(-) delete mode 100644 modules/restricted-guests/synapse/scripts/run_in_venv.js create mode 100755 modules/restricted-guests/synapse/scripts/run_in_venv.sh diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 05cfd61802..397948309c 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -13,10 +13,10 @@ "tsc": "echo \"Nothing to tsc\"", "lint": "echo \"Nothing to lint\"", "lint:ts": "echo \"Nothing to lint:ts\"", - "types:py": "node ./scripts/run_in_venv.js tox -e check_types", - "lint:py": "node ./scripts/run_in_venv.js tox -e check_codestyle", - "lint:fix": "node ./scripts/run_in_venv.js tox -e fix_codestyle", - "test": "node ./scripts/run_in_venv.js tox -e py", + "types:py": "./scripts/run_in_venv.sh tox -e check_types", + "lint:py": "./scripts/run_in_venv.sh tox -e check_codestyle", + "lint:fix": "./scripts/run_in_venv.sh tox -e fix_codestyle", + "test": "./scripts/run_in_venv.sh tox -e py", "depcheck": "echo \"Nothing to check\"", "package": "yarn docker:build" } diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.js b/modules/restricted-guests/synapse/scripts/run_in_venv.js deleted file mode 100644 index 472d471c32..0000000000 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2023 Nordeck IT + Consulting GmbH - * Copyright 2025 New Vector Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// A script to run a command in a python virtual environment that is stored in -// `/.venv`. If the virtual environment doesn't exist yet, it will -// be created. -// -// Example: $ node ./run_in_venv.js python --version - -const fs = require("fs"); -const path = require("path"); -const child_process = require("child_process"); - -const cwd = path.resolve(__dirname, ".."); -const venvPath = path.resolve(__dirname, "../../../.venv"); -const venvRelativeToCwd = path.relative(cwd, venvPath); - -function run(command) { - return new Promise((resolve) => { - const proc = child_process.spawn(`source ${venvRelativeToCwd}/bin/activate && ${command}`, [], { - cwd, - stdio: "inherit", - shell: true, - }); - - proc.on("close", (code) => { - console.log("command terminated:", code); - resolve(); - }); - }); -} - -async function main() { - if (!fs.existsSync(venvPath) || !fs.lstatSync(venvPath).isDirectory()) { - child_process.execSync(`python3 -m venv ${venvRelativeToCwd}`, { cwd }); - await run("pip install tox"); - await run('pip install -e ."[dev]"'); - } - - const command = process.argv.slice(2).join(" "); - - await run(command); -} - -main(); diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh new file mode 100755 index 0000000000..fb20a0a6d4 --- /dev/null +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env sh + +# Copyright 2023 Nordeck IT + Consulting GmbH +# Copyright 2025 New Vector Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A script to run a command in a python virtual environment that is stored in +# `/.venv`. If the virtual environment doesn't exist yet, it will +# be created. +# +# Example: $ ./run_in_venv.sh python --version + +set -e + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." + +repo_root=$(git rev-parse --show-cdup) +venv_path=${repo_root:-./}.venv + +[ -d "$venv_path" ] || python3.11 -m venv "$venv_path" +source "$venv_path/bin/activate" + +pip install -e ."[dev]" + +[ $# -gt 1 ] && $* From 2f8c31797ba397a927d0f556f28b74ba4a2a6276 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 7 Feb 2025 10:06:01 -0500 Subject: [PATCH 019/211] Make run_in_venv POSIX compliant (not just Bash) --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index fb20a0a6d4..7a222359ab 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -23,14 +23,14 @@ set -e -cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +cd -- "$(dirname -- "$0")/.." repo_root=$(git rev-parse --show-cdup) venv_path=${repo_root:-./}.venv [ -d "$venv_path" ] || python3.11 -m venv "$venv_path" -source "$venv_path/bin/activate" +. "$venv_path/bin/activate" pip install -e ."[dev]" -[ $# -gt 1 ] && $* +[ $# -gt 1 ] && "$@" From d0f183517562dfebc073ab2bd2cdc5310556d80c Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 7 Feb 2025 10:53:54 -0500 Subject: [PATCH 020/211] run_in_venv: allow setting Python version --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 7a222359ab..1125d3f238 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -28,7 +28,7 @@ cd -- "$(dirname -- "$0")/.." repo_root=$(git rev-parse --show-cdup) venv_path=${repo_root:-./}.venv -[ -d "$venv_path" ] || python3.11 -m venv "$venv_path" +[ -d "$venv_path" ] || python${PYTHON_VERSION:-3} -m venv "$venv_path" . "$venv_path/bin/activate" pip install -e ."[dev]" From 2e58691093afa05a74d7bff23395b4d4751cd87d Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 7 Feb 2025 11:36:10 -0500 Subject: [PATCH 021/211] run_in_venv: skip unnecessary env reinstallation --- .../restricted-guests/synapse/scripts/run_in_venv.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 1125d3f238..21e676f0c0 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -28,9 +28,9 @@ cd -- "$(dirname -- "$0")/.." repo_root=$(git rev-parse --show-cdup) venv_path=${repo_root:-./}.venv -[ -d "$venv_path" ] || python${PYTHON_VERSION:-3} -m venv "$venv_path" -. "$venv_path/bin/activate" +if [ ! -d "$venv_path" ]; then + "python${PYTHON_VERSION:-3}" -m venv "$venv_path" + "$venv_path/bin/pip" install -e ."[dev]" +fi -pip install -e ."[dev]" - -[ $# -gt 1 ] && "$@" +[ $# -gt 1 ] && . "$venv_path/bin/activate" && "$@" From 65d75c3779499415783808a0f7a5605b9a1ada15 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Fri, 7 Feb 2025 12:02:19 -0500 Subject: [PATCH 022/211] Split out vitest from general tests --- modules/opendesk/element-web/package.json | 3 ++- modules/restricted-guests/synapse/package.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index ca9e7b3f2d..d051def5c1 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -9,7 +9,8 @@ "scripts": { "prepare": "vite build", "lint:ts": "tsc --noEmit", - "test": "echo no tests yet" + "test": "echo no tests yet", + "vitest": "echo no tests yet" }, "devDependencies": { "@element-hq/element-web-module-api": "^0.1.0", diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 397948309c..f166cec809 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -17,6 +17,7 @@ "lint:py": "./scripts/run_in_venv.sh tox -e check_codestyle", "lint:fix": "./scripts/run_in_venv.sh tox -e fix_codestyle", "test": "./scripts/run_in_venv.sh tox -e py", + "vitest": "echo \"Nothing to vitest\"", "depcheck": "echo \"Nothing to check\"", "package": "yarn docker:build" } From 673bda4515eb554d726165ae3d4c6c18af38979d Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Mon, 10 Feb 2025 15:04:35 -0500 Subject: [PATCH 023/211] synapse guest module: set version to 1.0.0 as opposed to picking up from where upstream left off at 2.0.0. (Note that CHANGELOG.md was already at 1.0.0.) --- modules/restricted-guests/synapse/package.json | 2 +- modules/restricted-guests/synapse/setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index f166cec809..00d2345ef8 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -1,6 +1,6 @@ { "name": "@element-hq/synapse-guest-module", - "version": "2.0.0", + "version": "1.0.0", "private": true, "description": "A synapse module to restrict the actions of guests", "author": "New Vector Ltd.", diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg index a810e40931..8eeff887b1 100644 --- a/modules/restricted-guests/synapse/setup.cfg +++ b/modules/restricted-guests/synapse/setup.cfg @@ -3,7 +3,7 @@ name = synapse_guest_module description = A synapse module to restrict the actions of guests long_description = file: README.md long_description_content_type = text/markdown -version = 2.0.0 +version = 1.0.0 classifiers = License :: OSI Approved :: Apache Software License From 226b89f846862e8799a41ddd3f072d17e30a48d4 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Mon, 10 Feb 2025 15:01:20 -0500 Subject: [PATCH 024/211] synapse guest module: change license Set license to multi-license of AGPL + Element Commercial, and add associated license files to the repository root. This does not add a notice of "This file includes modifications" to any files because they were only moved and relicensed, but otherwise unmodified. The exception is run_in_venv.sh, which is a rewrite of upstream's run_in_venv.js and thus lacks the original copyright entirely. --- modules/restricted-guests/synapse/README.md | 12 ++++++++++++ modules/restricted-guests/synapse/package.json | 2 +- .../synapse/scripts/run_in_venv.sh | 17 +++-------------- modules/restricted-guests/synapse/setup.cfg | 7 ++++--- .../synapse/synapse_guest_module/__init__.py | 16 +++++----------- .../synapse/synapse_guest_module/config.py | 16 +++++----------- .../synapse_guest_module/guest_module.py | 16 +++++----------- .../guest_registration_servlet.py | 16 +++++----------- .../synapse_guest_module/guest_user_reaper.py | 16 +++++----------- .../restricted-guests/synapse/tests/__init__.py | 16 +++++----------- .../synapse/tests/test_guest_module.py | 16 +++++----------- .../tests/test_guest_registration_servlet.py | 16 +++++----------- .../synapse/tests/test_guest_user_reaper.py | 16 +++++----------- 13 files changed, 65 insertions(+), 117 deletions(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 5db9f5c867..958806ae7f 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -91,3 +91,15 @@ The module is not published to a python registry, but we provide a docker contai + name: synapse-modules # ... ``` + +## Copyright & License + +Copyright 2023 Nordeck IT + Consulting GmbH +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 00d2345ef8..529323342e 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -4,7 +4,7 @@ "private": true, "description": "A synapse module to restrict the actions of guests", "author": "New Vector Ltd.", - "license": "Apache-2.0", + "license": "SEE LICENSE IN README.md", "scripts": { "prepare": "echo \"Nothing to prepare\"", "clean": "echo \"Nothing to clean\"", diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 21e676f0c0..3c97ad3e8c 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -1,19 +1,8 @@ #!/usr/bin/env sh - -# Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # A script to run a command in a python virtual environment that is stored in # `/.venv`. If the virtual environment doesn't exist yet, it will diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg index 8eeff887b1..4ebc42cd85 100644 --- a/modules/restricted-guests/synapse/setup.cfg +++ b/modules/restricted-guests/synapse/setup.cfg @@ -4,9 +4,10 @@ description = A synapse module to restrict the actions of guests long_description = file: README.md long_description_content_type = text/markdown version = 1.0.0 - -classifiers = - License :: OSI Approved :: Apache Software License +license = AGPL-3.0-only OR LicenseRef-Element-Commercial +license_files = + ../../../LICENSE-AGPL-3.0 + ../../../LICENSE-COMMERCIAL [options] diff --git a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py index fc47effa9f..33a6e8331f 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . from synapse_guest_module.guest_module import GuestModule diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index 2ef607a491..4a53ae47c5 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import attr diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 89fb3f87de..ec09c0a58b 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import logging from typing import Any, Dict, Literal, Tuple, Union diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 7f9849d781..4555823b22 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import logging import secrets diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index c2492be9bb..d03d5da4f4 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import logging import time diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index 49012b4ca7..4def99172a 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import sqlite3 from asyncio import Future diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index c5d7164edf..c08ce3071d 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import aiounittest from synapse.module_api import ProfileInfo, UserProfile diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index e8700d2f03..314264b657 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import io from typing import cast diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index 191312f380..3ad2b03d6a 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -1,17 +1,11 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright 2025 New Vector Ltd. +# Copyright (C) 2025 New Vector Ltd. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Originally licensed under the Apache License, Version 2.0: +# . import time from unittest.mock import call From 3e107737aad827a56014d250b06e1b9732c62e82 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Tue, 11 Feb 2025 14:00:07 -0500 Subject: [PATCH 025/211] Remove "(C)" from copyright lines for consistency with other copyright lines, and readability --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 2 +- .../restricted-guests/synapse/synapse_guest_module/__init__.py | 2 +- .../restricted-guests/synapse/synapse_guest_module/config.py | 2 +- .../synapse/synapse_guest_module/guest_module.py | 2 +- .../synapse/synapse_guest_module/guest_registration_servlet.py | 2 +- .../synapse/synapse_guest_module/guest_user_reaper.py | 2 +- modules/restricted-guests/synapse/tests/__init__.py | 2 +- modules/restricted-guests/synapse/tests/test_guest_module.py | 2 +- .../synapse/tests/test_guest_registration_servlet.py | 2 +- .../restricted-guests/synapse/tests/test_guest_user_reaper.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 3c97ad3e8c..f53288d162 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -1,5 +1,5 @@ #!/usr/bin/env sh -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py index 33a6e8331f..777308ee00 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index 4a53ae47c5..4d2248c710 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index ec09c0a58b..eb89099d90 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 4555823b22..7d6e575b37 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index d03d5da4f4..df5f7160dc 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index 4def99172a..199848523d 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index c08ce3071d..dfa6ed20e4 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index 314264b657..d45b992fa6 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index 3ad2b03d6a..a9144619f7 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -1,5 +1,5 @@ # Copyright 2023 Nordeck IT + Consulting GmbH -# Copyright (C) 2025 New Vector Ltd. +# Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. From 523758cbea6eecefdbb4bcf926bdae1f5d184f04 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 12 Feb 2025 15:42:18 -0500 Subject: [PATCH 026/211] synapse guest module: move license files Move them into the module folder so that they'll be properly included in distribution builds, such as when installing this module via pip. Also reword copyright headers to refer to the project root instead of the repository root, which isn't included in distribution builds. --- .../synapse/LICENSE-AGPL-3.0 | 661 ++++++++++++++++++ .../synapse/LICENSE-COMMERCIAL | 6 + .../synapse/scripts/run_in_venv.sh | 2 +- modules/restricted-guests/synapse/setup.cfg | 4 +- .../synapse/synapse_guest_module/__init__.py | 2 +- .../synapse/synapse_guest_module/config.py | 2 +- .../synapse_guest_module/guest_module.py | 2 +- .../guest_registration_servlet.py | 2 +- .../synapse_guest_module/guest_user_reaper.py | 2 +- .../synapse/tests/__init__.py | 2 +- .../synapse/tests/test_guest_module.py | 2 +- .../tests/test_guest_registration_servlet.py | 2 +- .../synapse/tests/test_guest_user_reaper.py | 2 +- 13 files changed, 679 insertions(+), 12 deletions(-) create mode 100644 modules/restricted-guests/synapse/LICENSE-AGPL-3.0 create mode 100644 modules/restricted-guests/synapse/LICENSE-COMMERCIAL diff --git a/modules/restricted-guests/synapse/LICENSE-AGPL-3.0 b/modules/restricted-guests/synapse/LICENSE-AGPL-3.0 new file mode 100644 index 0000000000..19bae35887 --- /dev/null +++ b/modules/restricted-guests/synapse/LICENSE-AGPL-3.0 @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/modules/restricted-guests/synapse/LICENSE-COMMERCIAL b/modules/restricted-guests/synapse/LICENSE-COMMERCIAL new file mode 100644 index 0000000000..173e03e0c0 --- /dev/null +++ b/modules/restricted-guests/synapse/LICENSE-COMMERCIAL @@ -0,0 +1,6 @@ +Licensees holding a valid commercial license with Element may use this +software in accordance with the terms contained in a written agreement +between you and Element. + +To purchase a commercial license please contact our sales team at +licensing@element.io diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index f53288d162..9f5ba12584 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # A script to run a command in a python virtual environment that is stored in # `/.venv`. If the virtual environment doesn't exist yet, it will diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg index 4ebc42cd85..8fe46af1a8 100644 --- a/modules/restricted-guests/synapse/setup.cfg +++ b/modules/restricted-guests/synapse/setup.cfg @@ -6,8 +6,8 @@ long_description_content_type = text/markdown version = 1.0.0 license = AGPL-3.0-only OR LicenseRef-Element-Commercial license_files = - ../../../LICENSE-AGPL-3.0 - ../../../LICENSE-COMMERCIAL + LICENSE-AGPL-3.0 + LICENSE-COMMERCIAL [options] diff --git a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py index 777308ee00..29b1836399 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/__init__.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/__init__.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index 4d2248c710..a0ddbe569a 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index eb89099d90..156012c35d 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 7d6e575b37..6cdb382831 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index df5f7160dc..7b3f2c7288 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index 199848523d..04d9c250b8 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index dfa6ed20e4..f92614c714 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index d45b992fa6..f0a63b7a62 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index a9144619f7..0a865c3f2d 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -2,7 +2,7 @@ # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -# Please see LICENSE files in the repository root for full details. +# Please see LICENSE files in the project root for full details. # # Originally licensed under the Apache License, Version 2.0: # . From 504f35a65bbf851cc4a3c76c09cb13770b26a525 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 12 Feb 2025 15:42:59 -0500 Subject: [PATCH 027/211] synapse guest module: nitpick quote refactor --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 9f5ba12584..23adf1e03b 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -19,7 +19,7 @@ venv_path=${repo_root:-./}.venv if [ ! -d "$venv_path" ]; then "python${PYTHON_VERSION:-3}" -m venv "$venv_path" - "$venv_path/bin/pip" install -e ."[dev]" + "$venv_path/bin/pip" install -e '.[dev]' fi [ $# -gt 1 ] && . "$venv_path/bin/activate" && "$@" From b429ae9c10bcdbd625273c3664ff8062be361397 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 12 Feb 2025 16:01:24 -0500 Subject: [PATCH 028/211] synapse guest module: generate coverage report --- modules/restricted-guests/synapse/setup.cfg | 6 ++++++ modules/restricted-guests/synapse/tox.ini | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg index 8fe46af1a8..75fead2743 100644 --- a/modules/restricted-guests/synapse/setup.cfg +++ b/modules/restricted-guests/synapse/setup.cfg @@ -29,6 +29,7 @@ dev = tox twisted aiounittest + coverage # for type checking mypy == 1.10.0 pydantic == 2.4.2 @@ -38,6 +39,11 @@ dev = isort == 5.9.3 +[coverage:run] +relative_files = True +branch = True + + [flake8] # see https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes # for error codes. The ones we ignore are: diff --git a/modules/restricted-guests/synapse/tox.ini b/modules/restricted-guests/synapse/tox.ini index 1410d2ee59..0da1d491aa 100644 --- a/modules/restricted-guests/synapse/tox.ini +++ b/modules/restricted-guests/synapse/tox.ini @@ -9,7 +9,8 @@ isolated_build = true extras = dev commands = - python -m twisted.trial tests + coverage run -m twisted.trial tests + coverage xml [testenv:check_codestyle] From ab17724182db1c6ccfde0f920a845b41a18004a5 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 13 Feb 2025 11:37:53 -0500 Subject: [PATCH 029/211] Make "yarn test" always enable coverage --- modules/opendesk/element-web/package.json | 3 +-- modules/restricted-guests/synapse/package.json | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index d051def5c1..ca9e7b3f2d 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -9,8 +9,7 @@ "scripts": { "prepare": "vite build", "lint:ts": "tsc --noEmit", - "test": "echo no tests yet", - "vitest": "echo no tests yet" + "test": "echo no tests yet" }, "devDependencies": { "@element-hq/element-web-module-api": "^0.1.0", diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 529323342e..868aadef5b 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -17,7 +17,6 @@ "lint:py": "./scripts/run_in_venv.sh tox -e check_codestyle", "lint:fix": "./scripts/run_in_venv.sh tox -e fix_codestyle", "test": "./scripts/run_in_venv.sh tox -e py", - "vitest": "echo \"Nothing to vitest\"", "depcheck": "echo \"Nothing to check\"", "package": "yarn docker:build" } From 8c11b576cbc0a5bd9cd104345387970f66850b55 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 13 Feb 2025 16:04:03 -0500 Subject: [PATCH 030/211] Include Python in static analysis job Also refactor "yarn lint" of each module/package to run whatever lint task is appropriate for it --- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/synapse/package.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index ca9e7b3f2d..298d4de5c2 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -8,7 +8,7 @@ ], "scripts": { "prepare": "vite build", - "lint:ts": "tsc --noEmit", + "lint": "tsc --noEmit", "test": "echo no tests yet" }, "devDependencies": { diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 868aadef5b..3968ba3a87 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -11,8 +11,7 @@ "build": "echo \"Nothing to build\"", "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", "tsc": "echo \"Nothing to tsc\"", - "lint": "echo \"Nothing to lint\"", - "lint:ts": "echo \"Nothing to lint:ts\"", + "lint": "yarn types:py && yarn lint:py", "types:py": "./scripts/run_in_venv.sh tox -e check_types", "lint:py": "./scripts/run_in_venv.sh tox -e check_codestyle", "lint:fix": "./scripts/run_in_venv.sh tox -e fix_codestyle", From 41cce625aaa440658c1550ea3987c4e36d2f2642 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 3 Mar 2025 13:09:10 +0000 Subject: [PATCH 031/211] Iterate --- modules/opendesk/element-web/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 019aa4e2e0..54e9a37ab0 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -26,7 +26,7 @@ export default defineConfig({ target: "esnext", sourcemap: true, rollupOptions: { - external: ["react", "@matrix-org/react-sdk-module-api"], + external: ["react"], }, }, plugins: [ From e66adfecba8406b2d16c3c255df957f5e1ed51df Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 6 Mar 2025 11:24:08 -0500 Subject: [PATCH 032/211] Fix minor typo in synapse guest module README --- modules/restricted-guests/synapse/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 958806ae7f..d749a65c39 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -64,7 +64,7 @@ The module is not published to a python registry, but we provide a docker contai # ... template: spec: -+ # The init container copies the module to he `synapse-modules` volume ++ # The init container copies the module to the `synapse-modules` volume + initContainers: + - image: ghcr.io/element-hq/synapse-guest-module: + name: install-guest-module From 4890a7bd74b46e237a29fc49e82e0c37a6507dab Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 6 Mar 2025 11:50:29 -0500 Subject: [PATCH 033/211] Add line break between README copyright lines --- modules/restricted-guests/synapse/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index d749a65c39..57bbfa1294 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -94,7 +94,7 @@ The module is not published to a python registry, but we provide a docker contai ## Copyright & License -Copyright 2023 Nordeck IT + Consulting GmbH +Copyright 2023 Nordeck IT + Consulting GmbH Copyright (c) 2025 New Vector Ltd This software is multi licensed by New Vector Ltd (Element). It can be used either: From 774972817aabed328373e79b55b603b3f6bda4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Goinvic?= Date: Mon, 10 Mar 2025 12:07:49 +0100 Subject: [PATCH 034/211] add cp to synapse-guest-module image --- modules/restricted-guests/synapse/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile index 80d9b87f12..e98bbddd61 100644 --- a/modules/restricted-guests/synapse/Dockerfile +++ b/modules/restricted-guests/synapse/Dockerfile @@ -3,5 +3,6 @@ FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC} WORKDIR /src COPY synapse_guest_module /src/synapse_guest_module +COPY /bin/cp /bin/cp CMD ["cp", "-r", "/src/synapse_guest_module", "/modules"] From e068c9f60efef7bc57c50faa425f347da991d964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Goinvic?= Date: Mon, 10 Mar 2025 12:14:35 +0100 Subject: [PATCH 035/211] copy cp to target image --- modules/restricted-guests/synapse/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile index e98bbddd61..dc39a088c6 100644 --- a/modules/restricted-guests/synapse/Dockerfile +++ b/modules/restricted-guests/synapse/Dockerfile @@ -1,8 +1,12 @@ ARG DEBIAN_VERSION_NUMERIC=12 + +# Now copy it into our base image. +FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC}:debug AS build + FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC} WORKDIR /src COPY synapse_guest_module /src/synapse_guest_module -COPY /bin/cp /bin/cp +COPY --from=build /busybox/cp /bin/cp CMD ["cp", "-r", "/src/synapse_guest_module", "/modules"] From 83788cc8e0470c660b2783c2ec73c52269180775 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 14:18:31 +0000 Subject: [PATCH 036/211] Add playwright tests for the opendesk module --- .../element-web/tests/fixture/app1.png | Bin 0 -> 1588 bytes .../element-web/tests/fixture/app2.png | Bin 0 -> 13818 bytes .../element-web/tests/fixture/logo.svg | 50 +++++ .../element-web/tests/fixture/navigation.json | 44 ++++ .../tests/fixture/silent-login.html | 14 ++ .../element-web/tests/fixture/widget.html | 52 +++++ .../element-web/tests/opendesk.spec.ts | 209 ++++++++++++++++++ ...Opendesk-should-render-top-bar-1-linux.yml | 5 + ...Opendesk-should-render-top-bar-2-linux.yml | 15 ++ .../snapshots/opendesk.spec.ts/menu-linux.png | Bin 0 -> 18149 bytes .../opendesk.spec.ts/nav-hover-linux.png | Bin 0 -> 2288 bytes .../snapshots/opendesk.spec.ts/nav-linux.png | Bin 0 -> 2288 bytes .../opendesk.spec.ts/nav-loading-linux.png | Bin 0 -> 2226 bytes 13 files changed, 389 insertions(+) create mode 100644 modules/opendesk/element-web/tests/fixture/app1.png create mode 100644 modules/opendesk/element-web/tests/fixture/app2.png create mode 100644 modules/opendesk/element-web/tests/fixture/logo.svg create mode 100644 modules/opendesk/element-web/tests/fixture/navigation.json create mode 100644 modules/opendesk/element-web/tests/fixture/silent-login.html create mode 100644 modules/opendesk/element-web/tests/fixture/widget.html create mode 100644 modules/opendesk/element-web/tests/opendesk.spec.ts create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png create mode 100644 modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png diff --git a/modules/opendesk/element-web/tests/fixture/app1.png b/modules/opendesk/element-web/tests/fixture/app1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c34416b885b9728da50b3d275652cec6e9d6004 GIT binary patch literal 1588 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 T^B6||yQ} + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/opendesk/element-web/tests/fixture/navigation.json b/modules/opendesk/element-web/tests/fixture/navigation.json new file mode 100644 index 0000000000..79bac98d25 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/navigation.json @@ -0,0 +1,44 @@ +{ + "categories": [ + { + "identifier": "category1", + "display_name": "Category 1", + "entries": [ + { + "identifier": "app1", + "display_name": "App 1", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app1", + "target": "_blank" + }, + { + "identifier": "app2", + "display_name": "App 2", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app2", + "target": "_blank" + } + ] + }, + { + "identifier": "category2", + "display_name": "Category 2", + "entries": [ + { + "identifier": "app3", + "display_name": "App 3", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app3", + "target": "_blank" + }, + { + "identifier": "app4", + "display_name": "App 4", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app4", + "target": "_blank" + } + ] + } + ] +} diff --git a/modules/opendesk/element-web/tests/fixture/silent-login.html b/modules/opendesk/element-web/tests/fixture/silent-login.html new file mode 100644 index 0000000000..ae80633f60 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/silent-login.html @@ -0,0 +1,14 @@ + + + + + + Univention Silent Login + + + + diff --git a/modules/opendesk/element-web/tests/fixture/widget.html b/modules/opendesk/element-web/tests/fixture/widget.html new file mode 100644 index 0000000000..3e6a834a73 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/widget.html @@ -0,0 +1,52 @@ + + + + Demo Widget + + + +

Hello unknown!

+ + diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts new file mode 100644 index 0000000000..6598dad05a --- /dev/null +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -0,0 +1,209 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; + +import { test, expect } from "../../../../playwright/element-web-test.ts"; + +declare module "@element-hq/element-web-module-api" { + interface Config { + "net.nordeck.element_web.module.opendesk": { + config: { + banner: { + ics_navigation_json_url: string; + ics_silent_url: string; + portal_logo_svg_url: string; + portal_url: string; + }; + custom_css_variables: { + [key: `--cpd-${string}`]: string; + }; + }; + }; + "net.nordeck.element_web.module.widget_toggles": { + config: { + types: string[]; + }; + }; + "net.nordeck.element_web.module.widget_lifecycle": { + widget_permissions: { + [url: string]: { + preload_approved: boolean; + identity_approved: boolean; + capabilities_approved: string[]; + }; + }; + }; + } +} + +test.use({ + displayName: "Timmy", + synapseConfig: async ({ synapseConfig, _homeserver: homeserver }, use) => { + // Required for widget lifecycle test + (homeserver as SynapseContainer).withConfigField("listeners[0].resources[0].names", ["client", "openid"]); + await use(synapseConfig); + }, + page: async ({ context, page, moduleDir }, use) => { + for (const path of ["logo.svg", "app1.png", "app2.png"]) { + await context.route(`/${path}*`, async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/${path}` }); + }); + } + await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { + await route.fulfill({ + path: `${moduleDir}/tests/fixture/navigation.json`, + contentType: "application/json", + }); + }); + await context.route("http://localhost:8080/ics/silent", async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/silent-login.html`, contentType: "text/html" }); + }); + await context.route("http://localhost:8080/widget.html*", async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/widget.html`, contentType: "text/html" }); + }); + + await page.goto("/"); + await use(page); + }, + config: { + "net.nordeck.element_web.module.opendesk": { + config: { + banner: { + ics_navigation_json_url: "http://localhost:8080/ics/navigation.json", + ics_silent_url: "http://localhost:8080/ics/silent", + portal_logo_svg_url: "http://localhost:8080/logo.svg", + portal_url: "https://example.com/portal", + }, + + custom_css_variables: { + "--cpd-color-text-action-accent": "purple", + }, + }, + }, + "net.nordeck.element_web.module.widget_toggles": { + config: { + types: [], + }, + }, + "net.nordeck.element_web.module.widget_lifecycle": { + widget_permissions: { + "http://localhost:8080/widget.html": { + preload_approved: true, + identity_approved: true, + capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"], + }, + }, + }, + "net.nordeck.element_web.module.guest": { + config: { + guest_user_homeserver_url: "https://matrix.local/", + }, + }, + }, +}); + +test.describe("Opendesk", () => { + test("should render top-bar", { tag: ["@screenshot"] }, async ({ page }) => { + await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); + + const nav = page.locator("nav"); + await expect(nav).toMatchScreenshot(`nav_loading.png`); + // The stub silent html doesn't seem to work in Playwright so send the postMessage manually + await page.evaluate(() => { + window.postMessage({ + loggedIn: true, + }); + }); + + const trigger = page.getByLabel("Show menu"); + await expect(trigger).toBeVisible(); + + // Assert the banner looks as we expect + await expect(nav).toMatchAriaSnapshot(); + await expect(nav).toMatchScreenshot(`nav.png`); + + // Check hover styles + await trigger.hover(); + await expect(nav).toMatchScreenshot(`nav_hover.png`); + + await test.step("open menu", async () => { + await trigger.click(); + const app1 = page.getByRole("link", { name: "App 1" }); + await expect(app1).toHaveAttribute("href", "https://example.com/app1"); + await app1.hover(); + + // Assert the sidebar looks as we expect + await expect(page.getByTestId("menu-list")).toMatchAriaSnapshot(); + await expect(page).toMatchScreenshot(`menu.png`, { + // We exclude this as we don't want to assert Element's styling, only our own + css: ` + .mx_AuthPage { + opacity: 0; + } + `, + }); + }); + + await test.step("close menu", async () => { + // Assert it closes by clicking the overlay + await page.getByTestId("menu-backdrop").click(); + await expect(page.getByTestId("menu-list")).not.toBeVisible(); + }); + }); + + test("should apply custom_css_variables", async ({ page, user }) => { + await expect(page.getByRole("button", { name: "Send a Direct Message" })).toHaveCSS( + "background-color", + "rgb(128, 0, 128)", + ); // -> purple + }); + + test("should show the widget without any permission requests", async ({ page, user, homeserver }) => { + const bot = await homeserver.registerUser("bot", "password", "Bot"); + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Trusted Widget", + }, + ); + await homeserver.csApi.request<{ + event_id: string; + }>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, bot.accessToken, { + id: "1", + creatorUserId: bot.userId, + type: "custom", + name: "Trusted Widget", + url: `http://localhost:8080/widget.html?hsUrl=${encodeURIComponent(homeserver.baseUrl)}`, + }); + await homeserver.csApi.request( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, + bot.accessToken, + { + widgets: { + "1": { + container: "top", + }, + }, + }, + ); + await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { + user_id: user.userId, + }); + + await page.getByText("Trusted Widget").click(); + await page.getByText("Accept").click(); + + await expect( + page.frameLocator(`iframe[title="Trusted Widget"]`).getByRole("heading", { name: `Hello ${user.userId}!` }), + ).toBeVisible(); + }); +}); diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml new file mode 100644 index 0000000000..f6b58ee014 --- /dev/null +++ b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml @@ -0,0 +1,5 @@ +- navigation: + - link "Show portal": + - img "Portal logo" + - button "Show menu": + - img diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml new file mode 100644 index 0000000000..e979e85507 --- /dev/null +++ b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml @@ -0,0 +1,15 @@ +- list: + - listitem: + - text: Category 1 + - list: + - listitem: + - link "App 1" + - listitem: + - link "App 2" + - listitem: + - text: Category 2 + - list: + - listitem: + - link "App 3" + - listitem: + - link "App 4" diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..c4a893dd31694c2cc6d774ea531f56e19b69e052 GIT binary patch literal 18149 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 literal 0 HcmV?d00001 diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..c1adf808ab74f4723b83c3f4711bb49cb4c57cea GIT binary patch literal 2288 zcmb_eYgm%m7XHjp8BL`cwWm~K8gs0XtSL#&LpeESmRaOzqiH8C^Ac)gc&7}rGNBpM zN>j$lTZ-m2#Y8hNsio!(0hP=ILBKl*h@M@~{5a=%&cAc^kMG;-dG=m=?X}+bUHi71 ztCOzwCT$3Ube#_$Itf7Fx-vnF^xBb%*W+RNVFx&%6`+CQ}~D?w&5KG%7djZ*dE(qQqy|3gxK;|M!J*d!+x#|p>*@|nEZJmfvTi;#dWD9oi@zc z%ex;P1z}vh7}NJ&XiAK=ea5~fG@ z^))p$<(TS#6IE%c?f#n2o<4cg$O(4AdN27&01bH(;u4_WjmUZ8p1*_H{g#OJy z5(~7f{H4=eqEy6!HyuNd>{$~Oq`wH)>}|khJf=}p%8M8;u;EWXxO!MX06)02-f+r1JMvRL zH9mSg1-1|QAIq{`te1^u+Q(dl*`w3(a|Mz;qxoDantp(-`w2|3=AS7B50mv-;;U<1`JBMRl&?hKL^as z&G|$Na^EEB6Q!&GBx#pl>wcxELQCJ&yX;(xRLn85{Jj!4LVr0Ly`yf8w~r4apgY?> zM%wJk>!xD-TJL%?nM~qri&0s;sI0A(g1|(s0ti4i%*|8V*B*9qbabTAXmMU_HoK%= z%J$sp%UDkH@M&-Dhdac_$NTw>*G)6(qUL!FLhi}JIHhzXf_Vl>x}l}tm5JVI>K;KZ z^J^PqGLL83M)Vh);1j*v+4KkSwcQ}83!U++#Cw_T} zk#try+lDYRGi#$DC`>dOoqw`0Jw2U+s}qBC&Gr=2yk7c5cKq^I+X{jj6Lmi!J#B1k zSc(=S-*=szF8O_6YgukxU5sS7HWMA1ZS_wF+bk3cMHZWwm=t-G>LC{v7X0;tviF65 z0(l}xhUK4AkGFQluc~AL*-_i1k6iQd_*J_qtQ1^qG>V^vB!R3*rBdC^)+BgF zW~Ppg&UnjKC-Qzg@91!@Hx|2Lx?|hS26NYDvtgl7c;e}=XNamrqRm@YMZiv}l>i;^N}>fx}Vqsl--#U7fNC!{%@@ zX#ri2qq;PC4VAI-IfX(o%}}C9FXB~-p&I}8KbLBD16VyMJ-D_pj*4Qaq@0QfZf?H? zHqU9*YZ>)oO1OPtEU zzH()5{&DJxu&7izZRQA{n(t|+IwyN^pcpMWq+Hr|MY4XzgE~$qT-CaY@dnK0uvecbh7<30E+$>!KHhA&QjDCqP~}tKlNBd9B?W!X^9& zJ)*2;oNUKX2#b~q!eZ-s)UcL4%x^=^4td3><4pq+nJemus$y*(P>Zmx&vIXrBF1muv8` zqod>aKjc4TuTj0-O7Z77k&RiSqoxk~;uKea3f@|`)d4oo?_T|QL;sWDv{EXbisAi+ z*}Hcy&AWE@pu4;KJny{@vKWNlMmCuhH|t}TXJlL==Bj8&EoTWq{G9*8ajp{oNBQkpGvOFz~k{QE-urx{A?}L$d8~* zlm{xsR4L%cU2~5J0)aU&kYyiZO|rVgu3UP`((A^wM*-0X>V_mOC42$`2b9b%dv`s! zZRD}-Y0ry_iV6#FBhVp3_hXyotQC?H5kGt2Tz?SDvs)`G{z_prwQ ni{Y>`yTSZsG;j%7O|Gb$(0k5nocnPWn2@uh>!A{dvkCtK7QI_^ literal 0 HcmV?d00001 diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..f79c7ff28bcf87167166f1b0d7c1c3b7d440737f GIT binary patch literal 2226 zcmb_deK^x=AOAU2I~dN@i(Z`8(g`_~bT09PBE399$tsOh5*cQ#OkTE>nn zaQ)5t5Cp-GA3N*|L90Lr9fW-czFiYu7%=JJeUG|96>PI92!cf%KYYk9HgATH4g2^J zu|`%+nkoK$wRN`d{k>hN{b~7zv+NwjvG{G6LvX)cmdZ8$?ti@ev-7E8mu23Q&h742 z2)J(^eqZ^XqPQ2G_xfDh4h=5bFGw|8^AOpEYX2Hh6fuZ-lol^p%8zF)@N%V&LUhLa zD=Xaui1j<-R)T0GtQkx@?m&j1)9wHB;=y^y*2=2n?{N)_MU>mko<^h5uQ~nw{gwF! zpp-cZ^l&7kpsK1$B9WZhG~Kj4sTLHMjf8x^5A5IH2I)dj2JE{%|FLKbc22UZ#*VP; zL)pnD6^)XHYnvDfXceXI>3ZzC83GZ%`{mOk{FU(6*WBbrD zAwcLL`zI$TU9k?Fsf)h8@wQ5-ybw24bk0@YY3{l#Yqe&+mM;lu+BuGo{negWA(b$} zrfzWy3lEQ5;1i6u9?1D;Q8)&3w(Oi8hFd1y){%}Nji))pNIun*w3?Qd7SG*p_EEh@ zZV9PyI6NdIM6Fgc&N7XVa|;U{n#PzJr$ptAQ>Xgyz0QQmKMT`cHT#qI;6A^zpe4wg zlXSsRL1l19f05?^Ge|pxVTRD40WMdYGBIFcm4<0#>R%0IPYJEh?^{Y zpJ^eKAOOhQ2>N>;_xxe#cjgS2&v$-ZM5hZ=tt;*+N>Pjpv17kRoPZAi^akUSuD+6W0#x*x8ySS7ev)eZC6*9`BBO{4V6kQ@hfF8 zM8&yLPPgP&4(!7W*F=&My8-%N%FVXkL7hCAQ4TJ`d;0Y04W~eV|4r!Ct5;9-(waiL zA6cC)e$JVa%jJWD;bDN>kjdN8(Tnp16D6nVlEKPgA)A`+q6`zqhzBbz@?*DC9(&Ub z474P3PO%r+rZu#Gco=>5tSLuItCx3K*GBO-3qO7QsOYipO*WAE}Uc1(``{O-OZenLF-it+O0+`Id0Q7F_1Nt*~m z+ImD`vC`@3)NJf<%?uZfg(Lssa`T;ThL5+WA(Po`HW10+txt70x(|)Q$l9`H%Q%Xs zN1U7NTZ9qbKm2LDBfUvE&Co_a#0%MhH4!`pleO6`cJ_mV57kLAMW?khYohpZOJB2t zGMa!5-Oj5!sIw@Dm!?fQB%cIV(~X9WRd#H(wy}XD17dg~fN|DmD-4nEZgx5dVuLXl zjQ9)+wVqs9SO{8n=IZL|lGM-K-Q9T&4Gqe&RXwDzkEJNUrICbLFW9Bl6nG_WN&Xer zxEJ7|4RkbYZY7w4y$`&&#b%TnowB@l9RX};wqN30bPZXcYyD2hL9UyVEsU?)1m32iVCi1Hd zdl{f34-XG7FE1b|$Am?hEULtBnn{?|jOkU;owVhkGN9D%rsciF*PsNMoT;j^1bC2- z7#X)Hv(KZ^hy+JgZ@P#q#iD5BHcm%sHLMMMt17?X{)s^(|VRaf3?)T&^W>~w*} zi5wGhtsu^t>fK9o)gB;zTXCgn2J`Y8;B|nUD`xd=yy2$EW$8;MiQo>>J}6p-H!Bp1 z%8h1b38P7CcJ0~)#5WQzeS;fsZ=27w@M)*NLM8(bX}=s67A6(E0Sea637wprxMlup zMJXvMs|+?In_M8N6;m8~N4rYeqy<8Ih6`QEo@6G`v9-1JrgJJ;6HhQzGi!IA=&Uhz z%?;wAM$z-9`c2&fFi+n&h)Du0rj z+neXO)-A^oe|5P}d|R0RB_J@6&*%T>c5k<3RCss;FsX4Xj)+@MPV_DD1BMeE9K2iy z?h*lqpk|V13(V&?B^r3Foe!nr#7?|yv_&8g0IL)Z+xa;*gn*{6BS}6<6n%aDBO3i6 zuwJxZYFx7s1V{e$U%imv9dscs#{!Ci5g)G#OM6Tk8XEdcou8X~pa=j7H1I&5+qu>F{x4k-A4SXjDTpPM|_3sOSYC8P^Cla>qUuU0Mz9ry4)T;X={ G&c6YO=sRox literal 0 HcmV?d00001 From dc94456ca2564469a9c56595fde1c230758e4aff Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Wed, 16 Apr 2025 11:10:04 -0400 Subject: [PATCH 037/211] Add line break in Synapse guest module README so that the text after the numbered list appears in its own paragraph --- modules/restricted-guests/synapse/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 57bbfa1294..11fbc70afe 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -102,4 +102,5 @@ This software is multi licensed by New Vector Ltd (Element). It can be used eith (1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR (2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). + Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. From 457d477c2cfe86901e4aa2831e09f8c22025a106 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 28 Apr 2025 12:23:04 +0100 Subject: [PATCH 038/211] Iterate --- modules/opendesk/element-web/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 54e9a37ab0..2c13d23d91 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -28,6 +28,7 @@ export default defineConfig({ rollupOptions: { external: ["react"], }, + minify: false, }, plugins: [ react(), From f7f04242966e1c5a075cb4cfecd84cf110a48b8d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 28 Apr 2025 12:43:17 +0100 Subject: [PATCH 039/211] Iterate --- modules/opendesk/element-web/package.json | 6 +++--- modules/opendesk/element-web/src/index.ts | 2 +- modules/opendesk/element-web/tests/opendesk.spec.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 298d4de5c2..e74754c4ce 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -18,10 +18,10 @@ "@nordeck/element-web-widget-lifecycle-module": "^1.0.1", "@nordeck/element-web-widget-toggles-module": "^0.1.0", "@types/node": "^22.10.7", - "@types/react": "^18", + "@types/react": "^19", "@vitejs/plugin-react": "^4.3.4", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "react": "^19", + "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^6.0.11", diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index 5c0e746c8d..d88e7f774d 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -41,7 +41,7 @@ declare global { } class OpendeskModule implements Module { - public static readonly moduleApiVersion = "^0.1.0"; + public static readonly moduleApiVersion = "^0.1.1"; public constructor(private api: Api) {} diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts index 6598dad05a..a182535d49 100644 --- a/modules/opendesk/element-web/tests/opendesk.spec.ts +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; +import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; import { test, expect } from "../../../../playwright/element-web-test.ts"; From d608e989d06772e02e2add34cb377b941d441614 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Apr 2025 13:07:13 +0100 Subject: [PATCH 040/211] Try with production mode --- modules/opendesk/element-web/vite.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 2c13d23d91..6535c4625d 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -41,6 +41,7 @@ export default defineConfig({ }), ], define: { - process: { env: {} }, + // Use production mode for the build as it is tested against production builds of Element Web + process: { env: { NODE_ENV: "production" } }, }, }); From 796a875024a4ef2ccc30d32921e1d14f23d8efbb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Apr 2025 13:14:40 +0100 Subject: [PATCH 041/211] Update comment --- modules/opendesk/element-web/vite.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 6535c4625d..8f1d83e06f 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -41,7 +41,8 @@ export default defineConfig({ }), ], define: { - // Use production mode for the build as it is tested against production builds of Element Web + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. process: { env: { NODE_ENV: "production" } }, }, }); From 06796a0a9d545667c1365e052f551949b6bb7313 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:06:05 +0100 Subject: [PATCH 042/211] Create module element-web-module-banner --- modules/banner/README.md | 45 ++++ modules/banner/element-web/package.json | 29 +++ modules/banner/element-web/src/Banner.tsx | 65 ++++++ modules/banner/element-web/src/Logo.tsx | 39 ++++ modules/banner/element-web/src/Menu.tsx | 207 ++++++++++++++++++ .../element-web/src/Univention/Menu.tsx | 50 +++++ .../src/Univention/SilentLogin.tsx | 41 ++++ .../element-web/src/Univention/navigation.ts | 86 ++++++++ modules/banner/element-web/src/config.ts | 93 ++++++++ modules/banner/element-web/src/index.tsx | 46 ++++ modules/banner/element-web/src/theme.ts | 43 ++++ .../banner/element-web/src/translations.json | 22 ++ .../banner/element-web/tests/banner.spec.ts | 153 +++++++++++++ .../banner/element-web/tests/fixture/app1.png | Bin 0 -> 1588 bytes .../banner/element-web/tests/fixture/app2.png | Bin 0 -> 13818 bytes .../banner/element-web/tests/fixture/logo.svg | 50 +++++ .../element-web/tests/fixture/navigation.json | 44 ++++ .../tests/fixture/silent/index.html | 14 ++ modules/banner/element-web/tsconfig.json | 8 + modules/banner/element-web/vite.config.ts | 45 ++++ modules/opendesk/element-web/package.json | 2 +- 21 files changed, 1081 insertions(+), 1 deletion(-) create mode 100644 modules/banner/README.md create mode 100644 modules/banner/element-web/package.json create mode 100644 modules/banner/element-web/src/Banner.tsx create mode 100644 modules/banner/element-web/src/Logo.tsx create mode 100644 modules/banner/element-web/src/Menu.tsx create mode 100644 modules/banner/element-web/src/Univention/Menu.tsx create mode 100644 modules/banner/element-web/src/Univention/SilentLogin.tsx create mode 100644 modules/banner/element-web/src/Univention/navigation.ts create mode 100644 modules/banner/element-web/src/config.ts create mode 100644 modules/banner/element-web/src/index.tsx create mode 100644 modules/banner/element-web/src/theme.ts create mode 100644 modules/banner/element-web/src/translations.json create mode 100644 modules/banner/element-web/tests/banner.spec.ts create mode 100644 modules/banner/element-web/tests/fixture/app1.png create mode 100644 modules/banner/element-web/tests/fixture/app2.png create mode 100644 modules/banner/element-web/tests/fixture/logo.svg create mode 100644 modules/banner/element-web/tests/fixture/navigation.json create mode 100644 modules/banner/element-web/tests/fixture/silent/index.html create mode 100644 modules/banner/element-web/tsconfig.json create mode 100644 modules/banner/element-web/vite.config.ts diff --git a/modules/banner/README.md b/modules/banner/README.md new file mode 100644 index 0000000000..c62890c417 --- /dev/null +++ b/modules/banner/README.md @@ -0,0 +1,45 @@ +# @element-hq/element-web-module-banner + +Banner module for Element Web. +Allows rendering a top bar with slide out left panel menu. + +Supports the following configuration options: + +| Key | Type | Description | +| ------------- | ------ | ------------------------------------------------------------ | +| logo_url | string | URL to the logo to render in the banner | +| logo_link_url | string | URL to send the user to when clicking the logo in the banner | +| menu | `Menu` | Data to render in the banner menu | + +The `Menu` type is fulfilled by the following discriminated union: + +### Univention menu + +| Key | Type | Description | +| ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ | +| type | "univention" | The type for this menu config | +| ics_url | string | URL to the UCS Intercom Service, https://docs.software-univention.de/intercom-service/latest/architecture.html#endpoints | + +### Static menu + +| Key | Type | Description | +| ---------- | ---------------- | ------------------------------------------------------------------------- | +| type | "static" | The type for this menu config | +| logo_url | string, optional | URL to the logo to render in the menu, defaults to banner logo if omitted | +| categories | `[]Category` | Categories to render in the menu | + +The `Category` type is fulfilled by the following interface: + +| Key | Type | Description | +| ----- | -------- | -------------------------------- | +| name | string | The name of this category | +| links | `[]Link` | Links to render in this category | + +The `Link` type is fulfilled by the following interface: + +| Key | Type | Description | +| -------- | ---------------- | --------------------------------------- | +| icon_uri | string | URL to the icon to render for this link | +| name | string | The name to render for this link | +| link_url | string | The URL to link to | +| target | string, optional | The `target` to use for this link | diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json new file mode 100644 index 0000000000..85ac88a46b --- /dev/null +++ b/modules/banner/element-web/package.json @@ -0,0 +1,29 @@ +{ + "name": "@element-hq/element-web-module-banner", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "lib/index.js", + "scripts": { + "prepare": "vite build", + "lint:ts": "tsc --noEmit", + "test": "echo no tests yet" + }, + "devDependencies": { + "@element-hq/element-web-module-api": "^0.2.0", + "@types/node": "^22.10.7", + "@types/react": "^19", + "@vitejs/plugin-react": "^4.3.4", + "react": "^19", + "rollup-plugin-external-globals": "^0.13.0", + "typescript": "^5.7.3", + "vite": "^6.0.11", + "vite-plugin-node-polyfills": "^0.23.0" + }, + "dependencies": { + "@radix-ui/react-dialog": "^1.1.6", + "framer-motion": "^12.4.10", + "styled-components": "^6.1.18", + "zod": "^3.24.2" + } +} diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx new file mode 100644 index 0000000000..4ad63827ab --- /dev/null +++ b/modules/banner/element-web/src/Banner.tsx @@ -0,0 +1,65 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type FC } from "react"; +import styled, { ThemeProvider } from "styled-components"; +import { type Api } from "@element-hq/element-web-module-api"; + +import { type ModuleConfig } from "./config"; +import UniventionMenu from "./Univention/Menu"; +import Menu from "./Menu"; +import { theme } from "./theme"; +import Logo from "./Logo.tsx"; + +const Root = styled.nav` + background-color: ${({ theme }) => theme.compound.color.bgCanvasDefault}; + border-bottom: ${({ theme }) => theme.navbar.border}; + height: ${({ theme }) => theme.navbar.height}; + display: grid; + grid-template-columns: ${({ theme }) => `${theme.navbar.triggerWidth} auto`}; + gap: 24px; +`; + +const Main = styled.div` + display: flex; + align-items: center; + grid-column: 2; +`; + +interface Props { + api: Api; + logoUrl: string; + href: string; + menu: ModuleConfig["menu"]; +} + +const Banner: FC = ({ api, logoUrl, href, menu }) => { + let menuJsx; + switch (menu.type) { + case "static": { + menuJsx = ; + break; + } + case "univention": { + menuJsx = ; + break; + } + } + + return ( + + + {menuJsx} +
+ +
+
+
+ ); +}; + +export default Banner; diff --git a/modules/banner/element-web/src/Logo.tsx b/modules/banner/element-web/src/Logo.tsx new file mode 100644 index 0000000000..48840486ca --- /dev/null +++ b/modules/banner/element-web/src/Logo.tsx @@ -0,0 +1,39 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type FC } from "react"; +import styled from "styled-components"; +import { type Api } from "@element-hq/element-web-module-api"; + +const Anchor = styled.a` + display: flex; +`; + +const Image = styled.img` + height: ${({ theme }) => theme.navbar.logoHeight}; + align-self: center; +`; + +interface Props { + api: Api; + src: string; + href?: string; +} + +const Logo: FC = ({ api, src, href }) => { + const img = {api.i18n.translate("Portal; + + if (!href) return img; + + return ( + + {img} + + ); +}; + +export default Logo; diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx new file mode 100644 index 0000000000..9ca1763858 --- /dev/null +++ b/modules/banner/element-web/src/Menu.tsx @@ -0,0 +1,207 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type FC, useState } from "react"; +import { AnimatePresence, motion } from "framer-motion"; +import * as Dialog from "@radix-ui/react-dialog"; +import styled from "styled-components"; + +import { StaticConfig } from "./config"; +import { theme } from "./theme"; +import type { Api } from "@element-hq/element-web-module-api"; +import Logo from "./Logo.tsx"; + +const Sidebar = styled(motion.div)` + padding: 16px 12px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + overflow: auto; + position: fixed; + left: 0; + top: 0; + height: 100%; + width: ${({ theme }): string => theme.menu.width}; + background: white; + border-top-right-radius: 16px; + border-bottom-right-radius: 16px; +`; + +const SidebarHeading = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +`; + +const Launcher = styled.button` + align-items: center; + border: none; + color: ${({ theme }) => theme.compound.color.textPrimary}; + cursor: pointer; + display: flex; + + &:hover, + &:focus, + &[data-expanded="true"] { + background-color: ${({ theme }): string => theme.color.accent}; + color: #ffffff; + } + + svg { + margin: auto; + } +`; + +const CloseButton = styled.button` + /* Reset button styles */ + appearance: none; + background: none; + border: none; + padding: 0; + margin: 0; + + height: 32px; + width: 32px; + cursor: pointer; + border-radius: 8px; + + &:hover, + &:focus { + background-color: rgba(238, 239, 242, 1); + } +`; + +const CategoryHeading = styled.h2` + font-weight: 700; + font-size: 12px; + color: #203257; + margin-top: 16px; + margin-bottom: 8px; +`; + +const LinkButton = styled.a` + font-size: 14px; + color: #000000; + font-weight: 500; + display: flex; + border-radius: 8px; + padding: 8px; + align-items: center; + + &:hover { + background-color: #eeeff2; + } +`; + +const LinkLogo = styled.img` + height: 24px; + width: 24px; + border-radius: 3px; + border: 1px solid #eeeff2; + margin-right: 8px; + background-color: #ffffff; +`; + +const Overlay = styled(motion.div)` + background-color: rgba(238, 239, 242, 0.5); + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; +`; + +interface Props { + api: Api; + config: StaticConfig; + fallbackLogoUrl: string; +} + +const WIDTH = parseInt(theme.menu.width.slice(0, -2), 10); + +const Category: FC<{ + data: StaticConfig["categories"][number]; +}> = ({ data }) => { + return ( +
+ {data.name} + {data.links.map((link) => ( + + {link.name} + + ))} +
+ ); +}; + +const Menu: FC = ({ api, config, fallbackLogoUrl }) => { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + + {open && ( + + + + + + + + + + setOpen(false)} + > + + + + + + + {config.categories.map((category) => ( + + ))} + + + + )} + + + ); +}; + +export default Menu; diff --git a/modules/banner/element-web/src/Univention/Menu.tsx b/modules/banner/element-web/src/Univention/Menu.tsx new file mode 100644 index 0000000000..690b03e94c --- /dev/null +++ b/modules/banner/element-web/src/Univention/Menu.tsx @@ -0,0 +1,50 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { FC, useEffect, useState } from "react"; + +import type { Api } from "@element-hq/element-web-module-api"; +import { StaticConfig, UniventionConfig } from "../config"; +import StaticMenu from "../Menu"; +import { fetchNavigation } from "./navigation"; +import SilentLogin from "./SilentLogin"; + +interface Props { + api: Api; + config: UniventionConfig; + fallbackLogoUrl: string; +} + +const Menu: FC = ({ api, config, fallbackLogoUrl }) => { + const [loggedIn, setLoggedIn] = useState(false); + const [data, setData] = useState(); + const language = api.i18n.language.toLowerCase().startsWith("de") ? "de-DE" : "en"; + + useEffect(() => { + let discard = false; + + setData(null); + fetchNavigation(config.ics_url, language).then((data) => { + if (discard) return; + setData(data); + }); + + return (): void => { + discard = true; + }; + }, [config, language, loggedIn]); + + if (!loggedIn) { + return ; + } + if (data) { + return ; + } + return
; +}; + +export default Menu; diff --git a/modules/banner/element-web/src/Univention/SilentLogin.tsx b/modules/banner/element-web/src/Univention/SilentLogin.tsx new file mode 100644 index 0000000000..06814333c2 --- /dev/null +++ b/modules/banner/element-web/src/Univention/SilentLogin.tsx @@ -0,0 +1,41 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { FC, useEffect } from "react"; +import styled from "styled-components"; + +const HiddenIFrame = styled.iframe` + display: none; +`; + +interface Props { + icsUrl: string; + onLoggedIn(success: boolean): void; +} + +const SilentLogin: FC = ({ onLoggedIn, icsUrl }) => { + const url = new URL("silent", icsUrl); + + useEffect(() => { + const listener = (event: MessageEvent): void => { + if (event.origin === url.origin && typeof event.data === "object" && event.data["loggedIn"] === true) { + onLoggedIn(true); + } + }; + + window.addEventListener("message", listener); + + return (): void => { + window.removeEventListener("message", listener); + }; + }, [onLoggedIn, url.origin]); + + // TODO title? + return ; +}; + +export default SilentLogin; diff --git a/modules/banner/element-web/src/Univention/navigation.ts b/modules/banner/element-web/src/Univention/navigation.ts new file mode 100644 index 0000000000..8443d2951f --- /dev/null +++ b/modules/banner/element-web/src/Univention/navigation.ts @@ -0,0 +1,86 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { z } from "zod"; + +import { StaticConfig } from "../config"; + +/** + * Univention Central Navigation API + * https://docs.software-univention.de/nubus-kubernetes-customization/1.x/en/api/central-navigation.html + */ +export const UniventionCentralNavigation = z.object({ + categories: z.array( + z.object({ + identifier: z.string(), + display_name: z.string(), + entries: z.array( + z.object({ + /** + * A unique identifier for the navigation item. + */ + identifier: z.string(), + /** + * The URL to the icon in SVG format. + */ + icon_url: z.string(), + /** + * The label of the link. + */ + display_name: z.string(), + /** + * The destination URL of the link. + */ + link: z.string(), + /** + * The browsing context in which the browser opens the link. + * Corresponds to the `target` property of `` tags in HTML. + */ + target: z.string(), + /** + * It’s usually empty. + */ + keywords: z.object({}).optional(), + }), + ), + }), + ), +}); + +type UniventionCentralNavigation = z.infer; + +function navigationToConfig(navigation: UniventionCentralNavigation): StaticConfig { + return { + type: "static", + categories: navigation.categories.map((category) => ({ + name: category.display_name, + links: category.entries.map((entry) => ({ + icon_uri: entry.icon_url, + name: entry.display_name, + link_url: entry.link, + target: entry.target, + })), + })), + }; +} + +export async function fetchNavigation(icsUrl: string, language: string): Promise { + const url = new URL("navigation.json", icsUrl); + url.search = `?language=${language}`; + + const response = await fetch(url, { + credentials: "include", + }); + + if (!response.ok) { + throw new Error(`Failed to fetch navigation: ${response.status}`); + } + + const data = await response.json(); + const config = await UniventionCentralNavigation.parseAsync(data); + return navigationToConfig(config); +} diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts new file mode 100644 index 0000000000..3e5966bbbc --- /dev/null +++ b/modules/banner/element-web/src/config.ts @@ -0,0 +1,93 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { z } from "zod"; + +const StaticConfig = z.object({ + type: z.literal("static"), + + /** + * Alternative logo URL to display in the popover menu. + * Will use the main logo url if omitted. + */ + logo_url: z.string().url().optional(), + + /** + * Categories of links to display in the menu. + */ + categories: z.array( + z.object({ + /** + * The category display name + */ + name: z.string(), + /** + * List of links to display in the category + */ + links: z.array( + z.object({ + /** + * The URL to the icon. + */ + icon_uri: z.string().url(), + /** + * The label of the link. + */ + name: z.string(), + /** + * The destination URL of the link. + */ + link_url: z.string().url(), + /** + * The browsing context in which the browser opens the link. + */ + target: z.string().optional(), + }), + ), + }), + ), +}); + +export type StaticConfig = z.infer; + +const UniventionConfig = z.object({ + type: z.literal("univention"), + + /** + * Base URL to an Intercom Service + * https://docs.software-univention.de/intercom-service/latest/architecture.html#endpoints + */ + ics_url: z.string().url(), +}); + +export type UniventionConfig = z.infer; + +export const ModuleConfig = z.object({ + /** + * The URL of the portal logo.svg file. + * @example `https://example.com/logo.svg` + */ + logo_url: z.string().url(), + + /** + * The URL of the portal. + * @example `https://example.com` + */ + logo_link_url: z.string().url(), + + menu: z.discriminatedUnion("type", [StaticConfig, UniventionConfig]), +}); + +export type ModuleConfig = z.infer; + +export const CONFIG_KEY = "io.element.element-web-modules.banner"; + +declare module "@element-hq/element-web-module-api" { + export interface Config { + [CONFIG_KEY]: ModuleConfig; + } +} diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx new file mode 100644 index 0000000000..9eeb972671 --- /dev/null +++ b/modules/banner/element-web/src/index.tsx @@ -0,0 +1,46 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; +import Translations from "./translations.json"; +import { ModuleConfig, CONFIG_KEY } from "./config"; +import Banner from "./Banner"; +import { name as ModuleName } from "../package.json"; + +class TopBarModule implements Module { + public static readonly moduleApiVersion = "^0.2.0"; + + private config?: ModuleConfig; + + public constructor(private api: Api) {} + + public async load(): Promise { + this.api.i18n.register(Translations); + + try { + this.config = ModuleConfig.parse(this.api.config.get(CONFIG_KEY)); + } catch (e) { + console.error("Failed to init module", e); + throw new Error(`Errors in module configuration for "${ModuleName}"`); + } + + const div = document.createElement("div"); + this.api.rootNode.before(div); + + const root = this.api.createRoot(div); + root.render( + , + ); + } +} + +export default TopBarModule satisfies ModuleFactory; diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts new file mode 100644 index 0000000000..4eec071127 --- /dev/null +++ b/modules/banner/element-web/src/theme.ts @@ -0,0 +1,43 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { DefaultTheme } from "styled-components"; + +const bgCanvasDefault = "var(--cpd-color-bg-canvas-default)"; +const textActionAccent = "var(--cpd-color-text-action-accent)"; +const textPrimary = "var(--cpd-color-text-primary)"; +const iconOnSolidPrimary = "var(--cpd-color-icon-on-solid-primary)"; + +const bodyMdSemibold = "var(--cpd-font-body-md-semibold)"; + +export const theme: DefaultTheme = { + compound: { + color: { + bgCanvasDefault, + textActionAccent, + textPrimary, + iconOnSolidPrimary, + }, + font: { + bodyMdSemibold, + }, + }, + color: { + accent: "#571EFA", // primary/700 TODO + }, + navbar: { + border: "1px solid #D3D7DE", // TODO + boxShadow: "4px 4px 12px 0 rgba(118, 131, 156, 0.6)", + height: "60px", + triggerWidth: "68px", + logoHeight: "34px", + }, + menu: { + // TODO + width: "235px", + }, +}; diff --git a/modules/banner/element-web/src/translations.json b/modules/banner/element-web/src/translations.json new file mode 100644 index 0000000000..a177686956 --- /dev/null +++ b/modules/banner/element-web/src/translations.json @@ -0,0 +1,22 @@ +{ + "Portal logo": { + "en": "Portal logo", + "de": "Portal Logo" + }, + "Menu": { + "en": "Menu", + "de": "Menü" + }, + "Show menu": { + "en": "Show menu", + "de": "Menü anzeigen" + }, + "Close menu": { + "en": "Close menu", + "de": "Menü schließen" + }, + "Show portal": { + "en": "Show portal", + "de": "Portal anzeigen" + } +} diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts new file mode 100644 index 0000000000..7637b688ad --- /dev/null +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -0,0 +1,153 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { test, expect } from "../../../../playwright/element-web-test.ts"; +import { ModuleConfig } from "../src/config.ts"; + +test.describe("Banner", () => { + test.use({ + displayName: "Timmy", + page: async ({ context, page, moduleDir }, use) => { + for (const path of ["logo.svg", "app1.png", "app2.png"]) { + await context.route(`/${path}*`, async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/${path}` }); + }); + } + await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { + await route.fulfill({ + path: `${moduleDir}/tests/fixture/navigation.json`, + contentType: "application/json", + }); + }); + await context.route("http://localhost:8080/ics/silent", async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/silent/index.html`, contentType: "text/html" }); + }); + + await page.goto("/"); + await use(page); + }, + }); + + test("should error if config is missing", { tag: ["@screenshot"] }, async ({ page }) => { + await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + // We don't take a screenshot as we don't want to assert Element's styling, only our own + }); + + const configs: ModuleConfig[] = [ + { + logo_url: "http://localhost:8080/logo.svg", + logo_link_url: "https://example.com/portal", + menu: { + type: "static", + categories: [ + { + name: "Applications", + links: [ + { + icon_uri: "http://localhost:8080/app1.png", + name: "App 1", + link_url: "https://example.com/app1", + target: "app1", + }, + { + icon_uri: "http://localhost:8080/app2.png", + name: "Riot", + link_url: "https://riot.im/app", + target: "riot-im", + }, + ], + }, + { + name: "Links", + links: [ + { + icon_uri: "http://localhost:8080/app1.png", + name: "Link", + link_url: "https://example.com/link1", + }, + ], + }, + ], + }, + }, + { + logo_url: "http://localhost:8080/logo.svg", + logo_link_url: "https://example.com/portal", + menu: { + type: "univention", + ics_url: "http://localhost:8080/ics/", + }, + }, + ]; + + for (const config of configs) { + const type = config.menu.type; + + test.describe(`${type} config`, () => { + test.use({ + config: { + "io.element.element-web-modules.banner": config, + }, + }); + + test("should render", { tag: ["@screenshot"] }, async ({ page, axe }) => { + await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); + + const nav = page.locator("nav"); + if (type === "univention") { + await expect(nav).toMatchScreenshot(`${type}_nav_loading.png`); + // The stub silent html doesn't seem to work in Playwright so send the postMessage manually + await page.evaluate(() => { + window.postMessage({ + loggedIn: true, + }); + }); + } + + const trigger = page.getByLabel("Show menu"); + await expect(trigger).toBeVisible(); + + // Assert the banner looks as we expect + await expect(nav).toMatchAriaSnapshot(); + await expect(nav).toMatchScreenshot(`${type}_nav.png`); + + // Check hover styles + await trigger.hover(); + await expect(nav).toMatchScreenshot(`${type}_nav_hover.png`); + + await test.step("open menu", async () => { + await trigger.click(); + const app1 = page.getByText("App 1"); + await expect(app1).toHaveAttribute("href", "https://example.com/app1"); + await app1.hover(); + + // Assert the sidebar looks as we expect + const sidebar = page.getByRole("dialog"); + await expect(sidebar).toMatchAriaSnapshot(); + await expect(page).toMatchScreenshot(`${type}_menu.png`, { + // We exclude this as we don't want to assert Element's styling, only our own + css: ` + #matrixchat { + opacity: 0; + background: orchid; + } + `, + }); + }); + + await test.step("close menu", async () => { + await expect(axe).toHaveNoViolations(); + + // Assert it closes by clicking the overlay + await page.getByTestId("dialog-overlay").click(); + await expect(page.getByRole("dialog")).not.toBeVisible(); + }); + }); + }); + } +}); diff --git a/modules/banner/element-web/tests/fixture/app1.png b/modules/banner/element-web/tests/fixture/app1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c34416b885b9728da50b3d275652cec6e9d6004 GIT binary patch literal 1588 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 T^B6||yQ} + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/banner/element-web/tests/fixture/navigation.json b/modules/banner/element-web/tests/fixture/navigation.json new file mode 100644 index 0000000000..79bac98d25 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/navigation.json @@ -0,0 +1,44 @@ +{ + "categories": [ + { + "identifier": "category1", + "display_name": "Category 1", + "entries": [ + { + "identifier": "app1", + "display_name": "App 1", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app1", + "target": "_blank" + }, + { + "identifier": "app2", + "display_name": "App 2", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app2", + "target": "_blank" + } + ] + }, + { + "identifier": "category2", + "display_name": "Category 2", + "entries": [ + { + "identifier": "app3", + "display_name": "App 3", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app3", + "target": "_blank" + }, + { + "identifier": "app4", + "display_name": "App 4", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app4", + "target": "_blank" + } + ] + } + ] +} diff --git a/modules/banner/element-web/tests/fixture/silent/index.html b/modules/banner/element-web/tests/fixture/silent/index.html new file mode 100644 index 0000000000..ae80633f60 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/silent/index.html @@ -0,0 +1,14 @@ + + + + + + Univention Silent Login + + + + diff --git a/modules/banner/element-web/tsconfig.json b/modules/banner/element-web/tsconfig.json new file mode 100644 index 0000000000..fbda6380e7 --- /dev/null +++ b/modules/banner/element-web/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts new file mode 100644 index 0000000000..48d989a42c --- /dev/null +++ b/modules/banner/element-web/vite.config.ts @@ -0,0 +1,45 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.tsx"), + name: "element-web-module-banner", + fileName: "index", + formats: ["es"], + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + rollupOptions: { + external: ["react"], + }, + }, + plugins: [ + react(), + nodePolyfills({ + include: ["events"], + }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], + define: { + process: { env: {} }, + }, +}); diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index e74754c4ce..438d65dfa1 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -12,7 +12,7 @@ "test": "echo no tests yet" }, "devDependencies": { - "@element-hq/element-web-module-api": "^0.1.0", + "@element-hq/element-web-module-api": "^0.2.0", "@nordeck/element-web-guest-module": "^2.0.0", "@nordeck/element-web-opendesk-module": "^0.5.0", "@nordeck/element-web-widget-lifecycle-module": "^1.0.1", From 9b57f46ccc65196333895e266b5ee04d49cf7840 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:23:13 +0100 Subject: [PATCH 043/211] Standardise lint/type scripts between modules/packages --- modules/opendesk/element-web/package.json | 3 ++- modules/restricted-guests/synapse/package.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index e74754c4ce..5495993d53 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -8,7 +8,8 @@ ], "scripts": { "prepare": "vite build", - "lint": "tsc --noEmit", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "echo no tests yet" }, "devDependencies": { diff --git a/modules/restricted-guests/synapse/package.json b/modules/restricted-guests/synapse/package.json index 3968ba3a87..327cfbef97 100644 --- a/modules/restricted-guests/synapse/package.json +++ b/modules/restricted-guests/synapse/package.json @@ -11,9 +11,9 @@ "build": "echo \"Nothing to build\"", "docker:build": "docker build -t element-hq/synapse-guest-module -f Dockerfile .", "tsc": "echo \"Nothing to tsc\"", - "lint": "yarn types:py && yarn lint:py", - "types:py": "./scripts/run_in_venv.sh tox -e check_types", - "lint:py": "./scripts/run_in_venv.sh tox -e check_codestyle", + "lint": "yarn lint:types && yarn lint:codestyle", + "lint:types": "./scripts/run_in_venv.sh tox -e check_types", + "lint:codestyle": "./scripts/run_in_venv.sh tox -e check_codestyle", "lint:fix": "./scripts/run_in_venv.sh tox -e fix_codestyle", "test": "./scripts/run_in_venv.sh tox -e py", "depcheck": "echo \"Nothing to check\"", From a111937d00f919cbb7cc39a50120bc4f11ca7cda Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:43:50 +0100 Subject: [PATCH 044/211] Iterate --- modules/banner/element-web/package.json | 3 +- modules/banner/element-web/src/Banner.tsx | 8 ++--- modules/banner/element-web/src/Logo.tsx | 2 +- modules/banner/element-web/src/Menu.tsx | 2 +- .../element-web/src/Univention/navigation.ts | 2 +- modules/banner/element-web/src/theme.ts | 29 +++++++++++++++++++ 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 85ac88a46b..bdc07531a0 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -6,7 +6,8 @@ "main": "lib/index.js", "scripts": { "prepare": "vite build", - "lint:ts": "tsc --noEmit", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "echo no tests yet" }, "devDependencies": { diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 4ad63827ab..2d957f9b6e 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -16,11 +16,11 @@ import { theme } from "./theme"; import Logo from "./Logo.tsx"; const Root = styled.nav` - background-color: ${({ theme }) => theme.compound.color.bgCanvasDefault}; - border-bottom: ${({ theme }) => theme.navbar.border}; - height: ${({ theme }) => theme.navbar.height}; + background-color: ${({ theme }): string => theme.compound.color.bgCanvasDefault}; + border-bottom: ${({ theme }): string => theme.navbar.border}; + height: ${({ theme }): string => theme.navbar.height}; display: grid; - grid-template-columns: ${({ theme }) => `${theme.navbar.triggerWidth} auto`}; + grid-template-columns: ${({ theme }): string => `${theme.navbar.triggerWidth} auto`}; gap: 24px; `; diff --git a/modules/banner/element-web/src/Logo.tsx b/modules/banner/element-web/src/Logo.tsx index 48840486ca..0866bf802c 100644 --- a/modules/banner/element-web/src/Logo.tsx +++ b/modules/banner/element-web/src/Logo.tsx @@ -14,7 +14,7 @@ const Anchor = styled.a` `; const Image = styled.img` - height: ${({ theme }) => theme.navbar.logoHeight}; + height: ${({ theme }): string => theme.navbar.logoHeight}; align-self: center; `; diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 9ca1763858..033bc78f9d 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -39,7 +39,7 @@ const SidebarHeading = styled.div` const Launcher = styled.button` align-items: center; border: none; - color: ${({ theme }) => theme.compound.color.textPrimary}; + color: ${({ theme }): string => theme.compound.color.textPrimary}; cursor: pointer; display: flex; diff --git a/modules/banner/element-web/src/Univention/navigation.ts b/modules/banner/element-web/src/Univention/navigation.ts index 8443d2951f..96b035d3ed 100644 --- a/modules/banner/element-web/src/Univention/navigation.ts +++ b/modules/banner/element-web/src/Univention/navigation.ts @@ -13,7 +13,7 @@ import { StaticConfig } from "../config"; * Univention Central Navigation API * https://docs.software-univention.de/nubus-kubernetes-customization/1.x/en/api/central-navigation.html */ -export const UniventionCentralNavigation = z.object({ +const UniventionCentralNavigation = z.object({ categories: z.array( z.object({ identifier: z.string(), diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index 4eec071127..300cf1924a 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -14,6 +14,35 @@ const iconOnSolidPrimary = "var(--cpd-color-icon-on-solid-primary)"; const bodyMdSemibold = "var(--cpd-font-body-md-semibold)"; +declare module "styled-components" { + export interface DefaultTheme { + compound: { + color: { + bgCanvasDefault: string; + textActionAccent: string; + textPrimary: string; + iconOnSolidPrimary: string; + }; + font: { + bodyMdSemibold: string; + }; + }; + color: { + accent: string; + }; + navbar: { + border: string; + boxShadow: string; + height: string; + triggerWidth: string; + logoHeight: string; + }; + menu: { + width: string; + }; + } +} + export const theme: DefaultTheme = { compound: { color: { From 5ecc754c94e127d5efcad60367037af14c39475e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:34:02 +0100 Subject: [PATCH 045/211] Stash --- modules/banner/element-web/src/config.ts | 6 ++ .../banner/element-web/tests/banner.spec.ts | 8 +- .../element-web/tests/fixture/navigation.json | 79 ++++++++++++++----- .../tests/fixture/opendesk/app1.svg | 3 + .../tests/fixture/opendesk/app10.svg | 4 + .../tests/fixture/opendesk/app11.svg | 4 + .../tests/fixture/opendesk/app2.svg | 6 ++ .../tests/fixture/opendesk/app3.svg | 4 + .../tests/fixture/opendesk/app4.svg | 4 + .../tests/fixture/opendesk/app5.svg | 4 + .../tests/fixture/opendesk/app6.svg | 7 ++ .../tests/fixture/opendesk/app7.svg | 6 ++ .../tests/fixture/opendesk/app8.svg | 4 + .../tests/fixture/opendesk/app9.svg | 5 ++ .../tests/fixture/opendesk/logofull.svg | 13 +++ .../tests/fixture/opendesk/logomark.svg | 4 + modules/banner/element-web/vite.config.ts | 4 +- modules/opendesk/element-web/src/index.ts | 2 +- .../element-web/tests/opendesk.spec.ts | 2 +- 19 files changed, 145 insertions(+), 24 deletions(-) create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app1.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app10.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app11.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app2.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app3.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app4.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app5.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app6.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app7.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app8.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/app9.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/logofull.svg create mode 100644 modules/banner/element-web/tests/fixture/opendesk/logomark.svg diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts index 3e5966bbbc..8b6db1c122 100644 --- a/modules/banner/element-web/src/config.ts +++ b/modules/banner/element-web/src/config.ts @@ -57,6 +57,12 @@ export type StaticConfig = z.infer; const UniventionConfig = z.object({ type: z.literal("univention"), + /** + * Alternative logo URL to display in the popover menu. + * Will use the main logo url if omitted. + */ + logo_url: z.string().url().optional(), + /** * Base URL to an Intercom Service * https://docs.software-univention.de/intercom-service/latest/architecture.html#endpoints diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index 7637b688ad..4795b67a6a 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -12,9 +12,10 @@ test.describe("Banner", () => { test.use({ displayName: "Timmy", page: async ({ context, page, moduleDir }, use) => { - for (const path of ["logo.svg", "app1.png", "app2.png"]) { + for (const path of ["logo.svg", "app1.png", "app2.png", "opendesk/"]) { await context.route(`/${path}*`, async (route) => { - await route.fulfill({ path: `${moduleDir}/tests/fixture/${path}` }); + const file = new URL(route.request().url()).pathname; + await route.fulfill({ path: `${moduleDir}/tests/fixture/${file}` }); }); } await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { @@ -75,10 +76,11 @@ test.describe("Banner", () => { }, }, { - logo_url: "http://localhost:8080/logo.svg", + logo_url: "http://localhost:8080/opendesk/logomark.svg", logo_link_url: "https://example.com/portal", menu: { type: "univention", + logo_url: "http://localhost:8080/opendesk/logofull.svg", ics_url: "http://localhost:8080/ics/", }, }, diff --git a/modules/banner/element-web/tests/fixture/navigation.json b/modules/banner/element-web/tests/fixture/navigation.json index 79bac98d25..7c05eca422 100644 --- a/modules/banner/element-web/tests/fixture/navigation.json +++ b/modules/banner/element-web/tests/fixture/navigation.json @@ -1,41 +1,84 @@ { "categories": [ { - "identifier": "category1", - "display_name": "Category 1", + "identifier": "applications", + "display_name": "Anwendungen", "entries": [ { "identifier": "app1", - "display_name": "App 1", - "icon_url": "http://localhost:8080/app1.png", + "display_name": "Portal", + "icon_url": "http://localhost:8080/opendesk/app1.svg", "link": "https://example.com/app1", "target": "_blank" }, { "identifier": "app2", - "display_name": "App 2", - "icon_url": "http://localhost:8080/app2.png", + "display_name": "Aufgaben", + "icon_url": "http://localhost:8080/opendesk/app2.svg", "link": "https://example.com/app2", "target": "_blank" - } - ] - }, - { - "identifier": "category2", - "display_name": "Category 2", - "entries": [ + }, { "identifier": "app3", - "display_name": "App 3", - "icon_url": "http://localhost:8080/app1.png", + "display_name": "Chat", + "icon_url": "http://localhost:8080/opendesk/app3.svg", "link": "https://example.com/app3", "target": "_blank" }, { "identifier": "app4", - "display_name": "App 4", - "icon_url": "http://localhost:8080/app2.png", - "link": "https://example.com/app4", + "display_name": "Dateien", + "icon_url": "http://localhost:8080/opendesk/app4.svg", + "link": "https://example.com/ap4", + "target": "_blank" + }, + { + "identifier": "app5", + "display_name": "E-Mail", + "icon_url": "http://localhost:8080/opendesk/app5.svg", + "link": "https://example.com/app5", + "target": "_blank" + }, + { + "identifier": "app6", + "display_name": "Kelender", + "icon_url": "http://localhost:8080/opendesk/app6.svg", + "link": "https://example.com/app6", + "target": "_blank" + }, + { + "identifier": "app7", + "display_name": "Kontakte", + "icon_url": "http://localhost:8080/opendesk/app7.svg", + "link": "https://example.com/app7", + "target": "_blank" + }, + { + "identifier": "app8", + "display_name": "Notizen", + "icon_url": "http://localhost:8080/opendesk/app8.svg", + "link": "https://example.com/app8", + "target": "_blank" + }, + { + "identifier": "app9", + "display_name": "Projekte", + "icon_url": "http://localhost:8080/opendesk/app9.svg", + "link": "https://example.com/app9", + "target": "_blank" + }, + { + "identifier": "app10", + "display_name": "Videokonferenz", + "icon_url": "http://localhost:8080/opendesk/app10.svg", + "link": "https://example.com/app10", + "target": "_blank" + }, + { + "identifier": "app11", + "display_name": "Wiki", + "icon_url": "http://localhost:8080/opendesk/app11.svg", + "link": "https://example.com/app11", "target": "_blank" } ] diff --git a/modules/banner/element-web/tests/fixture/opendesk/app1.svg b/modules/banner/element-web/tests/fixture/opendesk/app1.svg new file mode 100644 index 0000000000..758e28621f --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app1.svg @@ -0,0 +1,3 @@ + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app10.svg b/modules/banner/element-web/tests/fixture/opendesk/app10.svg new file mode 100644 index 0000000000..147057229c --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app10.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app11.svg b/modules/banner/element-web/tests/fixture/opendesk/app11.svg new file mode 100644 index 0000000000..d853d42473 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app11.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app2.svg b/modules/banner/element-web/tests/fixture/opendesk/app2.svg new file mode 100644 index 0000000000..a537b52199 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app3.svg b/modules/banner/element-web/tests/fixture/opendesk/app3.svg new file mode 100644 index 0000000000..90f21f0667 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app4.svg b/modules/banner/element-web/tests/fixture/opendesk/app4.svg new file mode 100644 index 0000000000..6c78f1bf58 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app5.svg b/modules/banner/element-web/tests/fixture/opendesk/app5.svg new file mode 100644 index 0000000000..90d15399f3 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app5.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app6.svg b/modules/banner/element-web/tests/fixture/opendesk/app6.svg new file mode 100644 index 0000000000..dbdba6b547 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app6.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app7.svg b/modules/banner/element-web/tests/fixture/opendesk/app7.svg new file mode 100644 index 0000000000..e88e54cbfe --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app7.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app8.svg b/modules/banner/element-web/tests/fixture/opendesk/app8.svg new file mode 100644 index 0000000000..1658f680ec --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app8.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/app9.svg b/modules/banner/element-web/tests/fixture/opendesk/app9.svg new file mode 100644 index 0000000000..590bc7dc27 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/app9.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/logofull.svg b/modules/banner/element-web/tests/fixture/opendesk/logofull.svg new file mode 100644 index 0000000000..eaadffcfe3 --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/logofull.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/modules/banner/element-web/tests/fixture/opendesk/logomark.svg b/modules/banner/element-web/tests/fixture/opendesk/logomark.svg new file mode 100644 index 0000000000..d23f0ec64c --- /dev/null +++ b/modules/banner/element-web/tests/fixture/opendesk/logomark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index 48d989a42c..79d308a798 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -40,6 +40,8 @@ export default defineConfig({ }), ], define: { - process: { env: {} }, + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + process: { env: { NODE_ENV: "production" } }, }, }); diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index d88e7f774d..f8f9ab289e 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -41,7 +41,7 @@ declare global { } class OpendeskModule implements Module { - public static readonly moduleApiVersion = "^0.1.1"; + public static readonly moduleApiVersion = "^0.2.0"; public constructor(private api: Api) {} diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts index a182535d49..5c7a79e926 100644 --- a/modules/opendesk/element-web/tests/opendesk.spec.ts +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -87,7 +87,7 @@ test.use({ }, "net.nordeck.element_web.module.widget_toggles": { config: { - types: [], + types: ["jitsi"], }, }, "net.nordeck.element_web.module.widget_lifecycle": { From 333f985065126f76d864cdacf9db1e8790e8a1c3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:40:26 +0100 Subject: [PATCH 046/211] Initial stable release of the Module API Primarily to get away from semver treating every update as breaking in the 0.x.y series. --- modules/opendesk/element-web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index e74754c4ce..1f860d2920 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -12,7 +12,7 @@ "test": "echo no tests yet" }, "devDependencies": { - "@element-hq/element-web-module-api": "^0.1.0", + "@element-hq/element-web-module-api": "^1.0.0", "@nordeck/element-web-guest-module": "^2.0.0", "@nordeck/element-web-opendesk-module": "^0.5.0", "@nordeck/element-web-widget-lifecycle-module": "^1.0.1", From 1985a351d9945dc7e2c9062a93071dc37edeab08 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 May 2025 09:27:27 +0100 Subject: [PATCH 047/211] Bump module API version --- modules/banner/element-web/package.json | 2 +- modules/banner/element-web/src/index.tsx | 2 +- modules/opendesk/element-web/src/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index bdc07531a0..2c5076379d 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -11,7 +11,7 @@ "test": "echo no tests yet" }, "devDependencies": { - "@element-hq/element-web-module-api": "^0.2.0", + "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^4.3.4", diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index 9eeb972671..cc1e1e5123 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -12,7 +12,7 @@ import Banner from "./Banner"; import { name as ModuleName } from "../package.json"; class TopBarModule implements Module { - public static readonly moduleApiVersion = "^0.2.0"; + public static readonly moduleApiVersion = "^1.0.0"; private config?: ModuleConfig; diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index f8f9ab289e..e50aac9ce4 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -41,7 +41,7 @@ declare global { } class OpendeskModule implements Module { - public static readonly moduleApiVersion = "^0.2.0"; + public static readonly moduleApiVersion = "^1.0.0"; public constructor(private api: Api) {} From 3c7bb885fdbccea9bf9c6a57bc7d7a4bd82062a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 May 2025 11:56:58 +0100 Subject: [PATCH 048/211] Iterate --- modules/banner/element-web/package.json | 2 + modules/banner/element-web/src/Logo.tsx | 4 +- modules/banner/element-web/src/Menu.tsx | 91 ++++++++++----- .../element-web/src/Univention/Menu.tsx | 30 ++--- .../banner/element-web/src/translations.json | 14 ++- .../banner/element-web/tests/banner.spec.ts | 108 +++++++++++++----- .../element-web/tests/fixture/navigation.json | 2 +- .../tests/fixture/silent/index.html | 6 +- 8 files changed, 174 insertions(+), 83 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 2c5076379d..6aa9b89e56 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -23,6 +23,8 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", + "@vector-im/compound-design-tokens": "^4.0.3", + "@vector-im/compound-web": "^7.11.0", "framer-motion": "^12.4.10", "styled-components": "^6.1.18", "zod": "^3.24.2" diff --git a/modules/banner/element-web/src/Logo.tsx b/modules/banner/element-web/src/Logo.tsx index 0866bf802c..d515cfdb4e 100644 --- a/modules/banner/element-web/src/Logo.tsx +++ b/modules/banner/element-web/src/Logo.tsx @@ -25,12 +25,12 @@ interface Props { } const Logo: FC = ({ api, src, href }) => { - const img = {api.i18n.translate("Portal; + const img = {api.i18n.translate("logo_alt")}; if (!href) return img; return ( - + {img} ); diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 033bc78f9d..df6771a56d 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -5,10 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { type FC, useState } from "react"; +import { type FC, type JSX, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import * as Dialog from "@radix-ui/react-dialog"; import styled from "styled-components"; +import { InlineSpinner } from "@vector-im/compound-web"; import { StaticConfig } from "./config"; import { theme } from "./theme"; @@ -16,7 +17,7 @@ import type { Api } from "@element-hq/element-web-module-api"; import Logo from "./Logo.tsx"; const Sidebar = styled(motion.div)` - padding: 16px 12px; + padding: 16px 12px 0; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); overflow: auto; position: fixed; @@ -105,6 +106,19 @@ const LinkLogo = styled.img` background-color: #ffffff; `; +const CentredContainer = styled.div` + display: flex; + height: 100%; + width: 100%; + align-items: center; + text-align: center; + font-weight: 600; + + svg { + margin: 0 auto; + } +`; + const Overlay = styled(motion.div)` background-color: rgba(238, 239, 242, 0.5); position: fixed; @@ -116,7 +130,7 @@ const Overlay = styled(motion.div)` interface Props { api: Api; - config: StaticConfig; + config: StaticConfig | Error | null; // null for loading fallbackLogoUrl: string; } @@ -140,10 +154,29 @@ const Category: FC<{ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { const [open, setOpen] = useState(false); + let content: JSX.Element; + if (config instanceof Error) { + content = {api.i18n.translate("univention_error")}; + } else if (config) { + content = ( + <> + {config.categories.map((category) => ( + + ))} + + ); + } else { + content = ( + + + + ); + } + return ( - + @@ -168,33 +201,33 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { animate={{ x: 0 }} exit={{ x: -WIDTH }} transition={{ type: "tween", ease: "easeInOut", duration: 0.3 }} - aria-label={api.i18n.translate("Menu")} + aria-label={api.i18n.translate("menu_label")} > - - - - setOpen(false)} - > - + + + + setOpen(false)} > - - - - - - {config.categories.map((category) => ( - - ))} + + + + + + + + {content} diff --git a/modules/banner/element-web/src/Univention/Menu.tsx b/modules/banner/element-web/src/Univention/Menu.tsx index 690b03e94c..7416c99232 100644 --- a/modules/banner/element-web/src/Univention/Menu.tsx +++ b/modules/banner/element-web/src/Univention/Menu.tsx @@ -21,30 +21,34 @@ interface Props { const Menu: FC = ({ api, config, fallbackLogoUrl }) => { const [loggedIn, setLoggedIn] = useState(false); - const [data, setData] = useState(); + const [data, setData] = useState(); const language = api.i18n.language.toLowerCase().startsWith("de") ? "de-DE" : "en"; useEffect(() => { let discard = false; - setData(null); - fetchNavigation(config.ics_url, language).then((data) => { - if (discard) return; - setData(data); - }); + setData(undefined); + fetchNavigation(config.ics_url, language) + .then((data) => { + if (discard) return; + setData(data); + }) + .catch((error) => { + if (discard) return; + setData(error); + }); return (): void => { discard = true; }; }, [config, language, loggedIn]); - if (!loggedIn) { - return ; - } - if (data) { - return ; - } - return
; + return ( + <> + {!loggedIn && } + + + ); }; export default Menu; diff --git a/modules/banner/element-web/src/translations.json b/modules/banner/element-web/src/translations.json index a177686956..be2627e3aa 100644 --- a/modules/banner/element-web/src/translations.json +++ b/modules/banner/element-web/src/translations.json @@ -1,22 +1,26 @@ { - "Portal logo": { + "logo_alt": { "en": "Portal logo", "de": "Portal Logo" }, - "Menu": { + "menu_label": { "en": "Menu", "de": "Menü" }, - "Show menu": { + "trigger_label": { "en": "Show menu", "de": "Menü anzeigen" }, - "Close menu": { + "close_label": { "en": "Close menu", "de": "Menü schließen" }, - "Show portal": { + "logo_link_label": { "en": "Show portal", "de": "Portal anzeigen" + }, + "univention_error": { + "en": "Failed to load data from Univention", + "de": "Daten konnten nicht von Univention geladen werden" } } diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index 4795b67a6a..41216f91e5 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -11,6 +11,9 @@ import { ModuleConfig } from "../src/config.ts"; test.describe("Banner", () => { test.use({ displayName: "Timmy", + navigationJsonResolver: async ({}, use) => { + await use(Promise.withResolvers()); + }, page: async ({ context, page, moduleDir }, use) => { for (const path of ["logo.svg", "app1.png", "app2.png", "opendesk/"]) { await context.route(`/${path}*`, async (route) => { @@ -18,15 +21,6 @@ test.describe("Banner", () => { await route.fulfill({ path: `${moduleDir}/tests/fixture/${file}` }); }); } - await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { - await route.fulfill({ - path: `${moduleDir}/tests/fixture/navigation.json`, - contentType: "application/json", - }); - }); - await context.route("http://localhost:8080/ics/silent", async (route) => { - await route.fulfill({ path: `${moduleDir}/tests/fixture/silent/index.html`, contentType: "text/html" }); - }); await page.goto("/"); await use(page); @@ -35,6 +29,7 @@ test.describe("Banner", () => { test("should error if config is missing", { tag: ["@screenshot"] }, async ({ page }) => { await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + await expect(page.getByText("Errors in module configuration")).toBeVisible(); // We don't take a screenshot as we don't want to assert Element's styling, only our own }); @@ -50,8 +45,8 @@ test.describe("Banner", () => { links: [ { icon_uri: "http://localhost:8080/app1.png", - name: "App 1", - link_url: "https://example.com/app1", + name: "E-Mail", + link_url: "https://example.com/email", target: "app1", }, { @@ -96,20 +91,27 @@ test.describe("Banner", () => { }, }); - test("should render", { tag: ["@screenshot"] }, async ({ page, axe }) => { + test.beforeEach(async ({ context, moduleDir, navigationJsonResolver }) => { + await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { + await navigationJsonResolver.promise; + await route.fulfill({ + path: `${moduleDir}/tests/fixture/navigation.json`, + contentType: "application/json", + }); + }); + await context.route("http://localhost:8080/ics/silent", async (route) => { + await route.fulfill({ + path: `${moduleDir}/tests/fixture/silent/index.html`, + contentType: "text/html", + }); + }); + }); + + test("should render", { tag: ["@screenshot"] }, async ({ page, axe, navigationJsonResolver }) => { await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); const nav = page.locator("nav"); - if (type === "univention") { - await expect(nav).toMatchScreenshot(`${type}_nav_loading.png`); - // The stub silent html doesn't seem to work in Playwright so send the postMessage manually - await page.evaluate(() => { - window.postMessage({ - loggedIn: true, - }); - }); - } const trigger = page.getByLabel("Show menu"); await expect(trigger).toBeVisible(); @@ -124,21 +126,28 @@ test.describe("Banner", () => { await test.step("open menu", async () => { await trigger.click(); - const app1 = page.getByText("App 1"); - await expect(app1).toHaveAttribute("href", "https://example.com/app1"); - await app1.hover(); + const sidebar = page.getByRole("dialog"); + + if (type === "univention") { + await expect(sidebar).toMatchScreenshot(`${type}_menu_loading.png`); + await navigationJsonResolver.resolve(); + } + + const emailApp = page.getByText("E-Mail"); + await expect(emailApp).toHaveAttribute("href", "https://example.com/email"); + await emailApp.hover(); // Assert the sidebar looks as we expect - const sidebar = page.getByRole("dialog"); + await expect(axe).toHaveNoViolations(); await expect(sidebar).toMatchAriaSnapshot(); await expect(page).toMatchScreenshot(`${type}_menu.png`, { // We exclude this as we don't want to assert Element's styling, only our own css: ` - #matrixchat { - opacity: 0; - background: orchid; - } - `, + #matrixchat { + opacity: 0; + background: orchid; + } + `, }); }); @@ -152,4 +161,43 @@ test.describe("Banner", () => { }); }); } + + test.describe("univention config", () => { + test.use({ + config: { + "io.element.element-web-modules.banner": { + logo_url: "http://localhost:8080/opendesk/logomark.svg", + logo_link_url: "https://example.com/portal", + menu: { + type: "univention", + logo_url: "http://localhost:8080/opendesk/logofull.svg", + ics_url: "http://localhost:8080/ics/", + }, + }, + }, + }); + + test.beforeEach(async ({ context }) => { + await context.route("http://localhost:8080/ics/silent", async (route) => { + await route.fulfill({ status: 500 }); + }); + await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { + await route.fulfill({ status: 500 }); + }); + }); + + test("should render error", { tag: ["@screenshot"] }, async ({ page, axe }) => { + await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); + + const nav = page.locator("nav"); + + const trigger = page.getByLabel("Show menu"); + + await trigger.click(); + const sidebar = page.getByRole("dialog"); + await expect(sidebar.getByText("Failed to load")).toBeVisible(); + await expect(sidebar).toMatchScreenshot("univention_error.png"); + }); + }); }); diff --git a/modules/banner/element-web/tests/fixture/navigation.json b/modules/banner/element-web/tests/fixture/navigation.json index 7c05eca422..7365ff1371 100644 --- a/modules/banner/element-web/tests/fixture/navigation.json +++ b/modules/banner/element-web/tests/fixture/navigation.json @@ -36,7 +36,7 @@ "identifier": "app5", "display_name": "E-Mail", "icon_url": "http://localhost:8080/opendesk/app5.svg", - "link": "https://example.com/app5", + "link": "https://example.com/email", "target": "_blank" }, { diff --git a/modules/banner/element-web/tests/fixture/silent/index.html b/modules/banner/element-web/tests/fixture/silent/index.html index ae80633f60..84072225b1 100644 --- a/modules/banner/element-web/tests/fixture/silent/index.html +++ b/modules/banner/element-web/tests/fixture/silent/index.html @@ -5,9 +5,9 @@ Univention Silent Login From 1a2184d5c89eca0430efec640e446298fc8ff0a2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 May 2025 13:52:58 +0100 Subject: [PATCH 049/211] Iterate --- modules/banner/element-web/src/Menu.tsx | 6 +++++- .../banner/element-web/tests/banner.spec.ts | 17 +++++++++++------ .../banner.spec.ts/static-menu-linux.png | Bin 0 -> 14028 bytes .../banner.spec.ts/static-nav-hover-linux.png | Bin 0 -> 2281 bytes .../banner.spec.ts/static-nav-linux.png | Bin 0 -> 2323 bytes .../banner.spec.ts/univention-error-linux.png | Bin 0 -> 8861 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 0 -> 27234 bytes .../univention-menu-loading-linux.png | Bin 0 -> 6317 bytes .../univention-nav-hover-linux.png | Bin 0 -> 1111 bytes .../banner.spec.ts/univention-nav-linux.png | Bin 0 -> 1117 bytes 10 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index df6771a56d..7b32d53c56 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -155,6 +155,7 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { const [open, setOpen] = useState(false); let content: JSX.Element; + let logoUrl = fallbackLogoUrl; if (config instanceof Error) { content = {api.i18n.translate("univention_error")}; } else if (config) { @@ -165,6 +166,9 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { ))} ); + if (config.logo_url) { + logoUrl = config.logo_url; + } } else { content = ( @@ -205,7 +209,7 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { > - + ; +}>({ + navigationJsonResolver: async ({}, use) => { + await use(Promise.withResolvers()); + }, +}); + test.describe("Banner", () => { test.use({ displayName: "Timmy", - navigationJsonResolver: async ({}, use) => { - await use(Promise.withResolvers()); - }, page: async ({ context, page, moduleDir }, use) => { for (const path of ["logo.svg", "app1.png", "app2.png", "opendesk/"]) { await context.route(`/${path}*`, async (route) => { @@ -190,14 +196,13 @@ test.describe("Banner", () => { await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); - const nav = page.locator("nav"); - const trigger = page.getByLabel("Show menu"); await trigger.click(); const sidebar = page.getByRole("dialog"); await expect(sidebar.getByText("Failed to load")).toBeVisible(); await expect(sidebar).toMatchScreenshot("univention_error.png"); + await expect(axe).toHaveNoViolations(); }); }); }); diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2531c66bbd75e0a7f0760076db290015acb092 GIT binary patch literal 14028 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 bAHY~ftVJ=K#CuB8K? literal 0 HcmV?d00001 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa9b9b2553f216e529ca54293f387920c121075 GIT binary patch literal 2323 zcmbtVX;f257Jewu8TJtaXtQ)s!D+z&+Mof%#;_ehTDCxtRUjBZc0!0Gv>+;rEF!DH z2rW?z#E6h&P!k&oi-8Ox`!1lOL}Uqs1L(Ymf9Fj9==$+)ovQn8efPUx)xG}P83$#B zy$S#TC_6c#-2gxioC4Oc9pE<=>z)7_nRqt`TcEyMdkz3#T~6psBQX^Rq4&-U&&pal)6bae9wHFghXX8;(E@d|g=l*ftyy-8vO>t;3ey zyWT7dA>+%!Cl?@a03g+Y@wX?QKLI8dd(QTnONDt?g~bAW+82vm zT3QO(czIntt15?*yN$#_A8<7y5{YI&TWc#(JxvB$^E3vb?h zSX&!XF&hBwN`GJgc|DVpwASEQ6!ldJQdL2rkb05beDB^pCNui$=K9ps)V^={@bLQ& z9?UJj7umwYD!db7Z~>l};W&&DY$D|f8VE{klh35CvfXEltm`!m|RyNhvz zh4HPqUOI!pxPSj(!_X+mpqE$8X32;+2}NbGSW6~{4}S&|jB5gShW6~<9m4Y7T;2yM zC@p}sV;;^PG5#@tNA_c1^oxji_};_SRrIc_f*m#DBhEy>!Jp6WgI@i8hD2$Dbot>REH8tJ2 zbLXc4CX>O~ZHupvq&CP8_|4UD`gS(rX?Wt3mNdMbZYU<)!yY)9Ke12Nh z!9$q(H{nNd-M8n&;<3k}*-wjK%@&TN<>}dwmu8%}hd515=7xrJF4qUf3U1A!yWWwP z-4s3My$F0XB0jM`1_>Zg|GeDbp-On*9v4*|)cy>UGH0<^ERjfTuhD2Ufk4pS?ubC6 z(Ju=RoSvEN@9*buID3wSeUPLeUQV@KJs%t#QqkIzs$9t8Du@N{dXFN41Q)e>7ydEV62A1?*D z&dJH?@mGNkH&EAO0(qmopIk&U2E2^@88B5Ze7#)wWUNJK=k6it%A7k6M~_^SNc^5(BF+Sg z_C`B7IVC1awl>xrNXNmV6%-%{KYf!71Ynr{V-2`GtH$Vw=OxI1uYcA-nD}}*?2~!T zeGf6O7S>c$R16Lr=%QK9?01FyH&Lr>V*?ZFD~sY?9RpZhTm zyqVy05ogK;Uj!4AFK-^WTU;DXmVET2P$(%WDXFQ}dZ!@FG+Wubu@`y8X9q8##cZ;9vU=l}R)KLQ;S6B874 zcRmu2N5#JrGRL;1u~`7AFUS78r{}Fm(e2~Cy*x0tI$w6@*0Yv2o0OIC@Nh@+4M$i( zdHL|zSn9^AxUy0g@p6R6vA8j)Opp zjeRrJP}I-Oe}XT%^_72RnT2*1EiJ_nOG`^X>}d+?Dt|A!qpGCT&Xmn-L2kE9b?V!g zW0#ImDSE%_mZ`QGn3=r}YgiAdy)Ftg85Ac`Fxw4dy1PwgkO$+RKMBY~e zkT(6&MIRDSloDtKD5ZJ-jRUt+_^e*sAp_d5Up literal 0 HcmV?d00001 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..14f92293ba62f7e1e9cdcc1f91fff4c8a8e7dee1 GIT binary patch literal 8861 zcmeHNXH=8R)0?8oK`72mUJj?|t z1n1hgKOJ(VQ78fRjRr_x z`Qv12Nb2HSIq94n@mn;)e_U^!6W`{PwIxlJ<@F zCUaNy-N$P86csa>sc0E@+XNtQPA)=`v{YF2D{+5kQ2ohI^%p;t3hf)R=3FXzzJruk zfw9@yy79XBn+-krK4kql(HAO^RmA>*S`^}+>s;XVL z1l|AdYqO4rwtriAl8-ur5oty~NGvD({?D~r!o0ij?p>jW8Z{Ef{ z{xE*C`9~{^LS+qAt@kK+A?j=VOx93nS9mX^;a6prL0)63y$-D|rz(SN#lwbrJZ~5K zte#2tK$5Qx0oT=|u^Qo2II!Z#A9r>C!(-N6{ppxIh{ys9*; zTGwPEs`YuXS3dgO{3NNu!#VE1$HkO*pf3f@;z0RsFXt2|wT7Kskf~{|am&8GEhYwC z$7F@mFe2Q>rI7O z1oTG5!pNj`+(aeR%}p`*$f`} za(=R}6vTrbsIWqv9^+>!ZP0m|@u4S+k*lk#fWn^|8X9_gdml=3EBe32jh6%4+88S} zQBi4QHL+xBa5+21Uz`zlXX-0^SErHg88di29i8SiI3zCCjG;+CM|o;$>TOVRa`Lx= zf+ov~Fv=NrxA_orvcF8gaoxQ6>|fGKKE)}AoX&c7-)adY{59LVOH2hMIoZhA*xGwY zJ=G?ZH277uz0F*4{VheaIjYROZYzg!UKnK=xcQAZ=Vz0lA6WR(#H8N&d0fw^&v+6- z-5!%U70tG)NV506?#B@4>j*-R9CdRtgk=s@75*%AnSLJctLYt17V~o)`OphBU!oJ< zt7N8ppzBK$Z?~5qY)>7+Ar+ed_Wb=%`qCw->oqhr!GzHl!1DD5vX$IHGYdb$RaH~~ zle{C~CTLQoT7v4wTp8%-V6xoo*8X3Ayh@XX7%v#-4K>*JeA*l-^utdRwn{5o_uA;x z?3MoU$1XZrS#AE?CbHp_`(P!C(gy7sR+UlrM&hQKWPxswiW>02 z_fz4jWQ3&PVwC2Hq^xz@;)~CFk(3z@(*+czwv|v^n7uV!hs%JrayB>;w#M!!Z8olF zkA%g<#p!8lb4fW5g30@g7jQB3`2nzcP`Zvy2qC$^g+L%YfBt-Pa}yhSR$kx|il*S} zvDfJ6ENbjOvxUm$p)~Q#Apy&5)o#BxqZ+Mq)W!u0flwY96->Dx@_kB5%Ck8+%%Ldy?aXd(Zvz|Za05Z$Aio_;CQsDd>4D6= z|1tnH0PCw*3Yqxy=~MLSSf!51PthS4^zM=QanI5bSQAPx) zASPO-e|Uf{k;5`BjLzc37Ee=sPte^^-LNWx07P9~-N(nrZ$7}O6t@c)7C`3Wh(6dy zJ_}x86@;&2x0%iTI^Pn+$OS4ZE7QrDJKh?@0W9m<`kf4THhyOPkOV{w^_3meu1Bj3 zR3g317~sf=j{z8h6CrtVw1lv0Tr z^KyZTDjRgDg75aA(@c3mJ|8pJZ^01)Q9ige;+cMM-TR1$(_m$n34rSebktYr2YIz9 z&nc!~8sYGpA(;UB0@l)t@=E*qiI1*+ekel?r|;l)7nt9XeCK@X|51D-cGMw*?G;7o zr~y;6b8=3dpB@|>e8=glT~3-_JKn`&D{a6^v`j9clbT@)ScH@Q0?al9y-~Y8g%GF` zqGVvtHPp6!&j4Ey)TlM<0f$HesCtVlrMbCzc-SzhrxuD;PVG(3$)Wrbs(fi=)Sm;U zba6qs<-LnSQ-vxTa-rB(3#6M_qt~}7H%w2D++4K}b3sMH!xy22yt2%RPU{%<@UPV1 zGho>&Dh{tyG88yM!dx0WgQMywJ?vFeZhD213JPaUxC%N(6I3_@0|Uab|Cx?X?F{~O zz890F(A(P!kP%12GP_N1wfhXRFgx&>+NV#SYHDi8$Mt9{zd4_`R{#LM2bP?4e>4S% zlJ^7zXhLTjVTH=wEyvRkXyYnd2)lL;3;1}_PCG@ImXUFww-*H%LwyC9cc$8|`?qsA zWd~@eHF27!O*Y|?_twf$0RjNcI5!&`^6&NtAi{+LkvE!6MB--b+m*rD&kxZM zpMJyu24XkGyP{KYP9Y6rwS%jL$np*&3j!~=y#MvJwcgflA32A}K!mSGn*z0Ss#l9N-3 zy~)P*J1?mZw9M!~?qJcE0)GrN1b7=8LbO`226$6IbOgv`L@baSGU{fxK&bm1cCD&;_tl!|8>+(z9JcMh?^PGD}(`&=;D;n z+t;@}RyvU;ZYPS`t^wjkM|U@l&KZ6jZF6orRpS5<2S8X?!+$s;HV;ig_=NaMO)7Z+ zHvvWj9&LDbvYNO}*KPu|$0Kg5_J~?`vt9(yu2(yQjIafA8{i9f{i~@cjWL=tr1o>Ha5n_Y+PIriMkW6=+KMP zSYB}Gc$wL$--I&d{P5?$ge9Kj3^z76zsbtV>Tjp4Eu~vpR3z)YF(?*B>RpS>zZ}Oc zXZZa2(BR-8I*fu)O3Nt$}Kpt_>gE1zn^jB5SB9#Hg3I*Us{`2mSblJR?yLnHq%IdX5 zBp7&F28H4y7Mq7L-Ll~6Ujs*zLzPfkX`iGJesWWuz^#8wMaAh+;5zRLhv+ ze+ew`_3Is7-NF;absb}#KHJRQKi92kviERmPqHt^WU*xiY1q+FJhvH5<@gt7>3}5F zRX$cy=4z#diQPhFrJi^MVt?=ZrbK#M`0Q6kYv7;39@feMNbvu3`t#}y%uU8j?LHQ& zuYWuSo(Go<9$dQqjOxZSs(+ZN-ZTHV`M)~(FChN&yn$9>f?8H}XZXDQTUW#A%48Hw z>2pqDpo!<=&eF%fstfW96;3m8i<#=v0r^`ko+j!VQ|4rp>-KaBa>tosHV&w~J zJlH7Ad7W}kZR%>A1#xUsaYZJBHzvAR1Tk_|ZFn8CNK|hq2Dqf=YC-qaFRIedZq^30c({Uo8`}rE2onvoR z_fpp8vy|5*&yI*PdNI;I3m*qKC#J<8zMvCHhk*M1kfdOw;qIy1a>do^g65{+CnaOf zr)G*w?sAOU8{!X^hd7Hp_2oADSstq?||qxeO;T*(1Wdn|DApl64U zj&V9wnlpG+&DdV^=C7l7`3w;*vtz!vK^NY!+E!KF{gSC94FLwE#hE}1ha{}LwK-}O z6>Z$t$Ia=q%PV7hK0*@RrHx;mSCkg_!8lnb){tDp!*{JMR$2Eydssel(D|5$o>P|M zhixtOpD78n#?uOLE@O zZ20xVjCJj1KK_A1Ye(KsMy93UnRedvj1a%!S17yTf}4r(B|j(qn_1pE}9^g)pPg`MehGd7l{n{3s{hI+aC1hUS=5-aLj#w{6#{QC?N+D7xs z>FAj)(9EY14`~!w$dnlTqoU>;ge)sj63aBY6S)62$m>^!{5}4;J7<3G{aMv@lL%30 zZo5UZ|1k9cdw~09{hU+cuXjGB2s)wIW0|fF|8S0sDF12evRDDvp1Hf-b4H1Xrthpg z8KBd(71`(pP+vpXaFZ)8!J_+Hx)T3xg=uZe&zT@Q-{ER%#F^GyS@YYj0o z%u7jl4U&#p{!%!1y+}ZZm7Y`n#sH!)(yl|7vkMwsUp6p*U^WIff{k9ATzvPQ&Nbqm ze5BTs#jRq-U`Ik=9`}dhH9QG5?$qx|Z|>#SM?w_KO|T@djmF4%&hF-9oqZO zUKEbqnnuH5Y zNT*1+qrVfMBWF@lkZGzQu0FuUz*2hW z?MmTZy7r?+b}}zrp2U^UbxsH8)|N=8Q~!Q(HNv=jt#k?PCVC?HsF}mUehZev|#rAb%t6YY#wbzIE_6!TAu=KjoDrtSArBd3Mm3brC0@G#Lov7OGociflc6T zvc+#pY?VZUUb?^wLI~tZe+&Eb@UYDjIWnW4vImPGvJ69eeq9JtduN}?xHNz`b12YP z`g)>4NYIi`qq4rd9MV$SkZ-OAQckF?o*L7bsOL#-nB_a}NW2yu71gt)<6}CHohJKl z&8@1l{%)>5-R@-0fxoh9Y^^PtRT4e6x45FOKIG&wouv7zW2PnFQfdeoq?^? zj7Zmu=8b`Zt4vJ$I|CUBBviSmoxPX2t=GZEN^UCbFNH0BrXv<36l4dwJMhq@O4iUY zvZFJP*3f%TXUu8Oc9Kw5eBqhY zGqPSG0kuf~5fufj)e&rYVt=S$H zMHf#;;!ZQMG`5ba-ZLQKr~a@!|Q2{k>Fnw>;ZxKn9$LwZcAI~=$Gs3 zt=!8`6W)O_GhU|mEY1KKC_*TGkxFbKQwUYQW(0(gw-mp5Pv#s+AYqoL^!dSR*;9<_HNRTA)!V5MWuAK_r zaa%{x6in`i1=?A|`|BJ+c4`P$JS@6+ja$#bvsYhx+x-#SJ9w_bJVPSAn_JYXwKUx<8qk zqP!U9#lMxOdnWtDeevk4Z|P}Xzg@|EKet=G^~KLM7P3xl100X8D&0nESa7PtnM$-HoHoLVpf9nEU()3skR4 zlhHdSv#PdXwGmYE%erOCBUUfX`t^3ayDL(M)3yGXx25)F)_Wu?eT+yMoCu{+uLa5yhgt~6#a#LRzg;IQi`HjA&8Un*zV zsVP5PD-$e-`j!r5!xLY9hSGsjir52s3iwjfB4hOG{uN=9Y-UXVj?qU+kBN5TvU`GG zJ$ddv17XAc^z`GmP`{@L!dH~_$!bT3@DVj(JG_!kA&C>&!4}V&8*P_!yCv^ePTTO# z8r)H#b^6Ys;M0Q=vbM@3&e!Om{CkmXMztpvPa(q__J{^wcfkrKCcpizInm9;%tQ`; zRxT4apA43@@&FlP=Q?cM!+bxX-=fBKlg%Up;tMz<%P@YCFXg1KTfl+dg)E~&IICZ4 zwGkQ5u(oOyT<-+0iH6zbsw%#RmMM|z-SCX8zt_O*GJ#F+AAgeadOy(BJiGMAjpVe% zQ47l%nUn@xsV{VvP;SaH5@|Y?E}@7*gx-S0A}#W6aYhTOUo&Y`BK#|)MI zbPfp%8S272WS6UZRAVKlr!>{TJ1)BEGQ`$2N8BR|{QY8jkN(2wW$nT)(BdCV<0q5z zOCmO?+^T0S#ZA%1Wr7DE`F*1k^M4o1=n)W}Ni z`gsJjI(^nF&fN(1~F-4`-Afs44;F+Dny zvQB>EP)+u4{HBVlB%Cj*Oh)9Egsnl4crAeRll*0wG+PbbCMTnzT*d7V;l0AF^3soQVP?{IHxA2r?c#KK zS08;eXw+};wImUW(8Y6dH%G2ukaNsc>Bb4v{_^Er-n0UyWgN?k*}K7MQ$@$hnN zskRNwZM22#)Qkn7nlaU4>zG@tootKVuRZ7VI13`IdG}gw-s@$gF4{t*tuLg=+p=cYmNd=YlRj)>1Bm;1W{OWpQ6-j|6E8Q|-Tv!;R^sI>p~8VGa;^g{lrwnxh5jJwB;rKyfxZ}Zw*(X&6? zFRbMUfb&CNm9xl;XLf`r{B$c<*FJy#43C$3IZyX4eMMmChfGeDCy?y%Ki*zQkt#GB zUHtc;ukS>^mZJui;;hZxaETV!3Ww zGmNj5G-6lt^S`Z$I^6n2NePT*NC2?-= z7#}b31fKacL7N0&%JcK4PadoA)LwH5e&nF>FNDKEl%e%>nuDv}LzFh$>{rq+7cc^g z#f&bu1aO9d@5|XU)x%D!{69AUqa@N@j6CvQm9}hxU0*T*hCq3=z+tq4y|=x2&m|aL zE&a2JiqVN8U8uX z1_%;H87LBlP|v*Vlld_W%uHediNS}-nK6NC+UNNfl$aD7fx(tF2mnZ&CL!)>?|FXL z22&>iU{u}rTEH1Dk@Ijx+E=Rj=P#0mw5|mN%H8K_>2QZgJf;2qhP>`0Fo@u)zoo+j znY&tYSw)VtR8`>Y!#ifhXKks2;;%#IzTTlBAOCI1#T|6WoL=fZZ9MbPWhVcqmke9YVk=~yWF zW1#w&?oeLI=zP(pB$>Ya=S@@mWQ=HDZ^1YdkaTcxd1z>g4adDoeqaZ6Tj<~0bL`&l zb`wHM8A1Yo-^l3_SBj~9=`Qcc>fW|qO+WAE4F=!lXzH_P3@eNmr>k?|;uS*0OE|(7 zgN$AJGwDjwwRl1Wy?@5z zl&^WjH7Dw9pK6SI`p_`-;U&tVQ+{EwRz|q7>z4%o%Pw7e=ojO`rRu^YA!u2nKL(vH z-B^%c6P+s>|KcV+GGq971{zer9qVZuGbcM7%*Ue*4<6P^)Xk!mCCk<&( z^9mx5vppa_Y@Mj7c=*LfM9<&LyapNUfu3zjFm!s+wV{&qxPI}Qa(e{RaK_V*>e3r4 z>HasPH#axcg+omb@gbS{_nqwpLr`VhN?(!0>v1HVHVpnvJ7RQ)=@UiN07A3gdV1Hf zzodbS;Xs!S-kI#EDF3dt%+Eb=BK3!_AM}L^>0hd_^;o4@&s4RgGrZv=_g@CCM5Uho zRsd#@)z@p6OtU%-`|I@EhJ!}=o9qT$c zLjiwEk)j5Ob>oOP2>~nc`7?(J^~dow&l8kuYK=x>`j;bKi1}^y3*gfL++nj`Jffna zj~+Sw$#}ZEyBnC`hsTb`#>PJXa%*X2g}Bp7!z^l7Yyh(xNRwt3v3XS;91#(LMi6C$ zg@pwLE0h!XZIQLovq%#^%fU>Dl2Z3*j_FBz?Q<^l933u1!E7eM;4jRg%?pu|s(a87rm_x6+bQi&t>WuzgWyMQ zo2!Hm!0y|c zYS)Z(pS`XG6Titd7vOHb>4;=$4WaU0FUV{E!fes(h3ONex%D{TtSA2CC7Q^nsHo7; zS1>yxZS9dyY4O%Q(G2isc{6>qJqtjx`q^EI;4JeQd;qiNFK&3GIV z3A6Xdj_LwbZSp<;Khnr>f|cWW1<&oww}qczH{1b& zw%DpkV@Ck$Od|aKh1%WGv%XtXH4xs;!pC8X4*?#!ra0UVX zaTt^8bD7_IVZqG!Gea-gDU$Ya0_M@-KWcmyT;6qHbX}J^vVJVoP&wV8E zDxlQ{^$*1T+GlpClwA-llkoGGOPzO*0xrYeVwZj6Zu=GtWoLY}7^Ak`i0NNlT?P1* zsH;<=FYLVBYvOm*mJ;3o(7^w6fekk81jx2QFMXjM@SbX_1pa6x-CWQOkVM#wn*l)J zb7sgg1mSpfFzXTxYsBZzw9%hfz1{;fAc@6U9fs9A0)BEXMK zTLLbzdd$_Yq)`~^!Cls9WKXZX`dkQ53FUi`I=q(h$Sf?hkw`dUVqzN3Q=_4TG-JmM z6e4dQtrwMD)PGg|elSJcxp$ujJRnPK3_aZ+R)c(ck}xgFAtEv+)!EfW8(pB4x4gU@ z*4gNZdGPZ4L-XG6aXh+7wyNJBK9xTk_qV7NZnZg4QPzCC4%|O4+@|NO=Sog)?oHdo zeFHzYhLSLS+{u?W!~v>SPB-;=KEfkap2L0anX4pkHz3G`mKQ^F2)QYYv!;Kr1j1My=uz}|;H6PB0_aq3Q$4ZhzT>zDs|WM^js zI>2VSZqTrtTTpN}PS=>rtF_h^IaXr8Al9@LZxGf$;wpCg%Prxlinj^{!Ki?U2ue)L znoAPbl$7uM=LJ_qq4w{RR}R*O^8j&{blda*Brs1ceKtoq0odB-@b9O#TpoMigqMgH zFJ6S-Jo+pVz>&@GQjDc!mo=4kY|M-+i7>v!h## zm+6yonRU%t>3(yG$L{0`=Ch)loLt2Q7Z(@!TUwgkgI+J}dYnx;z_;bb$<0lh@SMX> zRx@xca91%H3_BPspA8AW*=AS%ikG8s@51R`kHGTJNsDwN7oxGVBr21dO~yub$*9V_ z?{IekVEqgr=7duWz|>Pro#Vpu2u4B2`QI&l{0Ti$tAJwEvq*)yvHhCN{!Evvl6;&_ zlzI~~o077*l9v1)hAeu2%i)QsOu47J6HD>Xt=BG(0c_M+f+au+3j>7h@uGv5yk__! zK-$7Om-~{#A%<1e*6pegjas`I{k&2JD_wf*iFmCFvdio|MyK=Y8dP;37&p zw1x{bi&>BHI)D1%q@<$K1YFc}d4Z46!3@Ul`9*zu_+Y(h(gyMbOgiypy3EYWM$s9U z7;#1K-T5|uz!U;pgZ#FtkpVOvplf@2dmrarLL*@Ue=6JFBuaa`l^Hhy)CPcj+%Utc zIK^qT`bQz$$P+jDnWIjWlMq7`QWdwmGOa{*itbse%BROy~RyTba&(w{oL z@d74(KQAz(_~Ir3Be^!yZ~$nFhhF=u(%v}Ig-VrheMHd+SPG*iFRX!mlUeD+Q(SGhRjj@lZ`>L@j80N5{TGf{qoSpl^1k~!58ps@Mj(Pg2J z?PnYPi3B;^NG< z{YG+Cn4Q1Yw*H-H_Vu2DyY?B5RJ`qwB^T_FXOaitywG7Z&D+af8DOxjVii^dV8<1j=`FnXl_! zJ$%4Ha^;~VUQ~OVVzKKZQeF4L|a@{iHWkwqzyxZ!ZzeQ!ug@DDbz-47+ z0UpkKbF@X8XM3^vg7)4CmGd2!f-n_Y0k~;33T;7U0U(g^{Bq%0pZ8_NVas6pxe58t zVKT3;@cf8$+7-^}J&~qO=&1p;3ZM!BdCDAT22gzeCqpCa`DaBPoiPCE_0b|-gdbj$ zSr70qK3=3ci)4VN0?wA1xw!}c@ap`~8Bb47K%@^mqyW6dE~FNGE_`;uW9ArZexwUk zE&vhnPiQr?A%ZXp9i@!394|bXCJEHY<;Ne&OrJz=;9etAO^a2^KuL6JD?m$2aq0uE}SJL0zh|ofdHKJ z@H!AgI`2PkOB(of_J89R!B@|lB2Xx7*iRJ+n!^s3tinp=90LSb&5M;YZ%q#8Mr29B zC&=Mq{u2;kYoPB_CQ<|oy7Qa+TEGf^rzlj)MNO7AvB9+7({Ycv zD#?X)2YYIE4$X`zT}h>p6>mcy(D%~BemoX(P_eno{_z6U(JcNmftws@pT6E%GKLhyDaxl$o;;zcVMOUkFrSHcC z-DTTJaZeaEE5aN7!@wRcC5saFnCMA{ZE;~gYHv22C*h<0Hr{FWpu@y~npG76cOTDJ zv^a52Dlu-b9iM&nWqn-4#P{7!AO0Bk9WyiZDLrLxJcLCTw+;pu5vQ&*({X_TIuz;H zM@EHTkKQ(#@;hAfp-mK9pAD*86)8!Qd9;p&^yq|#j^`l`)1{v-#Go0n4!h+nyg;#ykx%pLYe>5Q0ED5}T|!Za48-r{rxWSjN{PqAk-h>9{LF zHmp+=MNCRd6tY{K(KQ;w))Erj8=N|O%lslD!#Izg!d>iG_C?9ElYg_DIRJ4)(4{Rf z;_*8w#D2LoqSxexoW9Vrjo7$|8C}d4qZJg&9^WW3f$7AAa9NAkL`8+$jnx?!#Zv*N zv$CKz(e)p(t+&GTJsr$V)w3f5kX`v~jjXz&VCdM)rafVI)O7s0h z)tVlcI-U!q3y07}$E#2nh!(}&Q;{ob#! zi}IM|5GtzyqNQ2a^Xv0(o_6(IJ0jogQQ4ebu4&`9-=7 z>y+iGZ}Ue?x_+aRfA^%$1w@`1k_qzfIWkI~Xhtuc3=}mQ2C6 zheu7=j1{2$YrIi^FI~d3KTN3mP8c_q+HWSnU;E&&15wn?Tn@hTS_k)jL+mG;SAJoT zX-GoxBQKfFK>svnw-~gaVZGPZ7<1X$YvYK|k*6r;EQuQHquCBjvr%0pv4+9KZ`X6_ zh?N8=NI1c%-5ys3C=@TQ8u>*)Ye`8SWv!J-zg~98$gkNL;|a?zEaWUKEDQ_HB!2=f znN4J=E2^S{r@=3f*!mYPF-N@?f*ZsfLi_Gm*fQy6?;R`hplTSToc!?b1{ca2Kzy}Ywyr=PchH0~2cUd4{R#Xi zo@YMLV=`qys@xO40cyWD;u8p4C(h9(@_zVsS=<9u`2lZ#zEbU&%i^{)Q!v+Y3cR*Q zUpyTyA;S;eUu!y8JZD$7+f(j!*Pe;Ay@MC@#RV}@t>*_kFA=$4cuuoQ56oCgeUkfP zO4JPLMNh-(E?$Wz624=jEHClwg(U-` z93}X!Bb?&VNu$#f`Qt{X4CHTLu+u{WHmL>0Dh@XTG;+{yIW>~AxB;>QFg8EPDSgtPmFx7gD+5;(yF zSmhKaQh7~c?Lt|-k?yL+6-xA4w5OV z#mbkoNsJEo52T*x`bG8}+9nvtPuq-=ZL&o*H)YF@(V7BZfOlbv$YWe~{sKEp7&;iq zSGZIjZ|%uf#&7gsbH<|uGl0oDt_`n)8~yGS7%D16kr*4W{-eG3jB4lzzckXbYqCFc z4W_L13AvJ9^{|;IjYWSyDV{gkU`h6p|+AEk5YV#kh{ESUTNr`P%J*KU%RQVn8 zv`fiyA%mGiKg961&{9XSJ)s*Z)%=$kHtIDWzAsaDdv&z?$OU!SkjGXKwoUes3u)TW zWv5fxOo`&Vj)QmC6fP&9<$k>d$A$r6EO!{7LBW}TLIplq-?D~>7IGJ6hH0jR-jL+S zRgHYqR3mK?bbHw3f8uUm<^~lN7H)X$r2cW8Nzo_U>nHSAxZS3<#?B^4c2X9TRF5sp z;G-p(X91NLY9ymu?VLX^F4t4G=DV;L7EW#He44rWQjJ6h5T`*o5+kuGQprR5E93a> zG9T&T|B%OTdSwsK59`>%-3rlIyQvMWL6%TA@O7qWPZs`emRyeA7sP${Oj6ywx#~V zkNIT!igDfiNe&PG$3W-q{uz()i~gNRG8ykE{-A78z;(V&I0`XYV;*X2@YY&=yMu0+ z2INXCWV5qVU_4CE4?&scm_5cW$<8+McNco-n9$Tn%v?Eu=4O##2w3OH_YDX?vMOwL zFEWuIqUlaht@_yn5S!c!pcV1SWZal>7lWfxP;T4WHQgC3AzJB= zQ!w`2R#U-z?&J~Wpb`_{-ftqKKf?KgH^Nw2@x(UJA4u8yR2quP(?xf1U@4&s{@c5o zeSf^GUG6{g(&^|Lc=lcH@!8xRP%i1YUb0a zr%UUNM;jPGI8E5TyF3+A$ZsYi_2i+P{u$M;4F2U(2PI}x+}27l*F<>1dq?Pcu#@LT zsLYByqujQ(@+nrE{%tQr{f=IhBJH8T z!rJ7TQSy{*bDmpA2=0f_aJ-#+qJXXVqObc4oR%)f{`Pp{x0(Jjqv?MA5)7Y zbQ|F_ds&1@Wz8I$7R|*pNjtlC2wzIp@q^o)dsIFv>Y)g7fd0fR*u!eGvh0hQa|BL$ zbQTuk;k%}XSaGv4RsKBsEi2WLcD)#d=}x;iT~gqSwS$EkPE1E%FJkE&S}E>gb8_r0 zI`>UJ+g93OfB~26F5~8DNOaE9&wB;Qw$@Tuplu*bZ`GG4K9ir8A^uX>7!4oZ-eyhR z+VjVGZ(GCe;j0Tw)k5sIpK&c4taljLefM(cGme^^YLb4tjkxz*UK_$n%2EFo}SwQRdz>P3v@ zA4Ba{8O43|RQ1!M8TDqrH(BKw$d{t8S*98P>3toUaWh)6p^~TXWK)#sM93FO?cFzR z-8@p7#&^x+q&ECJ-ePvFb(X$>pVm^=O>GbdW5JDujma+-Pl;fZoFX28^)CD?eNp%k z0IZ}44~e9r#Rk*Lmg!X>Mw0!NngqDpb^7CFiR`-a?Y&)$LLBT4yTd4_O9#BXXD_jC z%KqF|_CuJ+->zK<6dyA7@F8|6ppNf1=<=-4+mZyx0M21UShE1zFYH4VTPX)8W-wL*T$zo1&~T?jls9Gx)&Q= zS1~B(O9v63D~rID3;DXgxxfb0o=UbqAhyY&yfZp~N(zkRaqkY@a*lvw!#fj-FaWpk z?Iktx?}mROf+r9-PbWjd`%hr7NpZgxy=9_}EvjXE?>LKP-Sf*9xY8|sMZMOTN1vUX zsCwdhvm4|?pznTGX4$jMXxmVfK&1^?EOx8;VDE;xi_ZNx`&ak3kyovd!|oI=dN^Sv zdx@z;vzS)IY`&3Fg|;ZS*ARL<|Eff*cqUmg4gVm^IORT_%)C9#aAn0`zBSXKPtDW=+@ zRE_gEzG{79dz}@(!=b=n%kNRAyNM(!iD>O+cC5SEZ+p0m4c;Y6-rCSc_&sbZzD{za zK!tp5{+W7JOJAcC>DU-pzWD`}7tlGLoO;^%|Eh4UN{X0v1+6w=4qwCxOzx za@R_ZV9m|Ci2+R-C25k>Uvs)Ud$B;G!E{JQB|6Y3iw;~vEa{v~{$YZYsl1IQpPzE& z7bnQy#eGY|Br{<kY{CR!x#K8gUCSl_S9oe2#d-kPlu$q1n-z4@z6c zFW{Sg)YU!x<&qR2{`t+jG=|r`5BAyF_y=fd0pM8ISWXNPzcXaGNhfB6T=oT(qVD0g z>P6c*#3AWn~`gYve!~a(YDw=DJA(OIVYUUmE*w-egPQrh;15zS)+- z9pgDl!-%WnDF+G5DX8Cq`QH0Mo=gM%J#`zuRyBMVP{t|JF6R1x;qKs%!mJUZbeH>y zYD%p^TH2sXq~lZE?y3f0lYYw_{2VnurjK6P-nTt!iq-oGlPSRB-97WzES8zU*BLa% z@Lhvb`widt3imf|=xs!&^0?JqbdB=c+^Zw3Jg-r$2vZ!-jcjLk!`CC7iT4G+#PEWD zO<0o7xXg2xy5()2F{>DVXSC*US?DRQNrM1NVay3r)CtsRmsYXX<1Gvg%~ULT&xbS% zRm?V;j9d1_B}d>Bn{7Wef#-41u3JW=tpe5sMv9&!L}}=J=PR}RCOlTaKefs2B{;Cf zCe4BGP`O~%xJ!tNGCrC5SuKSR6QM<;5)F^Y9g85VW*~y=Uk&TSO4F*u5mMPL@M#UE z_}w~5Kv7=7JlYzooFY28&d$taA-u)R4(T1S3sK{6=>7BmWYI@>p$%6@R@L|nh&$dh z!FeHfhdb82MRYxG?>UP;Ai!IFv9afjk|qGR(N1y`5A4**v%qh&%(-BMov*&IL9tvW z;Bay(Akax^X#dreHhQhcd1@5qZev>1dx

Bafjn=iv~J99P&c;*?>2M53AHSo1IX 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 literal 0 HcmV?d00001 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..32a60c7b42a1dee75f92b8bd8d05132b42f1d473 GIT binary patch literal 6317 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 literal 0 HcmV?d00001 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..6587c1b481c16ae016a1fbb42c4fa09f480a3c02 GIT binary patch literal 1117 zcmeAS@N?(olHy`uVBq!ia0y~yUKWNlP%36Nqe4sv&5Sa(k5B?AMCxTlL_ zNX4ADcYHHsLKzNxG~a#FOe)9xua~K>VAj_K9v3TKI`S@J$iUFi#K6YDpuoi>!NAa_4PN*w^aT-^Ys4VlkYG0xB=6_0VBpc zeM^tt`uJeig!liS^R_k`m<3jEd+yH#4B>{JwI2@Mso#J3_L-UY%&mp%ek?2c=w$UT zr7k1*(wXo)$yKt`^-BwKfbKLoJbPvD*;vl_x@{pVw|rasWtQjtBQDkq3=Y@cFx|b; zx%g@r&=~~}bKX`Iygk*&0W^Ht%vm`(Wy_wLSDg1*?#_30Tj#x9i&utT2AWpVr+T3` zboJMD)AU=LfB!dg++!lYj4wp|`E>g{8P40~5XW&o_@DKz=*O46Rh@gDZ>?EW4@@ST z(!^h!ld3oSe!=+KOlAg#l=D0V%SG4@J(2#|`TVLr&^`VexJ#_Lm=nKie?3+9w_sDT z&Gv`a^!hk~F4}d5Vf*5G-y5H^>|O@~opFJQy9Au{Bp)1GclYqmFCSm|roVpsa!;zs zO@o}v=|C;>CQtENy5{(Xq^_e$>i^zMT(4z6-(&Wk%Ihw5Ps$j1fMyiyH0I^pmOX2? zv~l*~XSc6_+@LRXpqDq_SFXZhe)g7^S8px+@ZfRn-&Va|V2m_Oi(tCDruApRyO7d# zHGfyXHdl?_rrLQk9w-%;D!e95^jgc?^4SK!yb-ZhL>B1GK!F25(#4Sh=wAU=kfjbH rECy7T@L1OWf67;2=3`j!_#ex&ZD+q~+TXJWav3~b{an^LB{Ts5<2=Lu literal 0 HcmV?d00001 From bf6cd61c7911e9ad09cd8348426dcc7cf5601b3c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 May 2025 14:05:51 +0100 Subject: [PATCH 050/211] Add missing files --- ...atic-config-should-render-1-linux.aria.yml | 6 ++++ ...atic-config-should-render-2-linux.aria.yml | 13 +++++++++ ...tion-config-should-render-1-linux.aria.yml | 6 ++++ ...tion-config-should-render-2-linux.aria.yml | 28 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml create mode 100644 modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml new file mode 100644 index 0000000000..602fa03a65 --- /dev/null +++ b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml @@ -0,0 +1,6 @@ +- navigation: + - button "Show menu": + - img + - link "Show portal": + - /url: https://example.com/portal + - img "Portal logo" diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml new file mode 100644 index 0000000000..3ce946d830 --- /dev/null +++ b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml @@ -0,0 +1,13 @@ +- dialog "Portal logo Close menu": + - heading "Portal logo Close menu" [level=2]: + - img "Portal logo" + - button "Close menu": + - img + - heading "Applications" [level=2] + - link "E-Mail": + - /url: https://example.com/email + - link "Riot": + - /url: https://riot.im/app + - heading "Links" [level=2] + - link "Link": + - /url: https://example.com/link1 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml new file mode 100644 index 0000000000..602fa03a65 --- /dev/null +++ b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml @@ -0,0 +1,6 @@ +- navigation: + - button "Show menu": + - img + - link "Show portal": + - /url: https://example.com/portal + - img "Portal logo" diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml new file mode 100644 index 0000000000..47446acc17 --- /dev/null +++ b/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml @@ -0,0 +1,28 @@ +- dialog "Portal logo Close menu": + - heading "Portal logo Close menu" [level=2]: + - img "Portal logo" + - button "Close menu": + - img + - heading "Anwendungen" [level=2] + - link "Portal": + - /url: https://example.com/app1 + - link "Aufgaben": + - /url: https://example.com/app2 + - link "Chat": + - /url: https://example.com/app3 + - link "Dateien": + - /url: https://example.com/ap4 + - link "E-Mail": + - /url: https://example.com/email + - link "Kelender": + - /url: https://example.com/app6 + - link "Kontakte": + - /url: https://example.com/app7 + - link "Notizen": + - /url: https://example.com/app8 + - link "Projekte": + - /url: https://example.com/app9 + - link "Videokonferenz": + - /url: https://example.com/app10 + - link "Wiki": + - /url: https://example.com/app11 From 363d0a76545e0dd55ec889f33dddb2ea07051d87 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 May 2025 17:03:02 +0100 Subject: [PATCH 051/211] Stash --- modules/banner/element-web/src/Banner.tsx | 28 +++---- modules/banner/element-web/src/Logo.tsx | 11 ++- modules/banner/element-web/src/Menu.tsx | 65 +++++++++------- .../src/Univention/SilentLogin.tsx | 3 +- modules/banner/element-web/src/index.tsx | 4 +- modules/banner/element-web/src/theme.ts | 78 +++++-------------- .../banner/element-web/tests/banner.spec.ts | 3 +- 7 files changed, 81 insertions(+), 111 deletions(-) diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 2d957f9b6e..310f584312 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -8,26 +8,25 @@ Please see LICENSE files in the repository root for full details. import { type FC } from "react"; import styled, { ThemeProvider } from "styled-components"; import { type Api } from "@element-hq/element-web-module-api"; +import { Heading } from "@vector-im/compound-web"; import { type ModuleConfig } from "./config"; import UniventionMenu from "./Univention/Menu"; import Menu from "./Menu"; -import { theme } from "./theme"; import Logo from "./Logo.tsx"; +import { theme } from "./theme.ts"; const Root = styled.nav` - background-color: ${({ theme }): string => theme.compound.color.bgCanvasDefault}; - border-bottom: ${({ theme }): string => theme.navbar.border}; - height: ${({ theme }): string => theme.navbar.height}; - display: grid; - grid-template-columns: ${({ theme }): string => `${theme.navbar.triggerWidth} auto`}; - gap: 24px; + height: ${({ theme }): string => theme.bannerHeight}; + background-color: ${({ theme }): string => theme.bannerBackgroundColor}; + border-bottom: "1px solid var(--cpd-color-bg-subtle-primary)"; + display: flex; + gap: var(--cpd-space-3x); `; -const Main = styled.div` +const LogoContainer = styled.div` display: flex; - align-items: center; - grid-column: 2; + padding: var(--cpd-space-3x) 0; `; interface Props { @@ -54,9 +53,12 @@ const Banner: FC = ({ api, logoUrl, href, menu }) => { {menuJsx} -

- -
+ + + + + {api.config.get("brand")} + ); diff --git a/modules/banner/element-web/src/Logo.tsx b/modules/banner/element-web/src/Logo.tsx index d515cfdb4e..485f5afbbf 100644 --- a/modules/banner/element-web/src/Logo.tsx +++ b/modules/banner/element-web/src/Logo.tsx @@ -13,19 +13,22 @@ const Anchor = styled.a` display: flex; `; -const Image = styled.img` - height: ${({ theme }): string => theme.navbar.logoHeight}; +const Image = styled.img<{ + height?: string; +}>` align-self: center; + height: ${({ height }): string => height ?? "32px"}; `; interface Props { api: Api; src: string; + height?: string; href?: string; } -const Logo: FC = ({ api, src, href }) => { - const img = {api.i18n.translate("logo_alt")}; +const Logo: FC = ({ api, src, href, height }) => { + const img = {api.i18n.translate("logo_alt")}; if (!href) return img; diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 7b32d53c56..e77dfaf67b 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -8,47 +8,47 @@ Please see LICENSE files in the repository root for full details. import { type FC, type JSX, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import * as Dialog from "@radix-ui/react-dialog"; -import styled from "styled-components"; +import styled, { useTheme } from "styled-components"; import { InlineSpinner } from "@vector-im/compound-web"; import { StaticConfig } from "./config"; -import { theme } from "./theme"; import type { Api } from "@element-hq/element-web-module-api"; import Logo from "./Logo.tsx"; const Sidebar = styled(motion.div)` - padding: 16px 12px 0; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + padding: 0 var(--cpd-space-3x) var(--cpd-space-4x); + box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1); overflow: auto; position: fixed; left: 0; top: 0; height: 100%; - width: ${({ theme }): string => theme.menu.width}; - background: white; - border-top-right-radius: 16px; - border-bottom-right-radius: 16px; + width: ${({ theme }): string => theme.menuWidth}; + background: ${({ theme }): string => theme.menuBackgroundColor}; + border-radius: 0 16px 16px 0; `; const SidebarHeading = styled.div` display: flex; justify-content: space-between; align-items: center; - margin-bottom: 20px; + margin-bottom: 20px; // TODO check `; -const Launcher = styled.button` +const Trigger = styled.button` align-items: center; border: none; - color: ${({ theme }): string => theme.compound.color.textPrimary}; cursor: pointer; display: flex; + background-color: ${({ theme }): string => theme.triggerBackgroundColor}; + color: ${({ theme }): string => theme.triggerColor}; + width: ${({ theme }): string => theme.triggerWidth}; &:hover, &:focus, &[data-expanded="true"] { - background-color: ${({ theme }): string => theme.color.accent}; - color: #ffffff; + background-color: ${({ theme }): string => theme.triggerBackgroundColorHover}; + color: ${({ theme }): string => theme.triggerColorHover}; } svg { @@ -71,39 +71,43 @@ const CloseButton = styled.button` &:hover, &:focus { - background-color: rgba(238, 239, 242, 1); + background-color: ${({ theme }): string => theme.menuButtonBackgroundColorHover}; } `; const CategoryHeading = styled.h2` font-weight: 700; font-size: 12px; - color: #203257; + color: #203257; // TODO font margin-top: 16px; margin-bottom: 8px; `; const LinkButton = styled.a` - font-size: 14px; - color: #000000; + font-size: 14px; // TODO font + color: var(--cpd-color-text-action-primary); font-weight: 500; display: flex; border-radius: 8px; padding: 8px; align-items: center; + &:link { + color: var(--cpd-color-text-action-primary); // TODO + } + &:hover { - background-color: #eeeff2; + background-color: #eeeff2; // TODO } `; const LinkLogo = styled.img` height: 24px; width: 24px; - border-radius: 3px; - border: 1px solid #eeeff2; + border-radius: 3px; // TODO + border: 1px solid #eeeff2; // TODO margin-right: 8px; - background-color: #ffffff; + background-color: ${({ theme }): string => theme.menuBackgroundColor}; `; const CentredContainer = styled.div` @@ -134,8 +138,6 @@ interface Props { fallbackLogoUrl: string; } -const WIDTH = parseInt(theme.menu.width.slice(0, -2), 10); - const Category: FC<{ data: StaticConfig["categories"][number]; }> = ({ data }) => { @@ -152,6 +154,8 @@ const Category: FC<{ }; const Menu: FC = ({ api, config, fallbackLogoUrl }) => { + const theme = useTheme(); + const width = parseInt(theme.menuWidth.slice(0, -2), 10); const [open, setOpen] = useState(false); let content: JSX.Element; @@ -180,11 +184,14 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { return ( - - - + + + - + @@ -201,9 +208,9 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { diff --git a/modules/banner/element-web/src/Univention/SilentLogin.tsx b/modules/banner/element-web/src/Univention/SilentLogin.tsx index 06814333c2..46e0f39eb0 100644 --- a/modules/banner/element-web/src/Univention/SilentLogin.tsx +++ b/modules/banner/element-web/src/Univention/SilentLogin.tsx @@ -34,8 +34,7 @@ const SilentLogin: FC = ({ onLoggedIn, icsUrl }) => { }; }, [onLoggedIn, url.origin]); - // TODO title? - return ; + return ; }; export default SilentLogin; diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index cc1e1e5123..54dc3a0392 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -11,7 +11,7 @@ import { ModuleConfig, CONFIG_KEY } from "./config"; import Banner from "./Banner"; import { name as ModuleName } from "../package.json"; -class TopBarModule implements Module { +class BannerModule implements Module { public static readonly moduleApiVersion = "^1.0.0"; private config?: ModuleConfig; @@ -43,4 +43,4 @@ class TopBarModule implements Module { } } -export default TopBarModule satisfies ModuleFactory; +export default BannerModule satisfies ModuleFactory; diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index 300cf1924a..f6a6c43f30 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -5,68 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { DefaultTheme } from "styled-components"; +const theme = { + textColor: "var(--cpd-color-text-primary)", + bannerBackgroundColor: "var(--cpd-color-bg-canvas-default)", + bannerHeight: "60px", + triggerWidth: "68px", + triggerBackgroundColor: "var(--cpd-color-bg-subtle-primary)", + // triggerBackgroundColorHover: "var(--cpd-color-bg-accent-hovered)", + triggerBackgroundColorHover: "var(--cpd-color-text-action-accent)", + triggerColor: "var(--cpd-color-text-primary)", + triggerColorHover: "var(--cpd-color-bg-canvas-default)", + menuWidth: "320px", + menuBackgroundColor: "var(--cpd-color-bg-canvas-default)", + menuButtonBackgroundColorHover: "var(--cpd-color-bg-subtle-primary)", +}; -const bgCanvasDefault = "var(--cpd-color-bg-canvas-default)"; -const textActionAccent = "var(--cpd-color-text-action-accent)"; -const textPrimary = "var(--cpd-color-text-primary)"; -const iconOnSolidPrimary = "var(--cpd-color-icon-on-solid-primary)"; +type Theme = typeof theme; -const bodyMdSemibold = "var(--cpd-font-body-md-semibold)"; +export { theme }; declare module "styled-components" { - export interface DefaultTheme { - compound: { - color: { - bgCanvasDefault: string; - textActionAccent: string; - textPrimary: string; - iconOnSolidPrimary: string; - }; - font: { - bodyMdSemibold: string; - }; - }; - color: { - accent: string; - }; - navbar: { - border: string; - boxShadow: string; - height: string; - triggerWidth: string; - logoHeight: string; - }; - menu: { - width: string; - }; - } + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + export interface DefaultTheme extends Theme {} } - -export const theme: DefaultTheme = { - compound: { - color: { - bgCanvasDefault, - textActionAccent, - textPrimary, - iconOnSolidPrimary, - }, - font: { - bodyMdSemibold, - }, - }, - color: { - accent: "#571EFA", // primary/700 TODO - }, - navbar: { - border: "1px solid #D3D7DE", // TODO - boxShadow: "4px 4px 12px 0 rgba(118, 131, 156, 0.6)", - height: "60px", - triggerWidth: "68px", - logoHeight: "34px", - }, - menu: { - // TODO - width: "235px", - }, -}; diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index 4857e31865..ae9566ec5d 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -136,8 +136,9 @@ test.describe("Banner", () => { if (type === "univention") { await expect(sidebar).toMatchScreenshot(`${type}_menu_loading.png`); - await navigationJsonResolver.resolve(); + navigationJsonResolver.resolve(); } + await page.pause(); const emailApp = page.getByText("E-Mail"); await expect(emailApp).toHaveAttribute("href", "https://example.com/email"); From fc1b07773eb688cd3dc2f77008d4193645c3fb95 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 May 2025 17:24:32 +0100 Subject: [PATCH 052/211] Update screenshots --- .../banner.spec.ts/static-menu-linux.png | Bin 14028 -> 13085 bytes .../banner.spec.ts/static-nav-hover-linux.png | Bin 2281 -> 3445 bytes .../banner.spec.ts/static-nav-linux.png | Bin 2323 -> 3451 bytes .../banner.spec.ts/univention-error-linux.png | Bin 8861 -> 8861 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 27234 -> 26330 bytes .../univention-menu-loading-linux.png | Bin 6317 -> 6278 bytes .../univention-nav-hover-linux.png | Bin 1111 -> 2719 bytes .../banner.spec.ts/univention-nav-linux.png | Bin 1117 -> 2706 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png index ae2531c66bbd75e0a7f0760076db290015acb092..8752e393a547dceb24b9f2054c777035f9afcdf3 100644 GIT binary patch literal 13085 zcmeHuXH-+`x^Bd!vOpjfKv8620TqFzARVHh3l#ws0g)1rPCz;V5)xDpm1P3~0Yhx` zDlPONA_xdUdJiqMKmvgfLP&Bx+;`t|?!9}Ay~iDQoc-gjKO`e_&TqcoJKyp=&pUa1 z&BRbdNJ3>a z{4ny>P0HBX7}6yO88j<+`2O2R+kSbvZSSL7_qU^r-(TP3c(YzY)uXG=QC&h+;=JX&wkKrf+%;#WQ}FmP4Q`WF7uQK_26r^;I(Z<$vF?Qcy`mf1-I19Njpb!3)) zr-wZRa_306tOOffla*DW7TYwvtk6{}un!V`_5tK!S(nS1xH$7bk)an`>!kk2k28~g z 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 Kc=;vQ$NvF?245n5ntp zcG7aAaNmXi)6yJMQ&R&}Y!Tc*5Q9%t_}=~UdXk}E@CPwDyNpopv){kE?B`?(R>C8edMbb9c`Vwwt$KrorieM~@O+#39< ztsq>`jnjgl-&bk+73SO;;Qf(`d94jMWh258oJ9l3{Qta_#!{cXh*> z#xqPAjUTb?hW1gUMTOXHQPysV-MwL>pD_eA3sNDwx)1@|9^U-q$rId~W&r+=UTdAP zl<H&cy+!roexMLdD_puwf5#5jCudkJ*3d=bX-qfAIMz1LA!fMJHUDYdne0c7|b8c=C zgIGW{K|+MjXT_)&L|HzKJ%8# z<(-|KrlzI{I2@$PPIJzc0Bf>ztP3s3EhylSCj0*Sw#GX%d$JCGx~YTQibNvy_3sIj zZ)kQnm6n&E+`R#@1+~^Fgj>BwHJ=|Xv7yX;7Ix(%CMJUaa+c;g@7%fblKQxRa&&Yw zH8pjjy0&)yU734th?SL<8yh|YjjAd7-Me@BJRUN3zL&@2fsgwNVl4D4aqai#=H~h-Kvi+Xm(~?f z_kkrt~m!C?m(9M!#*%)*@*$$DeV-wFlls{FAz`{ z#xsY8hVVkb;#MCqF4FH@0BYp9jYin!E_S!iqWohmBepGv!7EEjgs%gQJdkM0(Bo}+t= z03qNb-M{wz-;mv2;}ufFddm{D<~;RX<1~Z$i$us$@Pb8eveVMj>!OvCev?nBr%>CX zUxc&1cnGV@%F2MjR8~rB!*qc5P*ujHmb&`-fWBFAgFxN7-Z5rL)?UF@O(mg6$$z>7 zSV<9|i9Pnpj(r7JP}JowLrL4w`4TZOi|#$2o@?7hVP`Kyb%*xmJaB?p;=5yHueMn7 z6Os)b9ij-r{)u6sW&hw{#)6D^?%aTB#d8ZgyO@c$kMa)vHX5rO!Q1Ws>O?fXu{D_# z&)$x-eSAK@C=!?n!uHDX0 zG`c9coKEK~2y<%4n*b}EgC&Qtjnfqz2Q<2Y!=Vxg1a9?Jxu^$Q(trB&X=7}M?e(d% zzP`S&M~@#ruB)?(CyX`UT%q%w*~LbJm{(VFoe6|iTwEM@kTS&Z;c~fHJK4+N3YKD7 zDNug-^30E;mVRjUy|WU6UOw3Q(+A<3Y`KCT?nnM`+bG_Ux+DW6hJ=JXAG?K!6!X%z z0r;D(@+rdXssfS~@4R1ZT`0f4ly$W;16;RtfsCi7rV6`-LSadM)YJ?T2xKh01I*(Y z504CBzKT~{{BkEN7!07zQZFr_ zM@a4fUPMI1Pd5}ZTsK@7&8j`wQ1da-9GH|7%q@gH%U=9BZq_+PG< z5?sDYcu_ft~h~vs+qPBoavn0~~MuEjFw4K_@O$>iq4<`k(Xb|0kNmSSZt_|zMY#_`h)E95$GQ#MzWp?cHRsq bAHY~ftVJ=K#CuB8K? diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png index dfa9b9b2553f216e529ca54293f387920c121075..cb3af0dfb7054612215c26c766bb2ce123b051f5 100644 GIT binary patch literal 3451 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 bzpwBE(o4xeCK!NI`2kp%@M`}WJoY~TkK})w literal 2323 zcmbtVX;f257Jewu8TJtaXtQ)s!D+z&+Mof%#;_ehTDCxtRUjBZc0!0Gv>+;rEF!DH z2rW?z#E6h&P!k&oi-8Ox`!1lOL}Uqs1L(Ymf9Fj9==$+)ovQn8efPUx)xG}P83$#B zy$S#TC_6c#-2gxioC4Oc9pE<=>z)7_nRqt`TcEyMdkz3#T~6psBQX^Rq4&-U&&pal)6bae9wHFghXX8;(E@d|g=l*ftyy-8vO>t;3ey zyWT7dA>+%!Cl?@a03g+Y@wX?QKLI8dd(QTnONDt?g~bAW+82vm zT3QO(czIntt15?*yN$#_A8<7y5{YI&TWc#(JxvB$^E3vb?h zSX&!XF&hBwN`GJgc|DVpwASEQ6!ldJQdL2rkb05beDB^pCNui$=K9ps)V^={@bLQ& z9?UJj7umwYD!db7Z~>l};W&&DY$D|f8VE{klh35CvfXEltm`!m|RyNhvz zh4HPqUOI!pxPSj(!_X+mpqE$8X32;+2}NbGSW6~{4}S&|jB5gShW6~<9m4Y7T;2yM zC@p}sV;;^PG5#@tNA_c1^oxji_};_SRrIc_f*m#DBhEy>!Jp6WgI@i8hD2$Dbot>REH8tJ2 zbLXc4CX>O~ZHupvq&CP8_|4UD`gS(rX?Wt3mNdMbZYU<)!yY)9Ke12Nh z!9$q(H{nNd-M8n&;<3k}*-wjK%@&TN<>}dwmu8%}hd515=7xrJF4qUf3U1A!yWWwP z-4s3My$F0XB0jM`1_>Zg|GeDbp-On*9v4*|)cy>UGH0<^ERjfTuhD2Ufk4pS?ubC6 z(Ju=RoSvEN@9*buID3wSeUPLeUQV@KJs%t#QqkIzs$9t8Du@N{dXFN41Q)e>7ydEV62A1?*D z&dJH?@mGNkH&EAO0(qmopIk&U2E2^@88B5Ze7#)wWUNJK=k6it%A7k6M~_^SNc^5(BF+Sg z_C`B7IVC1awl>xrNXNmV6%-%{KYf!71Ynr{V-2`GtH$Vw=OxI1uYcA-nD}}*?2~!T zeGf6O7S>c$R16Lr=%QK9?01FyH&Lr>V*?ZFD~sY?9RpZhTm zyqVy05ogK;Uj!4AFK-^WTU;DXmVET2P$(%WDXFQ}dZ!@FG+Wubu@`y8X9q8##cZ;9vU=l}R)KLQ;S6B874 zcRmu2N5#JrGRL;1u~`7AFUS78r{}Fm(e2~Cy*x0tI$w6@*0Yv2o0OIC@Nh@+4M$i( zdHL|zSn9^AxUy0g@p6R6vA8j)Opp zjeRrJP}I-Oe}XT%^_72RnT2*1EiJ_nOG`^X>}d+?Dt|A!qpGCT&Xmn-L2kE9b?V!g zW0#ImDSE%_mZ`QGn3=r}YgiAdy)Ftg85Ac`Fxw4dy1PwgkO$+RKMBY~e zkT(6&MIRDSloDtKD5ZJ-jRUt+_^e*sAp_d5Up diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png index 14f92293ba62f7e1e9cdcc1f91fff4c8a8e7dee1..48f4242c8596de74960859779108a0506b33aeb7 100644 GIT binary patch literal 8861 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 literal 8861 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 ax}xfpNm}ITjO9?RpKK5PSYZA0<^KW==@Ga9 literal 27234 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

Bafjn=iv~J99P&c;*?>2M53AHSo1IX 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 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png index 32a60c7b42a1dee75f92b8bd8d05132b42f1d473..c2b44b9f6bb62ed54fd00d85d2c567bba2c8ba0b 100644 GIT binary patch literal 6278 zcmeHM`Cn4oyQcCSEi&?}Wu?Y3E6pM6s#!UuWgaUWvO-g#lckmcnezlTm`!SFX3km1 z)J(|<2NX_OnX~4M1CmpSf`ZHfcXjVSaXvp61;lO9&U zS>{|k;4}UDz@_+Y_cERK>?zbZqxNM!z9@W7L~}poMKKRcq-_exPi}Qg9gAeXew%oA zxE+ZtTL>dLA(kJ+?|XDY+j-fc_(8DxQ^uhK8DVly^XzZ!25-zd8|efa-unO>p5KbN z183817PRG5;o11X!gGf3kwS??;%iZ3VPP@DPjFx$G&eXv%1|Ze-j$SZ7pCX43oa2m%_nK^88|ZO8h(*HCu_qxI-?%uLK*wr+eqQUA+YPqv8hG{P ziaPXM*NoEhqO}eXjC+OWLM0tfHZ^&E3JlMY!@qRp9?euc<~>w!+);d@VDquj^9C9M z)>!Kk2(3Y|II4LMLaTEqaO*8_0Ix0$q@%#aXzS?uHPvAW4)(tW8mql()w{{puh(LU zv?MD`O#9+Vqj`h7>6c~$4V{m zF}m@yNDuE?{L~9tL&M&&07q$fQ*51+@(DbQbfXm8;m=|BU)D>XH{%TPc?1viJ9^Vw zw{JzZNbMt};R>E~S(U*VF3+L~adQ?mNwlcGeB;=wUd;QCS*SQUB7C&W>8qkz=B~dr zOJMbhUV|LE5v%AL(R`dBhtG?Xvn_&E-W{4wO$#C+oA^Sb*-GzG*!B_9#Shm9-p#aU z=pPXb`;o&~gJaDFSH0?^o23m3n6NCi1(PKz{1~SwEiYIL7e_MG^`Hrt=UJ5t55I@F zrrY6k?ecwMR$8|tY)!kvDkrn^V%Q@ghzd__+gMRu@YE)&g%5khTO-E9hVHFbPqkkD ztlpv%;ghzYp5>;Vc!`0F$loFo6+snKnuHo&4E_S41;$E!kKfmAVyuJrIlb>A)U@#c zZlba2f-U!(9?~~#AVCu)dIOgg$4P}VXEs!8BVq?$MUS!*Zr>H!cDdRSJ$r+N+$!kB zAHHW=v_m=`sOg=2vMpG|NX(%3E`Qa+S@Sx~vSWOnW@AOm&mU%R>iye3w=3qlu{Yky zI6*2TT~g{NZ#3z^dy)YTaf z*M~WO?^$2i}(@U{dtn!pqcdWF!;<*Dz zG$vAV=QpT?&l(~}aNZ;zedlSM3>MM%7BQ2UY%BSBt?zATo=wcs` z5&U>;O6hb{hV6_M&yd%}3dbWdG;1(b=rS}_&MeADQDVg*-8K;;S2HZEg_!|)^;5}Kc!iX1da*hXfTW*u z^AnQ7P3In0HGACjc3bm(RU^~qA!Z;Op6NO5_iYIM{6I}z-^H5szUmNe!a9mOU0U8? z*!pOT(OyK>iiAakfwlek{4gLtF8uuYfh5ZbQ4=Q)M^plq?$9jgqV{bu5nt$-iX1{+L^C}@-jtx zXENMMKy+|3Oy6HWoyzPhBFviQ?;|qD>UkZRW{HJ7C~j|S zYszU)3d{4N)%OLwjD?*N%seKG|+|2s`G9G9$=WvvHJqk0KXm zLqkK#auUuMU;}`_N$h*Cf=x9rZInw`&Tr3To<$-frk^?4uJjyF+ly$B2}XZ0hU=Kv z(9keYip;RIm96xdQdhZNRsOkAvzdU^2HmH22v)zJHdTe&uGS2pSQAas#wcF0om>>5 z@F-fG?sWZU#LeKz4vXvEs)j~JDOT*dmYp#RPt3X^<>EQOfv%^PiwLJo3nkg{W&TR4YD^G9$vMe52aim4Xg=Jo zOaL2NdA0ri#_hK{4z?Jh8b$ZDurDow2G{H79Ee`|`3p86FfsQhS!hLcSXfwhp3PiG zX2?pf6Lkn~hUo>;)eY2O4_%A}QsY`qeH&DZRai4BPg)b*vkoh%>O(ExcikVr{g+)J z@)?v!mvVNsQ!^C;mu#?u0eMkxWB=eUj8rksc;B^+le|Y$ZChWMct;D_O+#;xKEaMe zR8w2KOe&=F3O4B+HE+C3DBb=M=s_EF-R~QRYl4vC0eN1<&(T^Oof6kBWqvPg2T$L+ zj<{)X;(#rvJGtID+C2viF z)QXO%#IOe8Xv-#$E{L$KR}YUb3G|_4_eETuSxp3q2H%c?Bu;E_!S-etEan}{Uv>N zNz`ewB2Kt|!Dz3miN*#*Wa4S{#K{#N4MUggbwU=n{Uih?>+s>V|5+#jR7T6W8AOU? z-ow?VhA|`u7k&I-(Si6Z{p}>wfY= z<$X;-#6;9wI?&FKziFD|m*}grmhDxkK;%ulN$(rEfG&$eM0D!%R0}$M1fXFeWk*o0 zK*|A~N8kMTo7SNlhXICrU2;UUAR;2q`bcYAx{>~wmj^C?ejt}gi9p3=625B2mQ=v= z*}i=F3J}OaBAW2yq5gi)50@tYxt#>p#SDVjuMjQh`Jq?U+0=)}56q4UBL&M~P8;Um zX)dU%p!yo>l%LTR46(-PO;aK=6#w?)Am=5jv^RcGs1h4&aMP#p| zCL*^%+O`PrneE8rP2PpM%is*|eNb=C$*N#>GKnj8`G`oCF z*70_Cub8{`pwGo>ks@Vl0la@%vdJ*(Dl~TKVq0fboKyg0oM|!S174iNt!b8Ws05(u zG1w3pf2cd>2rt=+Z4kkjh~em%`JWLNT&@r;HN(&`i#5EtZhG~#KH!C0_xlWQeg@rN zcL_LY4EtS-kp0UQe!jZfHYXNFe{na^*kmi3RCl#r2DyOo^fKPcp(^A606ws7WBRFc zUS47uhd}uDRClIb8%#_EnhMC5dqp7=+WmN-M_EcReNKdKgxkgfJ5en%1OZh8TrbI2 z#WO>iAbDf#2-Qnn0wxj0X=)M_&P|A!&31k4VKr%JF1O8y*eGR_GmO0|O|5RfDA}{h zXV~iGH%ZLrVTsYgpnU|iQe#QnhL2CxX<_3E9n$uticg8K@uAHdwGV$_T-X(O^%0Ix zvAAwTOracf_Qjy^l63#k+D#HXhQ_Eksy@qAiM_0gWpEZdyvDV zphBm`i9A=reTA%Pzuwysok!LlQ-SD0?m-@GhN!Lm!vS(aC0=FCW?jfGrDOlz{1=da zli~j`5uCqM>XnaGl@N&pzYtJt4FcajCs^nO=i4Le?X#NGPT&s$!Up>@Hf ze$^^&)57@ssn_7SR^^)p2}Kz=hQbqr8ArjkKvBC%*Bp8-tVVUx;N#}JB+GYgXnb)5 zQ4u=qB*p*y2bFFkf%n%2q^!$|i8t5R4OD-jV9w-E+!{@upWhAN9=SBEkcd+@TG!Pp zJ3@CUmC;#Rhvd^QyaooW(wBoB^;Jj=~#sZvKc+M*4qb>K-g)a#l_q3V-=k?TQ8dH2G$%xV!Zfs1J21i zVk0Yl+L_~86!5aaw}|X^DBQM=Zp*$tE>YDwguVXAzSp5XcKG^v%*a{P&xx{8%0j?E zKGIdpNJ%>*lNTxr%5S{x?X7&NR(%n{zcrbjPE6Mvq*4l;!~cZtRw^5mk4LyFy(8Ti z&yd|Hr`c2;zNBCy4(`9W3#?)j7;rz8mdYDVrC#EB_ICS}ol?s$b%77>y&9MwtT`<< z`CTqr;PLr<4n`k*5@H47-O7Ac73dlvY))R>0HVIH9d<&DQQ0bEFomSV1S-0+^zGhO z{p6#SB~DJikRSQrx=2a+DOZN?OFzXCFhQX>p}f=OtbxXkaK}=xMlZUp@+N8j!I_9X z_0l{#Wwb~!O5}Le;*Y1Q>1z*>k=??8C)FwrJ#DgPX8vM-iC^jntXpA6l~I!jKd5xe z81@@zYAAb*NnM?D=ge)phEEV4@SEne!!v9YY5ARorZC&p54b1N7I3U>>=byDuw=e~ zoaZHQ5c}U-Kh-V$sk_du_~Uo$d#?~LrDT2iB2s~ptNlP!pP=c z`P-wR#iEXV`f=g?D!9LT!d3p4N3PcV#3DuAjW6v+LLap8R+!C|N%y-jJM2&Wh6old zcHQ6GRqRL4Bhl2T|6!s=B|(TCCr1R$VW2caUC5XdL}kq%|9a}bMaKWr0_&+n60I9g zDkqUH{Ax;m-sshV!1!5lBO|*rzp_dreRD!@A#O%S`b=cM^U*!xBYO1SjBta9rQ(@P zC)@k3A>ACZ+*gd=mE>eQ*XZbNo;EH^qt^~jPEIl82JN^;PD>*}A?{@BB11u*W2k}*(ykcvnvu-rUhDuV zKRNTYC-&o!OLm4ZwX^Mebl-Q199DTlOR0gA{h!34>PB}{UcTJf$pS__sZBbKT#rjj z&*?&rT(b`kza$4r$;g=N6?<>1_+5+RH>#q8J|zz-J{g zC(Lt9Svjf1R$a7Zl;2S=L-6Em>gHbQ-O_V1=iqB~u`zi)gFv2lcl2Ihc+r%s}lPvm6(ys90B_J5^L1zZAHL zUc19<-??(5y<|Ux(UwiCws3o@Uw|te30n zFJD-kNc)P8^})`%e&m{{u&@&vtSHLECvE6qkA-`_{g(~EmxCZ zAX?nMBpr*yucxMEmvtS?@UkWK@&dc|nJ2!L6WTZ6tHMG;Ga(pB&HMYY?KB!qf1~N{ zWQ}WOW##r(k_Q&w;QzX8Zf-8d^(?uf7?vvRqkK7F?C?fJ4|{P<)pqEH@6=FiCeh1v zw@7v&2%^#}BhrUu|6~(l zRzKIa?_1hqG9;zrcV|;sqXlJv+`Cgx?=kBx!eZP;YZI8Xx$e~Bm;mSVQf~S|_!>{U z?KxUU>eyjjTq1n6SSdvM+KH$HuXlD4tSyQf@3YgpeS^b$CvLzD@f5m(ygZ+%`g$_kaKX9YTo4v?80Dnqr>J9tC(wY6=H{7VngPE%o&fZM zuO3VKhLM8yhZF*AYK9lQeG92O1>~x&^%<}DVoQ**@e^d`c&Z&$~7JDA@g-#J|}8 z?qc9%%{mULAS>Gh*Y_QiumBiL&6U;-oliS+QAE{acmA~vpQIkm9K3I5Y-}7={v)LD z=n0V<`Imn7{iUyuQ{g@>Bq)eC3WVcAOb|<`^;<6bAQowQxTNjtbsGUDi+ni@F3$hzDkS_wF5hl~3$ag=cG+SOC6ddmTw^_Z^a=ENdw# z`F~41I&pfc6%y>_<)to~_Tt4W8nAF@Vgdbt3F!WSPN}WKdYRt7U7~#wfM#JHUuYN? zgYZsN`Pq6-vT`v`*X$ zLQ+v{&*Y3mv`vQdXSazwSYhG6R^~1G6y@X=7{Z^*%YA))q2@SEJ`1_+wO@dL6blOr z59Di}NH$plCD_*Cvt6)2SyavNULp!lAQOr`6?ANaa32)GJ0#q(yVFdz z0=((JVy=zHA3RTy=#DXqfl=FdtO{z+*uxxVRQg<-t+MpC^m2( z9WF5~w<_5AsQ}}Vx~$XIK^+34Ll=5a$cJm}?d^3v<2^x~X^E(Yr0M$&zaRNv4sE?j z(D);0bGa^jWpsIY`HhMTA%V@zKE^LqSX9K^TY`b9{TH)KOG^QRoJ0l_fvpxrU~Y4^ zYgW_T%nS~A>F(yNJ2D7JOUR8IH*|Czwf#qH1Nx(Pel?$V-KiR1c*Vvgm5426ko{IC zKWk`c;9DWyb$7a73Mvrd57q%NG2PwkQ*~YLf1N-7aPIp{KrFLi<^b!_3%Tz0_VxfD zNCSH-HVM;k&sNRA$(-EWjcG(6i?NrLl?6PKx{;oi=23soA{~YUq_5=g$q}3yW6a#& z)(@GxvTOIjEL|_*T}4F&kPfHMT`XGcRCIA6!hM2HfOi%22C8cH{ zV<_E;p*^jW2=IyUh=hQP&sJCQBfzo%x&o{L>aCTfrI>b*2fz-1IYG?}BEcNkh~TE# zXMmGg0k#n$feF!zAkRypV>s)xbJe^J;~Bev__{F|`pVc6lTIYo`}7&mXB-MRJT~zn zc6N3Ud;}I4i(H$sW2)busB!`nQyp({Rz~J7sV5ymj6*`~Ra8{^paq_{Z{LoLj8qq0 z!`2d8DebuSxPwjpE%`YT=__FI%q}8hQ5(2hKS9*bHX_Dl2Xb^nKXo1P`D%7P#ycEc z;iT_773Z?4wYIgjWdPlwt^FFKfR`?c0I>toN$q28a;Jh0c2Goup>Mm=TY);IO> zRn<9;@TbfeUN~3j$z(VYqn9KpTZiJWojVun$udB|bF4WS9MBR(6W2x*e_Dlzghzqi46Fxn|0$`~o`}l= z2XuI7;p5fgL;)Vrc1^Zd_kY-{{R7lLK>goK?~2wAD8Ip0(b3Vv z-ab^>&``C~>CRpLa*Gd7%^uZ%@jJ)>C$t~*p$_<-`VJQVsg8VzyF_lvzhv3;skgTe zTfW~yJJ@fm1&S=l4)(+8DQSE(4Xys3 zn{Ln{pMDCS1w6BjwpH!3u#g?JmZ!H!F~d)3AnG`izbekU>BP^?r>e2xZEYr_lBO3h z45lI`L=0-H!zp*EDJ7+&v%9a2Cw_}G`@xKWpOWc3`Y31ex8d$4iIO4pcAUNFb?*_r z^yxDvM~|&p^IzGg<_a>U=|*??IZt2Z;tizc4pmiE$)>Y@zQ|i%?vdhCO*tht2?G;l zl=PGXZuNXlHxTjQmb>(RE_*aFxc$vI62cG5^xlq0Pbfa-c=P6(RoTbJT2fVp6i}D% z-HzkoKHcLwe$3I{jcg_R9fS2TI7*DwhZ0OIhWZO<8m;UN()pHd6%0Qn2WBq+#y&^A zyo<3J@8b?JwDdDNf9ipQt$j?D>Z+1z-NWmBB3#=VsvaO<_T2OCQG(C8+bjEG+70jF zo__x>^4~x0E`|`y`6AbU0((;zWVWqhL!eD@8Q(fiGd2A)MWfT!q>GG2mecE=g4Y=g z^5mB|4D9fmH`)92btmG7F4vslFBdqcjw1Lc-WbkA;OW@=7NHUfZOWI9So+=k zxjucLuc`YWZ;;(af36a@yG2ApXj5Dkrkp+v{`jifJ;&`j5$gL2G7Ofe#p+irHc?t@ zob{v{TSLr4S=m3RVYfpm0Y5ZM2Av+%hkT4&SVT4LLI~j$DSS_6D>|Cmbp2o6F=Sf3 zHi1xd@CITxN0d6~wkp#1s{W>Bm-8t70b_n_LB$1%UI%i7X8tMaI_4XLOCbYYiT3?q z_pci=6h+<6C1}(d^9*u3u*^>TY|f+gScEovfrxV5XF4?k9VKp`zLJ+a4eS$dsYlg+ zS?>5|nlQb0Ro%}Unl-Nc$D0-MASU3snPh~6F&Ea+HG1zL@T=i;gC-*!{eF450=gk_ zVGdprTQ-|t@?U~j9s_SMVqG>^hrL~36zX0}_{J0xl9`6vSNN_S+{U4GHD@?4vBWkc z1n8`lbOW*kV6OzEzhOkhxa2@{F^6s`atO$Qo}B{O{d4&b4*%at@M^Us2UJmCpM45c zq#FjX8t9kp4&}E160NFIwls2p%>Ouv2?0*G-GJ8h>eZvZb|YoUCM8|Z`JrPukL7zK z@JXEf&rHr*tVx!vC@U&f)YOCn4Tpq9R=R=IZ?_Z2k~sM!Dypis_8wgSt-~kx-d{;o zm9>KlmU9|<{P0lzE8rvu*xDaWGG6j{Ah_AJ1e|dB_}JNv0R3R#BGxLJ5*ka1ZA_8U^3+&r%Mhdz z#J;bkOcANQLM$c7AgwKSp7?y;zwq8K_ngnU-+S)a?mg$t1M_s z0GvEz0s3c-9j=w9!g>!0ANJwBJAk4dX*K|G?ujw<_M=znOU&R0kA@{XmR(^r^1WRr zm>Kd#<+S%X;5^4F_i%S*4fng6e2XX4^Gh=R^1I`k`ZmwItqq8#B;9|{fTQz-Jzx+- zAKqL!MZ0+b@Xj$q9c-?lI((D)832rurEBNml$>NSK>Ki+f&r+}(uQ=2D8`C-) zwh7<>+eXFkG!r=#&qRR3+e_l?BMu;O;e-QE6ec4S#>ZnKe+8W4QHf)G5lk@?HgWy?FIN^BX+*D=R=ww#ybtF|X0S;K}kYg4_G z2!fB@hkZytHm%*F)_2YH2xF^R`D@gyp~6Y5N~B2>(_r(SmoN9TCdsG-%u~_q1q{2Z zyhmeMm!0GgFo~Ia&KNlvi}FYenUz%k1pwRQ#%8_5VAJ>BGG_t4doPnN$4Wh zMjiyJN=VN;PRSG-qWs3D_bjMa==)=@Q{o}~JVn*tb-56;YyDrNfd2ahK_R)sQB9iB zdcWKOqpGf{Hm25%U^llNxO!ul1OB@!NU`@4rYt!4nfX3vdq{co;~s}r<}tK+IytFs zVx&)9VJjXM5tZt*j1w%RAEHMt5>hnuFza^K$R94q1=C^37xN6yvi4DFcgdI-a_U~6 z|A+|&%NokXx-zCkKoeO659`00ik%C*4s?z#Ov$s!7?%$sW&8eX3Lr!uB zt(ts$z8LNX2yud3(_CMS9v-L=p$G0}F{Ty`dbP?rmM)*hUe{(Jnez zQfKPJ5;g6Hr(~@Yk;=@Pub1Uyvu}vb(d?fri2&bdQFkI9&&T) zvPJq0EQK_cu>Y3Y%1>y+-3(=MBa0#n)AKj3HTvcBu5(Hvi5RdSbb)hgt8Q)UstSF_ zw6^8d^;x z!q@>{q~&C5=WFryX!0^1&$(3}5R8gL7!Jaq=wpT46E)y%A z(-T(<(1odZJgN4ZCFr|z!@<9Fg=nOT7(A6r%Vr40aPd{aPK%RLX;gX771hIRg`Jre zDi8|19Pay1=*p^s>IJm`#;v@8ML6zkakkB)_>0)t_7$3B(!VWtIcR$;#Go7SLj`BD za;x&ItJT(BJJ2zg=v!B7%q+^ut*E0`^GRlxuX;L#A|ud|mvw`LNmZnK#A>{=*%$l# z!lK-SIE-3w?3dzylJ zc?}sLBw%ECLOCa^{**B^FSP5?V}q8!;g1|vcsJTDZL=yX z4`foEm+@5A?34$c*DS++s%^_Wmboy-7h7k>d70WbYjl+qp}Jk`|CEUvta1#^D|lK{ zW^pk1KJ}oJfBZ_5Z4Cx7)QBMalAmCfH=*)@Mm_o@mlRy0a|YexvA@&QYE;({SP?1W zQSreS6JsF`=XBfL#M*QVC2J{k7b}T%=woK`h8cM}nauiqV8inc$_`m8-_xu!rQ%zU z-tWBdLZf>HTh1x5O1wk$bpl6IsfYqi%&7epuqwmO7LNMizKs7^`C-+%z8u^@=z@pG&?+n&o(^V@6NZ5yC ztnF@C+}j*|Ug#y14hIeWdgXSezV{$e2h?Gk(dgwl>G%o*8haF2crY{3alB)EN&jqM zr$=7-2z&~1mUm3e=(Qo>-dD9_Uj3)}hU0qx(ozbs7y;ohqVxR{@76*=9!N-M*qeXt zg5&OMPR2J6QqD><>-V`(wrBrwcb_*rcPHC5{RloFP*o~*2|iPePCq&(A@}xjnDg{Z ztg4_^|F}J9F z^ZK!54Njxz{zaY$VE;77fJgtoKWNlP%36Nqe4sv&5Sa(k5B?AMCpr?yt zNX4ADcf2!VLS+s-EKXOueDT7yEt$c$)x@&i9`c>Iqi`YH#U^g~D?*0cN9St%I55>a zut2$VWe00vu!fQbi`T(cZgZ}v%^a7m33wfh3R<*f?w+)D+>3=Xt@VwI)#tsBzI$h; zV*Aa%&+6ZK9&?{|=>O5qoBP&G2fD^$PKfN7v)?|vfT6CY40yy#hQAn+VrH< z%!Spff#Lli>{=!Fms^r=Hka#~SEeif`gHizQ|{jTX2w(Nz6O3hvAQk;XxE7u=?%9h zE_?VT{c8QcnkPH&Twec#{d#mLS&xb^> zy)STR>CIcE|EGP86$IL>Ydk9_r*7R-^@{Xmm)`qb?d!Z(yc>VLuaiEN$yTMT$=mU)z4CdRu@jU;0`ReVo*K=R)$+oy@ z_vAu4P-@;|kr&5)nYW*=k3a9TTyaV1y_(|-)?Nha-Frgp#WA;cdEcV7?#h+DGy4ZL z@P#YWj*ScVp8U)Iytr3wcQo6f=6>;CU9nN3Aj>}T6a;^h^(lL=9b*w5t8xDi@9i>` z-ENbPd;vOQK{)4wG*R=`zu%>10OL3(q>B&aGL8hG&P56hK-YIPF#zL1feRFGRFzN( kzPvwWFVJj;7dz}3O`Q)PO1|Y<3*<6*y85}Sb4q9e065*Z<^TWy diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png index 6587c1b481c16ae016a1fbb42c4fa09f480a3c02..1ce126e499914dc4fb32f9d9b3868c5e50ba0cef 100644 GIT binary patch literal 2706 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 From a81bbd9b9cae726f3705f955ddc0ba04d4930a83 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 May 2025 11:59:40 +0100 Subject: [PATCH 053/211] Iterate --- modules/banner/element-web/src/Banner.tsx | 2 +- modules/banner/element-web/src/Menu.tsx | 48 +++++++++++------- modules/banner/element-web/src/theme.ts | 14 ++--- .../banner.spec.ts/static-menu-linux.png | Bin 13085 -> 13121 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 26330 -> 26520 bytes 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 310f584312..27e9fae5b9 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -19,7 +19,7 @@ import { theme } from "./theme.ts"; const Root = styled.nav` height: ${({ theme }): string => theme.bannerHeight}; background-color: ${({ theme }): string => theme.bannerBackgroundColor}; - border-bottom: "1px solid var(--cpd-color-bg-subtle-primary)"; + border-bottom: "var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary)"; display: flex; gap: var(--cpd-space-3x); `; diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index e77dfaf67b..673080a70b 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -32,7 +32,7 @@ const SidebarHeading = styled.div` display: flex; justify-content: space-between; align-items: center; - margin-bottom: 20px; // TODO check + margin-bottom: var(--cpd-space-5x); // TODO check `; const Trigger = styled.button` @@ -45,10 +45,15 @@ const Trigger = styled.button` width: ${({ theme }): string => theme.triggerWidth}; &:hover, - &:focus, - &[data-expanded="true"] { + &:focus { background-color: ${({ theme }): string => theme.triggerBackgroundColorHover}; - color: ${({ theme }): string => theme.triggerColorHover}; + color: ${({ theme }): string => theme.triggerColorContrast}; + } + + &:active, + &[data-expanded="true"] { + background-color: ${({ theme }): string => theme.triggerBackgroundColorPressed}; + color: ${({ theme }): string => theme.triggerColorContrast}; } svg { @@ -73,41 +78,50 @@ const CloseButton = styled.button` &:focus { background-color: ${({ theme }): string => theme.menuButtonBackgroundColorHover}; } + + &:active { + background-color: ${({ theme }): string => theme.menuButtonBackgroundColorPressed}; + } `; const CategoryHeading = styled.h2` font-weight: 700; font-size: 12px; - color: #203257; // TODO font - margin-top: 16px; - margin-bottom: 8px; + color: ${({ theme }): string => theme.subheadingColor}; + margin-top: var(--cpd-space-4x); + margin-bottom: var(--cpd-space-2x); `; const LinkButton = styled.a` - font-size: 14px; // TODO font + font-size: 14px; color: var(--cpd-color-text-action-primary); - font-weight: 500; + font-weight: var(--cpd-font-weight-medium); display: flex; border-radius: 8px; - padding: 8px; + padding: var(--cpd-space-2x); align-items: center; &:link { - color: var(--cpd-color-text-action-primary); // TODO + color: var(--cpd-color-text-action-primary); } - &:hover { - background-color: #eeeff2; // TODO + &:hover, + &:focus { + background-color: ${({ theme }): string => theme.menuButtonBackgroundColorHover}; + } + + &:active { + background-color: ${({ theme }): string => theme.menuButtonBackgroundColorPressed}; } `; const LinkLogo = styled.img` height: 24px; width: 24px; - border-radius: 3px; // TODO - border: 1px solid #eeeff2; // TODO - margin-right: 8px; + border-radius: 4px; + border: ${({ theme }): string => `var(--cpd-border-width-1) solid ${theme.menuButtonBackgroundColorPressed}`}; background-color: ${({ theme }): string => theme.menuBackgroundColor}; + margin-right: var(--cpd-space-2x); `; const CentredContainer = styled.div` @@ -116,7 +130,7 @@ const CentredContainer = styled.div` width: 100%; align-items: center; text-align: center; - font-weight: 600; + font-weight: var(--cpd-font-weight-semibold); svg { margin: 0 auto; diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index f6a6c43f30..bdc1285727 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -7,17 +7,19 @@ Please see LICENSE files in the repository root for full details. const theme = { textColor: "var(--cpd-color-text-primary)", + subheadingColor: "var(--cpd-color-text-secondary)", // TODO bannerBackgroundColor: "var(--cpd-color-bg-canvas-default)", bannerHeight: "60px", triggerWidth: "68px", - triggerBackgroundColor: "var(--cpd-color-bg-subtle-primary)", - // triggerBackgroundColorHover: "var(--cpd-color-bg-accent-hovered)", - triggerBackgroundColorHover: "var(--cpd-color-text-action-accent)", - triggerColor: "var(--cpd-color-text-primary)", - triggerColorHover: "var(--cpd-color-bg-canvas-default)", + triggerBackgroundColor: "var(--cpd-color-bg-subtle-secondary)", + triggerBackgroundColorHover: "var(--cpd-color-bg-accent-hovered)", + triggerBackgroundColorPressed: "var(--cpd-color-bg-accent-pressed)", + triggerColor: "var(--cpd-color-icon-primary)", + triggerColorContrast: "var(--cpd-color-icon-on-solid-primary)", menuWidth: "320px", menuBackgroundColor: "var(--cpd-color-bg-canvas-default)", - menuButtonBackgroundColorHover: "var(--cpd-color-bg-subtle-primary)", + menuButtonBackgroundColorHover: "var(--cpd-color-bg-action-secondary-hovered)", + menuButtonBackgroundColorPressed: "var(--cpd-color-bg-action-secondary-pressed)", }; type Theme = typeof theme; diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png index 8752e393a547dceb24b9f2054c777035f9afcdf3..9970b9ebdec6c8b4fd916936811d1980e0cacdd7 100644 GIT binary patch literal 13121 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 literal 13085 zcmeHuXH-+`x^Bd!vOpjfKv8620TqFzARVHh3l#ws0g)1rPCz;V5)xDpm1P3~0Yhx` zDlPONA_xdUdJiqMKmvgfLP&Bx+;`t|?!9}Ay~iDQoc-gjKO`e_&TqcoJKyp=&pUa1 z&BRbdNJ3>a z{4ny>P0HBX7}6yO88j<+`2O2R+kSbvZSSL7_qU^r-(TP3c(YzY)uXG=QC&h+;=JX&wkKrf+%;#WQ}FmP4Q`WF7uQK_26r^;I(Z<$vF?Qcy`mf1-I19Njpb!3)) zr-wZRa_306tOOffla*DW7TYwvtk6{}un!V`_5tK!S(nS1xH$7bk)an`>!kk2k28~g z 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 Uh1{v@a6p;EwvGp1S)YviSFRG7od5s; literal 26330 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 ax}xfpNm}ITjO9?RpKK5PSYZA0<^KW==@Ga9 From b4aa07c38500331db6d9f7d5e60e1889ed857bef Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 May 2025 12:19:30 +0100 Subject: [PATCH 054/211] Iterate --- modules/banner/element-web/src/Menu.tsx | 2 +- modules/banner/element-web/src/theme.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 673080a70b..447582822e 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -32,7 +32,7 @@ const SidebarHeading = styled.div` display: flex; justify-content: space-between; align-items: center; - margin-bottom: var(--cpd-space-5x); // TODO check + margin-bottom: var(--cpd-space-5x); `; const Trigger = styled.button` diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index bdc1285727..80d2528418 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. const theme = { textColor: "var(--cpd-color-text-primary)", - subheadingColor: "var(--cpd-color-text-secondary)", // TODO + subheadingColor: "var(--cpd-color-text-secondary)", bannerBackgroundColor: "var(--cpd-color-bg-canvas-default)", bannerHeight: "60px", triggerWidth: "68px", From 6e7cf567db05c8f14c16a88b316bba7a02cc5cd2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 May 2025 12:44:58 +0100 Subject: [PATCH 055/211] Finish theming --- modules/banner/element-web/src/Banner.tsx | 23 +++++------ modules/banner/element-web/src/config.ts | 16 +++++++- modules/banner/element-web/src/index.tsx | 16 +++++--- modules/banner/element-web/src/theme.ts | 36 +++++++++--------- .../banner/element-web/tests/banner.spec.ts | 8 +++- .../univention-nav-hover-linux.png | Bin 2719 -> 2705 bytes 6 files changed, 58 insertions(+), 41 deletions(-) diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 27e9fae5b9..81193fbaa2 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { type FC } from "react"; -import styled, { ThemeProvider } from "styled-components"; +import styled from "styled-components"; import { type Api } from "@element-hq/element-web-module-api"; import { Heading } from "@vector-im/compound-web"; @@ -14,7 +14,6 @@ import { type ModuleConfig } from "./config"; import UniventionMenu from "./Univention/Menu"; import Menu from "./Menu"; import Logo from "./Logo.tsx"; -import { theme } from "./theme.ts"; const Root = styled.nav` height: ${({ theme }): string => theme.bannerHeight}; @@ -50,17 +49,15 @@ const Banner: FC = ({ api, logoUrl, href, menu }) => { } return ( - - - {menuJsx} - - - - - {api.config.get("brand")} - - - + + {menuJsx} + + + + + {api.config.get("brand")} + + ); }; diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts index 8b6db1c122..1066a85bc2 100644 --- a/modules/banner/element-web/src/config.ts +++ b/modules/banner/element-web/src/config.ts @@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z } from "zod"; +import { z, ZodSchema, ZodTypeDef } from "zod"; + +import { Theme } from "./theme.ts"; const StaticConfig = z.object({ type: z.literal("static"), @@ -85,15 +87,25 @@ export const ModuleConfig = z.object({ */ logo_link_url: z.string().url(), + /** + * Configuration for the menu. + */ menu: z.discriminatedUnion("type", [StaticConfig, UniventionConfig]), + + /** + * Theme variable overrides, optional. + */ + theme: Theme.default({}), }); export type ModuleConfig = z.infer; +export type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; + export const CONFIG_KEY = "io.element.element-web-modules.banner"; declare module "@element-hq/element-web-module-api" { export interface Config { - [CONFIG_KEY]: ModuleConfig; + [CONFIG_KEY]: ConfigSchema["_input"]; } } diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index 54dc3a0392..f6baa9c896 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -5,6 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import { ThemeProvider } from "styled-components"; + import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; import Translations from "./translations.json"; import { ModuleConfig, CONFIG_KEY } from "./config"; @@ -33,12 +35,14 @@ class BannerModule implements Module { const root = this.api.createRoot(div); root.render( - , + + + , ); } } diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index 80d2528418..aadc572c3f 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -5,26 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -const theme = { - textColor: "var(--cpd-color-text-primary)", - subheadingColor: "var(--cpd-color-text-secondary)", - bannerBackgroundColor: "var(--cpd-color-bg-canvas-default)", - bannerHeight: "60px", - triggerWidth: "68px", - triggerBackgroundColor: "var(--cpd-color-bg-subtle-secondary)", - triggerBackgroundColorHover: "var(--cpd-color-bg-accent-hovered)", - triggerBackgroundColorPressed: "var(--cpd-color-bg-accent-pressed)", - triggerColor: "var(--cpd-color-icon-primary)", - triggerColorContrast: "var(--cpd-color-icon-on-solid-primary)", - menuWidth: "320px", - menuBackgroundColor: "var(--cpd-color-bg-canvas-default)", - menuButtonBackgroundColorHover: "var(--cpd-color-bg-action-secondary-hovered)", - menuButtonBackgroundColorPressed: "var(--cpd-color-bg-action-secondary-pressed)", -}; +import { z } from "zod"; -type Theme = typeof theme; +export const Theme = z.object({ + textColor: z.string().default("var(--cpd-color-text-primary)"), + subheadingColor: z.string().default("var(--cpd-color-text-secondary)"), + bannerBackgroundColor: z.string().default("var(--cpd-color-bg-canvas-default)"), + bannerHeight: z.string().default("60px"), + triggerWidth: z.string().default("68px"), + triggerBackgroundColor: z.string().default("var(--cpd-color-bg-subtle-secondary)"), + triggerBackgroundColorHover: z.string().default("var(--cpd-color-bg-accent-hovered)"), + triggerBackgroundColorPressed: z.string().default("var(--cpd-color-bg-accent-pressed)"), + triggerColor: z.string().default("var(--cpd-color-icon-primary)"), + triggerColorContrast: z.string().default("var(--cpd-color-icon-on-solid-primary)"), + menuWidth: z.string().default("320px"), + menuBackgroundColor: z.string().default("var(--cpd-color-bg-canvas-default)"), + menuButtonBackgroundColorHover: z.string().default("var(--cpd-color-bg-action-secondary-hovered)"), + menuButtonBackgroundColorPressed: z.string().default("var(--cpd-color-bg-action-secondary-pressed)"), +}); -export { theme }; +export type Theme = z.infer; declare module "styled-components" { // eslint-disable-next-line @typescript-eslint/no-empty-object-type diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index ae9566ec5d..fe60dfc3a4 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { test as base, expect } from "../../../../playwright/element-web-test.ts"; -import { ModuleConfig } from "../src/config.ts"; +import { type ConfigSchema } from "../src/config.ts"; const test = base.extend<{ // Resolver for when to respond to the navigation.json request @@ -39,7 +39,7 @@ test.describe("Banner", () => { // We don't take a screenshot as we don't want to assert Element's styling, only our own }); - const configs: ModuleConfig[] = [ + const configs: ConfigSchema["_input"][] = [ { logo_url: "http://localhost:8080/logo.svg", logo_link_url: "https://example.com/portal", @@ -84,6 +84,10 @@ test.describe("Banner", () => { logo_url: "http://localhost:8080/opendesk/logofull.svg", ics_url: "http://localhost:8080/ics/", }, + theme: { + triggerBackgroundColorHover: "#571EFA", + triggerBackgroundColorPressed: "#4519C2", + }, }, ]; diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png index 5cbc8775ee71225ed381a7913e7db6097b3e3b70..a72b1cedc1f8b8b4fc59513680e161814d9f0674 100644 GIT binary patch literal 2705 zcmcgu`8(U$7mlU6s8KUiJE%6*s@=D>1)(acXcR3>*SqsEz3j5V2312?Cw4FoPM{MZ8{S#QP6%WIETk2L^-CYlN#S zxO-uJW%VxTEz81ohg__Mp(@8l}m zd&y_Xc_SB_UGS`o_b!aa(3x01O3)h&x3HL6JBTtR?Zg@k7VXBYe(9$~AFzhE7r47Q z=d1#=NU3zIJ89_SfY*c&-y7w^00X%ypGx2Z$m8oEAppNk3&?tBT-E-uO)T+wc?)d9RTJrhef8a5_R<<2EOO;1#d zEM|4dh`u^al{Vbnt56dGH0u8T_tTXlEbR@$4Q%$7*S}7+mTmMG?8cnz1`-5H6X(I}c4pNtX zC)c*1lZ>z3ySYUYq!jzdZKVi72mMlJ6T1Z1wrv#7`UHpgc~nhCRpov9qH{+Y&8}kg zkLN`m3;iKEdTK|cEH>iyOOYjgdchv8CpBpjfFE_@;?Zd{y4gU z{y5RwV(c1UI`gQLyJ~>SH)h-ZYFeWV+%t_+8mZC9e*C|*I<@{TNlCqW$`}6$5OiZT zj=E5&M@roql^~Kxu6_sk!-;jafP|xqvuca$i1k9e_K_edF3FButl~H zLS6MPfkKAYTk9;y@M=LAEapXG`uNpUzcG`x>dV@fMqgG`b;*MVNB7`=@(CQ;P9`h{ z-8ZPMyC|OdN7DZ8T)I)xZ1ZTs(2wfbDnotFOgw{4r6NGwo z;nrgZD~}(pJpSI1nUckZYciN?8ueCtXfd`%!|>}&H+vXNY-|L`LC3>IOhW&WZ`G1| zU2$JRRMmIX=UXm793#J087f2jw`FWF^{AOVbuT>jh?A!udC14D+Zh?sv&{RuZf>z} zrswkv)TYr5VT)1PMnnOoX>RxLW==?Nx;!3RMSt-E z6Mp}p${b63XwP$_93CxY7#JSA7#I^8kPolgd(t07T;=d_O4k<6uAQ(DU2@FHI1f(8ElVVI)kr`xi|_)k!`I?c6Idfv;WB)f(5Ia9uil_fWnHdY|`$qNR^=_RMO*YHjMd^l`d zE3(?cV!7lB*nB}7$VPJZqPD8u)4E?XObf8%mE_rt*0)q{aNe$)93GJ;{D~(5ZO7}} z7GNk!2h`JJCoX~q!~bNDVWPM6xUpbx9Er-kZ=O=B$cTO_VPXvvFW$Zsic4O^vA{-z z;=YUE`t46NjtIs6;I^a~p_jxQ2)JZ=T!y=swfNi0i_K| z^aLQC#Rw=!e3&B{-(WEWf6pXglvfyw{BbXOKuhBhUWg{EjoQz1ip6%Oz zOLXy_J-SE%p{9y7<+l#cU8}RvLAfYN>%h-QozSS6 zQ(2n$p7#pyPN?33H+{TBFP~A*yG!xfF46YYe8(xC>rwo`zU>N_s&Lyheh==7@MvM- zI|7mH;7br;%>LZRu0dx{c=eNft*6PpxscUe$(LTD!Q7Q+SA33)D=ou4iyK6TYbWxx zG6em+;?~*S-guFNdFM;ZpH=jVJdn%`T}&kk$`nmeVZVED5>sptI?!6EAPPjh*}WNY zFFFjde>3x&S~^RA{=1Xwv%6uGINzDz_-`or8=X@u-Q!W;MM}y?L#&BErw?_5!%xup z&oTbs?xYL!0V3$NhKcl)lpIK2E+?}jt;ztn`1+Dlv&uh+#2qXYSs_qh!9hNpc)3p) zzrF1>a0wVx63Y5_C|}V2PC@4;DW7Z~Lw?qSy5$)tNd8VnZs0lJ63V>e?iMDEhXn?l zCU}aD{8`ZsMmYVEpFAosJ@X0;d0UhxXVAA8-2o`3eNFP8*GAMSxwkl<-B&x@0f9dD z-HQE5jL(CB)&e?tA9NBxbwPf00Gm!7YXT7TUkG*#XWqY5@B5BW03w>1Si*=mTp#=k DI2_$L delta 2459 zcmYjTc{JPE7pLoVw1}k`js7exn#4M^_Gz2wl%g3$D?(94Q$lSe_!?8Bw2T@{Z5cvR z31Z&~rA%$9y+SM{$RMpPc7D?H`{Vb{dFR~o-sgVa``q{LJvUL3Ao-^z@ZdK5`n~6w z%dD`w_eNwoR@@P_N_|}?SXoNN6+LhApd{yN&qz;*mgh}v!KD+L1*KVk2i^!se?{_b zYXf1ZDRfT1wgGqn|vz|-6FrjhtwSN@<%vX>-!RNp#Ogw1s8@c77M)mUn z_xWC7!?U}r)-SEZ79k@uRFKd6!55C=#yq&>tizK2`NgleXmAAm{+|8*#A_6V2~K(Y zz|!wd2LfRu0YW1c&|p6EA40%W)c%6F%)nPh!=|h{i6P^EO*ad(7`{I8G})MA2j`Z2 zOzI-mMIQno>e3e$oTueWOwfVjGy7Jwi;RQu7iozwAwE;h7kwVg;_|?!7*NnbVQ6?h zaZJ0%bYnpAkXc<%uZyemAUMwNgsfd1;X?oE3RUU*30oc(_So`(yE6A(~U%`nKF^AE-ybw;hV^3)=li)cXT-si8P)#g2@y7?J<%<8PS_v99g*c;zwK6ck;7V4t26q`a(7Nl0S9*$ z!1HS8hVYZTAzOMtUxH(C9~^$quVM~UFj{$qO4%7Qd77E=$tHyh+eL&&nO}v(X8PJS zG__GB^K^?3mo-_2h-7WYk!c0HWE6x|`$$EWpU6;UFx#2mFsBw$z-;= z#Gx8P2Eyv%qO<+}wk)?q{uKxi>b+_8#br$;Ppzvcmkq$#A)99iYA)GNs?){Ax?}Mw zH)Lgx^fccWJrMp@u;!kc;(a(u0DztMwbx@nQfL23Ts0?$UFU?;X=n5GSjsE$vXujE zOotB`-C+-xE(cWL;Bq*=X%HU@WV zxKS4q${@_#Iv{fpr#e2^`NM=8A%tYnF~lYYu+vrwko59PqPSDjsapCx07?QcaNtA)&p&C#e_9 zV{0I0i#Vl+xVdt@-grc}XB?rYEOuey6fH2I04;%lbGA5o#N+J=HALKc8+WvoFu-VCgE5eh6)O>T_@D8kP` zId5_)W+M}>2&4oE!3q9&K~bS@p$_kap{(HXT%(5IFm!^A$q)jLS@Ep%ZyHhwv42xp zpGr;dq2TewQK5;}^Nj{T4EuY^Moibm*Dgn2I{^`ueiRvgeO#yU#%d|0d941}Jg&l! zzBY)LEffuriwyV|SY1jKsZC8DAx81(!2tMW(YjmmB!m5JOiattIKtSNV{GMRVOd3^QxPz2) zm5fbSVR*%By|zD){P5JO!i+eAUi9oLS-+N9TWMnl`&Wa3ibp0b*%s*c-5q$hake?! ziAu7EDq<$=f3x#82lxHRe-}|ryqtzpx8Wq9>)w6imXMKmTz1uG?Z=~VL!2#b z6g^oOhX=MAbx_X+z6L4fHGZ>P{AO$8#hDCH`C?E~_4hlsdpc)s+KX|DADnL*JM#DC&y5Sl*IRI(rT5R7ULr@S z@6-i7V&R6WodHtequO$-!=X3nhn+$b7wHbQ0M=%>$%Ya@d4OHnf-8lX_8OAi(s0SH zSq!iHK`zs4Fy|5-@li_*glb#A640l4vy|S+=2hU~DC_byJ18OR=k=JN}4reDjj(1Ef8=eUPI=x60qsVF48NQ!}=?fEqTc0$J`3#&E97*gIxFD+>j};b+ zAiCZu^=&N@;e$l=Mtp_V&pGeC;AVaHBIm6(vwxolINk%tJ^f$u1y8kW2NL|j;OcbR zGIF*Cllk3ETJe=!gzL;~yt;_az=VH!D4S|}*lx^coH~WbO!_N^$Zf)o_avQ4C$`}? zEC*T>5)!m_vVHl88f4 Date: Mon, 19 May 2025 13:04:46 +0100 Subject: [PATCH 056/211] Update opendesk to Module API v1 --- modules/opendesk/element-web/src/index.ts | 2 +- modules/opendesk/element-web/tests/opendesk.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts index d88e7f774d..e50aac9ce4 100644 --- a/modules/opendesk/element-web/src/index.ts +++ b/modules/opendesk/element-web/src/index.ts @@ -41,7 +41,7 @@ declare global { } class OpendeskModule implements Module { - public static readonly moduleApiVersion = "^0.1.1"; + public static readonly moduleApiVersion = "^1.0.0"; public constructor(private api: Api) {} diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts index a182535d49..5c7a79e926 100644 --- a/modules/opendesk/element-web/tests/opendesk.spec.ts +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -87,7 +87,7 @@ test.use({ }, "net.nordeck.element_web.module.widget_toggles": { config: { - types: [], + types: ["jitsi"], }, }, "net.nordeck.element_web.module.widget_lifecycle": { From f0b10945da534976cb418ace8a1e788fe71c7648 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 May 2025 13:19:18 +0100 Subject: [PATCH 057/211] Update screenshots --- .../banner.spec.ts/static-nav-hover-linux.png | Bin 3445 -> 3346 bytes .../banner.spec.ts/static-nav-linux.png | Bin 3451 -> 3347 bytes .../univention-nav-hover-linux.png | Bin 2705 -> 2583 bytes .../banner.spec.ts/univention-nav-linux.png | Bin 2706 -> 2597 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png index c4806ab94c084ba0afeaa4ae177545f36a3b3537..06f9925da1ae9d9f53ed1f9defb0779308f3754e 100644 GIT binary patch literal 3346 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 T<*P-r()<@KVNl|4-bw!eb;(jH literal 3445 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 XxfMA1JEH5C{9mUnZIR_CJZ}FJ_DDMT literal 3451 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 bzpwBE(o4xeCK!NI`2kp%@M`}WJoY~TkK})w diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png index a72b1cedc1f8b8b4fc59513680e161814d9f0674..2c62995b7d27a7edbed008f8dcc2ac10eedf0bea 100644 GIT binary patch literal 2583 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 DI2_$L diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png index 1ce126e499914dc4fb32f9d9b3868c5e50ba0cef..f0870325ddbba510d65939e21381a9fcf869b4bf 100644 GIT binary patch delta 2328 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 From bb4a8b170e4d285e65c1b181a851cf1d961fa9ef Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 20 May 2025 13:43:25 +0100 Subject: [PATCH 058/211] Extract svg --- modules/banner/element-web/package.json | 3 ++- modules/banner/element-web/src/Menu.tsx | 8 ++------ modules/banner/element-web/src/trigger.svg | 3 +++ modules/banner/element-web/src/vite-env.d.ts | 8 ++++++++ modules/banner/element-web/vite.config.ts | 2 ++ 5 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 modules/banner/element-web/src/trigger.svg create mode 100644 modules/banner/element-web/src/vite-env.d.ts diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 6aa9b89e56..f852df5db3 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -19,7 +19,8 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^6.0.11", - "vite-plugin-node-polyfills": "^0.23.0" + "vite-plugin-node-polyfills": "^0.23.0", + "vite-plugin-svgr": "^4.3.0" }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 447582822e..4649f64a77 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -14,6 +14,7 @@ import { InlineSpinner } from "@vector-im/compound-web"; import { StaticConfig } from "./config"; import type { Api } from "@element-hq/element-web-module-api"; import Logo from "./Logo.tsx"; +import TriggerIcon from "./trigger.svg?react"; const Sidebar = styled(motion.div)` padding: 0 var(--cpd-space-3x) var(--cpd-space-4x); @@ -199,12 +200,7 @@ const Menu: FC = ({ api, config, fallbackLogoUrl }) => { - - - + diff --git a/modules/banner/element-web/src/trigger.svg b/modules/banner/element-web/src/trigger.svg new file mode 100644 index 0000000000..72273ad287 --- /dev/null +++ b/modules/banner/element-web/src/trigger.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/banner/element-web/src/vite-env.d.ts b/modules/banner/element-web/src/vite-env.d.ts new file mode 100644 index 0000000000..ef030aec3d --- /dev/null +++ b/modules/banner/element-web/src/vite-env.d.ts @@ -0,0 +1,8 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/// diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index 79d308a798..f57993abbd 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -11,6 +11,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; +import svgr from "vite-plugin-svgr"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -31,6 +32,7 @@ export default defineConfig({ }, plugins: [ react(), + svgr(), nodePolyfills({ include: ["events"], }), From 629b58bceaa969c82b7e493ebf77aca5587c9978 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 20 May 2025 13:43:35 +0100 Subject: [PATCH 059/211] Remove commented code --- modules/banner/element-web/tests/fixture/silent/index.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/banner/element-web/tests/fixture/silent/index.html b/modules/banner/element-web/tests/fixture/silent/index.html index 84072225b1..969c920d77 100644 --- a/modules/banner/element-web/tests/fixture/silent/index.html +++ b/modules/banner/element-web/tests/fixture/silent/index.html @@ -4,11 +4,6 @@ Univention Silent Login - From b22cf92880a2bae4a3efcfc57d3799eb06f1addb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 15:43:59 +0000 Subject: [PATCH 060/211] Bump vite in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.0.11 to 6.1.6 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.1.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.1.6/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 6.1.6 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index f852df5db3..d2a071d7b8 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -18,7 +18,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.0.11", + "vite": "^6.1.6", "vite-plugin-node-polyfills": "^0.23.0", "vite-plugin-svgr": "^4.3.0" }, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 68d4282a06..7df4196570 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -25,7 +25,7 @@ "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.0.11", + "vite": "^6.1.6", "vite-plugin-node-polyfills": "^0.23.0" } } From 33e4e30f9eadfb168f0298916713ab2bc3fcc7d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 12 Aug 2025 14:12:57 +0100 Subject: [PATCH 061/211] Element Web module for Restricted Guests --- modules/restricted-guests/README.md | 17 +++ .../restricted-guests/element-web/README.md | 11 ++ .../element-web/package.json | 30 ++++ .../element-web/src/RegisterDialog.tsx | 89 ++++++++++++ .../element-web/src/RoomPreviewBar.tsx | 66 +++++++++ .../element-web/src/config.ts | 45 ++++++ .../element-web/src/index.tsx | 65 +++++++++ .../element-web/src/translations.json | 38 +++++ .../tests/restricted-guests.spec.ts | 133 ++++++++++++++++++ .../element-web/tests/services.ts | 35 +++++ .../element-web/tsconfig.json | 8 ++ .../element-web/vite.config.ts | 47 +++++++ 12 files changed, 584 insertions(+) create mode 100644 modules/restricted-guests/README.md create mode 100644 modules/restricted-guests/element-web/README.md create mode 100644 modules/restricted-guests/element-web/package.json create mode 100644 modules/restricted-guests/element-web/src/RegisterDialog.tsx create mode 100644 modules/restricted-guests/element-web/src/RoomPreviewBar.tsx create mode 100644 modules/restricted-guests/element-web/src/config.ts create mode 100644 modules/restricted-guests/element-web/src/index.tsx create mode 100644 modules/restricted-guests/element-web/src/translations.json create mode 100644 modules/restricted-guests/element-web/tests/restricted-guests.spec.ts create mode 100644 modules/restricted-guests/element-web/tests/services.ts create mode 100644 modules/restricted-guests/element-web/tsconfig.json create mode 100644 modules/restricted-guests/element-web/vite.config.ts diff --git a/modules/restricted-guests/README.md b/modules/restricted-guests/README.md new file mode 100644 index 0000000000..9904fa8698 --- /dev/null +++ b/modules/restricted-guests/README.md @@ -0,0 +1,17 @@ +# Restricted Guests Module + +A pair of modules to allow guests to register with Element using the Module API. + +Users get a link to an ask to join room, enter their name, and can participate in the room without any further registration. + +These guest users + +- have a real user account on the Homeserver. +- get a username with the (configurable) pattern @guest-. +- have a display name that always includes the (configurable) suffix (Guest). +- are restricted in what they can do (can't create rooms or participate in direct messages on the homeserver). +- are only temporary and will be deactivated after a lifetime of (configurable) 24 hours. + +This was initially created to allow non-organisation members to join NeoDateFix meeting rooms, even if they don't have a user account in the private and potentially non-federated homeserver. + +See further documentation in the Synapse and Element Web module directories. diff --git a/modules/restricted-guests/element-web/README.md b/modules/restricted-guests/element-web/README.md new file mode 100644 index 0000000000..75a29f2eed --- /dev/null +++ b/modules/restricted-guests/element-web/README.md @@ -0,0 +1,11 @@ +# @element-hq/element-web-module-restricted-guests + +Restricted Guests module for Element Web. + +Supports the following configuration options under the configuration key `io.element.element-web-modules.restricted-guests`: + +| Key | Type | Description | +| ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| guest_user_homeserver_url | string | URL of the homeserver on which to register the guest, must be running the synapse module. | +| guest_user_prefix | string | Prefix to apply to all guests registered via the module, defaults to `@guest-`. | +| skip_single_sign_on | boolean | If true, the user will be forwarded to the login page instead of to the SSO login. This is only required if the home server has no SSO support. | diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json new file mode 100644 index 0000000000..6d01895cb3 --- /dev/null +++ b/modules/restricted-guests/element-web/package.json @@ -0,0 +1,30 @@ +{ + "name": "@element-hq/element-web-module-restricted-guests", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "lib/index.js", + "scripts": { + "prepare": "vite build", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", + "test": "echo no tests yet" + }, + "devDependencies": { + "@element-hq/element-web-module-api": "^1.0.0", + "@types/node": "^22.10.7", + "@types/react": "^19", + "@vitejs/plugin-react": "^4.3.4", + "react": "^19", + "rollup-plugin-external-globals": "^0.13.0", + "typescript": "^5.7.3", + "vite": "^6.1.6", + "vite-plugin-node-polyfills": "^0.23.0" + }, + "dependencies": { + "@vector-im/compound-design-tokens": "^4.0.3", + "@vector-im/compound-web": "^7.11.0", + "styled-components": "^6.1.18", + "zod": "^3.24.2" + } +} diff --git a/modules/restricted-guests/element-web/src/RegisterDialog.tsx b/modules/restricted-guests/element-web/src/RegisterDialog.tsx new file mode 100644 index 0000000000..47bc0f7e18 --- /dev/null +++ b/modules/restricted-guests/element-web/src/RegisterDialog.tsx @@ -0,0 +1,89 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { FC, useState, type JSX, FormEvent } from "react"; +import { type Api, type AccountAuthInfo, type DialogProps } from "@element-hq/element-web-module-api"; +import { Form } from "@vector-im/compound-web"; + +import { ModuleConfig } from "./config.ts"; + +export interface RegisterDialogProps extends DialogProps { + api: Api; + config: ModuleConfig; +} + +const enum State { + Idle, + Busy, + Error, +} + +const RegisterDialog: FC = ({ api, config, onCancel, onSubmit }) => { + const [username, setUsername] = useState(""); + const [state, setState] = useState(State.Idle); + + async function trySubmit(ev: FormEvent): Promise { + ev.preventDefault(); + setState(State.Busy); + + try { + const homeserverUrl = config.guest_user_homeserver_url; + + const url = new URL("/_synapse/client/register_guest", homeserverUrl); + + const response = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ displayname: username }), + }); + + if (response.ok) { + const accountAuthInfo = await response.json(); + onSubmit(accountAuthInfo); + } + } catch (e) { + console.error("Failed to create guest account", e); + setState(State.Error); + } + } + + let message: JSX.Element | undefined; + if (state === State.Error) { + message = {api.i18n.translate("register_dialog_error")}; + } else if (state === State.Busy) { + message = {api.i18n.translate("register_dialog_busy")}; + } + + const disabled = state !== State.Idle; + + return ( + + + {api.i18n.translate("register_dialog_register_username_label")} + { + setUsername(event.currentTarget.value); + }} + placeholder={api.i18n.translate("register_dialog_field_label")} + /> + {message} + + +
+ {api.i18n.translate("register_dialog_existing_account")} + + + + {api.i18n.translate("register_dialog_continue_label")} + + + ); +}; + +export default RegisterDialog; diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx new file mode 100644 index 0000000000..e67c11ce1a --- /dev/null +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -0,0 +1,66 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { FC, type JSX } from "react"; +import { Button } from "@vector-im/compound-web"; +import { type Api } from "@element-hq/element-web-module-api"; +import styled from "styled-components"; +import { useWatchable } from "@element-hq/element-web-module-api"; + +import { ModuleConfig } from "./config.ts"; +import RegisterDialog from "./RegisterDialog.tsx"; + +export interface RoomPreviewBarProps { + api: Api; + config: ModuleConfig; + children: JSX.Element; + roomId?: string; + roomAlias?: string; + promptAskToJoin?: boolean; +} + +const Container = styled.aside` + margin: auto; +`; + +const RoomPreviewBar: FC = ({ api, config, roomId, roomAlias, promptAskToJoin, children }) => { + const profile = useWatchable(api.profile); + const isGuest = profile.isGuest; + + if (promptAskToJoin || !isGuest || !(roomId || roomAlias)) return children; + + const onTryJoin = async (): Promise => { + const { finished } = api.openDialog( + { + title: api.i18n.translate("register_dialog_title"), + }, + RegisterDialog, + { + api, + config, + }, + ); + + const { model: accountAuthInfo, ok } = await finished; + + if (ok && accountAuthInfo) { + await api.overwriteAccountAuth(accountAuthInfo); + await api.navigation.toMatrixToLink(`https://matrix.to/#/${roomId ?? roomAlias}`, true); + } + }; + + return ( + +
{api.i18n.translate("join_message")}
+
+ +
+
+ ); +}; + +export default RoomPreviewBar; diff --git a/modules/restricted-guests/element-web/src/config.ts b/modules/restricted-guests/element-web/src/config.ts new file mode 100644 index 0000000000..c340068766 --- /dev/null +++ b/modules/restricted-guests/element-web/src/config.ts @@ -0,0 +1,45 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { z, ZodSchema, ZodTypeDef } from "zod"; + +export const ModuleConfig = z.object({ + /** + * The URL of the homeserver where the guest users should be registered. This + * must have the `synapse-restricted-guests-module` installed. + * @example `https://synapse.local` + */ + guest_user_homeserver_url: z.string().url(), + + /** + * The username prefix that identifies guest users. + * @defaultValue `@guest-` + */ + guest_user_prefix: z + .string() + .regex(/@[a-zA-Z-_1-9]+/) + .default("@guest-"), + + /** + * If true, the user will be forwarded to the login page instead of to the SSO + * login. This is only required if the home server has no SSO support. + * @defaultValue `false` + */ + skip_single_sign_on: z.boolean().default(false), +}); + +export type ModuleConfig = z.infer; + +export type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; + +export const CONFIG_KEY = "io.element.element-web-modules.restricted-guests"; + +declare module "@element-hq/element-web-module-api" { + export interface Config { + [CONFIG_KEY]: ConfigSchema["_input"]; + } +} diff --git a/modules/restricted-guests/element-web/src/index.tsx b/modules/restricted-guests/element-web/src/index.tsx new file mode 100644 index 0000000000..564945ffe3 --- /dev/null +++ b/modules/restricted-guests/element-web/src/index.tsx @@ -0,0 +1,65 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; +import Translations from "./translations.json"; +import { ModuleConfig, CONFIG_KEY } from "./config"; +import { name as ModuleName } from "../package.json"; +import RoomPreviewBar from "./RoomPreviewBar.tsx"; + +const GUEST_INVISIBLE_COMPONENTS = [ + "UIComponent.sendInvites", + "UIComponent.roomCreation", + "UIComponent.spaceCreation", + "UIComponent.exploreRooms", + "UIComponent.roomOptionsMenu", + "UIComponent.addIntegrations", +]; + +class RestrictedGuestsModule implements Module { + public static readonly moduleApiVersion = "^1.0.0"; + + private config?: ModuleConfig; + + public constructor(private api: Api) {} + + public async load(): Promise { + this.api.i18n.register(Translations); + + try { + this.config = ModuleConfig.parse(this.api.config.get(CONFIG_KEY)); + } catch (e) { + console.error("Failed to init module", e); + throw new Error(`Errors in module configuration for "${ModuleName}"`); + } + + this.api.customComponents.registerRoomPreviewBar((props, OriginalComponent) => ( + + + + )); + + // TODO replace this with a more generic API + this.api._registerLegacyComponentVisibilityCustomisations(this); + } + + /** + * Returns true, if the `userId` should see the `component`. + * + * @param component - the name of the component that is checked + * @returns true, if the user should see the component + */ + public readonly shouldShowComponent = (component: string): boolean => { + if (!this.config || !this.api.profile.value.userId?.startsWith(this.config.guest_user_prefix)) { + return true; + } + + return GUEST_INVISIBLE_COMPONENTS.includes(component); + }; +} + +export default RestrictedGuestsModule satisfies ModuleFactory; diff --git a/modules/restricted-guests/element-web/src/translations.json b/modules/restricted-guests/element-web/src/translations.json new file mode 100644 index 0000000000..bca1dc4a76 --- /dev/null +++ b/modules/restricted-guests/element-web/src/translations.json @@ -0,0 +1,38 @@ +{ + "register_dialog_register_username_label": { + "en": "Username", + "de": "Benutzername" + }, + "register_dialog_title": { + "en": "Request room access", + "de": "Raumbeitritt anfragen" + }, + "register_dialog_busy": { + "en": "Creating your account...", + "de": "Erstelle dein Konto..." + }, + "register_dialog_continue_label": { + "en": "Continue as guest", + "de": "Als Gast fortfahren" + }, + "register_dialog_field_label": { + "en": "Name", + "de": "Name" + }, + "register_dialog_existing_account": { + "en": "I already have an account.", + "de": "Ich habe bereits einen Account." + }, + "register_dialog_error": { + "en": "The account creation failed.", + "de": "Die Anmeldung als Gastnutzer ist fehlgeschlagen." + }, + "join_message": { + "en": "Join the room to participate", + "de": "Treten Sie dem Raum bei, um teilzunehmen" + }, + "join_cta": { + "en": "Join", + "de": "Verbinden" + } +} diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts new file mode 100644 index 0000000000..f8f180a0cb --- /dev/null +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -0,0 +1,133 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { StartedSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; +import { Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.ts"; + +import { test as base, expect } from "../../../../playwright/element-web-test.ts"; +import { RestrictedGuestsSynapseContainer } from "./services.ts"; + +const test = base.extend< + { + testRoomId: string; + }, + { + guestHomeserver: StartedSynapseContainer; + bot: Credentials; + } +>({ + testRoomId: [ + async ({ homeserver, bot }, use) => { + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Test room", + preset: "public_chat", + topic: "All about happy hour", + initial_state: [ + { + // This is required to allow guests to join the room with this Synapse module + type: "m.room.join_rule", + state_key: "", + content: { join_rule: "knock" }, + }, + ], + }, + ); + await use(roomId); + }, + { scope: "test" }, + ], + bot: [ + async ({ homeserver }, use) => { + const bot = await homeserver.registerUser("bot", "pAs5w0rD!", "Bot"); + await use(bot); + }, + { scope: "worker" }, + ], + guestHomeserver: [ + async ({ logger, synapseConfig, network }, use) => { + const container = await new RestrictedGuestsSynapseContainer() + .withConfig(synapseConfig) + .withConfig({ server_name: "guest-homeserver" }) + .withNetwork(network) + .withNetworkAliases("guest-homeserver") + .withLogConsumer(logger.getConsumer("guest_homeserver")) + .start(); + + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], +}); + +test.use({ + displayName: "Tommy", + synapseConfig: { + allow_guest_access: true, + }, + labsFlags: ["feature_ask_to_join"], +}); + +test.describe("Restricted Guests", () => { + test.use({ + page: async ({ page, homeserver, guestHomeserver }, use) => { + await page.goto("/"); + await use(page); + }, + }); + + test("should error if config is missing", async ({ page }) => { + await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + await expect(page.getByText("Errors in module configuration")).toBeVisible(); + }); + + test.describe("with config", () => { + test.beforeEach(({ config, guestHomeserver }) => { + config["io.element.element-web-modules.restricted-guests"] = { + guest_user_homeserver_url: guestHomeserver.baseUrl, + }; + }); + + test( + "should show the default room preview bar for logged in users", + { tag: ["@screenshot"] }, + async ({ page, user, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); + + const button = page.getByRole("button", { name: "Join the discussion" }); + await expect(button).toBeVisible(); + }, + ); + + test( + "should show the module's room preview bar for guests", + { tag: ["@screenshot"] }, + async ({ page, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); + + const button = page.getByRole("button", { name: "Join", exact: true }); + await expect(button).toBeVisible(); + await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot("preview-bar.png"); + + await button.click(); + const dialog = page.getByRole("dialog"); + await expect(dialog).toMatchScreenshot("dialog.png"); + + await dialog.getByPlaceholder("Name").fill("Jim"); + await dialog.getByRole("button", { name: "Continue as guest" }).click(); + + await expect(page.getByText("Ask to join?")).toBeVisible(); + }, + ); + }); +}); diff --git a/modules/restricted-guests/element-web/tests/services.ts b/modules/restricted-guests/element-web/tests/services.ts new file mode 100644 index 0000000000..6b56108b06 --- /dev/null +++ b/modules/restricted-guests/element-web/tests/services.ts @@ -0,0 +1,35 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { + StartedSynapseContainer, + SynapseContainer, +} from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; +import path, { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// We use the SynapseContainer as a base to have all of its utilities for config setting +export class RestrictedGuestsSynapseContainer extends SynapseContainer { + public override async start(): Promise { + this.withCopyDirectoriesToContainer([ + { + source: path.resolve(__dirname, "..", "..", "synapse", "synapse_guest_module"), + target: "/modules/synapse_guest_module/", + }, + ]).withEnvironment({ + PYTHONPATH: "/modules", + }); + this.config.modules.push({ + module: "synapse_guest_module.GuestModule", + config: {}, + }); + + return super.start(); + } +} diff --git a/modules/restricted-guests/element-web/tsconfig.json b/modules/restricted-guests/element-web/tsconfig.json new file mode 100644 index 0000000000..fbda6380e7 --- /dev/null +++ b/modules/restricted-guests/element-web/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts new file mode 100644 index 0000000000..3663c873e1 --- /dev/null +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -0,0 +1,47 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.tsx"), + name: "element-web-module-restricted-guests", + fileName: "index", + formats: ["es"], + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + rollupOptions: { + external: ["react"], + }, + }, + plugins: [ + react(), + nodePolyfills({ + include: ["events"], + }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + process: { env: { NODE_ENV: "production" } }, + }, +}); From a596d89fa0e25e3586eb7baf6fa4b6ea327dfec7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 12 Aug 2025 15:53:40 +0100 Subject: [PATCH 062/211] Make knip happy --- modules/restricted-guests/element-web/src/RegisterDialog.tsx | 2 +- modules/restricted-guests/element-web/src/RoomPreviewBar.tsx | 2 +- modules/restricted-guests/element-web/src/config.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/restricted-guests/element-web/src/RegisterDialog.tsx b/modules/restricted-guests/element-web/src/RegisterDialog.tsx index 47bc0f7e18..ec82a54631 100644 --- a/modules/restricted-guests/element-web/src/RegisterDialog.tsx +++ b/modules/restricted-guests/element-web/src/RegisterDialog.tsx @@ -11,7 +11,7 @@ import { Form } from "@vector-im/compound-web"; import { ModuleConfig } from "./config.ts"; -export interface RegisterDialogProps extends DialogProps { +interface RegisterDialogProps extends DialogProps { api: Api; config: ModuleConfig; } diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx index e67c11ce1a..db303d95c8 100644 --- a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -14,7 +14,7 @@ import { useWatchable } from "@element-hq/element-web-module-api"; import { ModuleConfig } from "./config.ts"; import RegisterDialog from "./RegisterDialog.tsx"; -export interface RoomPreviewBarProps { +interface RoomPreviewBarProps { api: Api; config: ModuleConfig; children: JSX.Element; diff --git a/modules/restricted-guests/element-web/src/config.ts b/modules/restricted-guests/element-web/src/config.ts index c340068766..f5f03a3236 100644 --- a/modules/restricted-guests/element-web/src/config.ts +++ b/modules/restricted-guests/element-web/src/config.ts @@ -34,7 +34,7 @@ export const ModuleConfig = z.object({ export type ModuleConfig = z.infer; -export type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; +type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; export const CONFIG_KEY = "io.element.element-web-modules.restricted-guests"; From 22f53ece84a0a3bcb00cde33f380d78e14fc4c9b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 12 Aug 2025 16:30:58 +0100 Subject: [PATCH 063/211] Add screenshots --- .../restricted-guests.spec.ts/dialog-linux.png | Bin 0 -> 14020 bytes .../preview-bar-linux.png | Bin 0 -> 4101 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png create mode 100644 modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..74f69152f6d3ea0cde6eecabaca6ee8ca4c53359 GIT binary patch literal 14020 zcmd73XH-*9^frnGQ9-FvL_mWSsnV6I5PDO3M?iY-y(j{qi3o@R0RaIaK%^veA|N1y z-a_w1dM9+=;rCzbez@zsAKnl5u6q{CIp@sI%$_rQ?|Jq!W8^C}`8&7o-zFd+xTB~5 z)gU0a`WxIY-nx78uHQv#a$1U2?!n%C_-Oq`FvZSuzo`|I7+l-?O&=W zT!;T89di99gETqR4@ptzAxlm?Y=XR+^)Tuu4Lt<5m!*+taxL@2=U1P}`us;6HZ3Pf zudRN5@zp__gDMNftzp8)WZ*{%@ z+|9d9Pp!Li;~2#Ccr^ zhmthS8+EHHi!moDMgHr_WJY%T+b`&HG8&Zw@vUEETfP-6WE7F}NzZvlB=5(z1N+1UIIg;4@oMxIl-p<7{%-c!Er`1C1dWh{)HnYsVVi^Rl4r}1)@ z^*#714(a}E{QLLc=*U~huE6I{pZ=Ou3p#UL;>E1h`aEgs##PA2Iq}v%?KkK1Uiqv7 zjVC8(`^W^zDl)9-4s>KBzr!S1ZXzZR)%rH#z?#*eyi{9T>#$JcwLY?Y!h|a=#bVpf zPS2!eq*>J-NZHMA`N>aVE~ewNTT4sb-Q0?ahzL=({RP(D^RJmB+^fb11~e(zBrFkC zj$~8~a|`CMNa1Sabqgztj8zTqSpSc$g?4ziFb7AU z&eFcf29Hu&Ai^!^1BV z)n4h6j!|d(wlK4=QU@DS9{;vbg=@97r@wl7Dl3N-6Il2DO%|DbFho14t4cJ>(cZs5 zTCIIrZrd$V%jF+5RpUH$j@2#xl9+gWauOC1u^(JJHJ`{vc=cT%iuD{dFfagSXJKc7 zQ(u4Nc222ks?Pc-tC|{|>cNA7Kbz(8{X_&Ii=JZ-+k1LGl_#{W4AKe*>@_RXLHFAF z;?lo8ONVJ_s&{_f&yvn8)a`F=J;@W2dd$i?tTgF&axrvP+fe@zI*Z<$YZfr|S+1wN z8QO5zhee*Ax7LTVCGgwM%16_TIz8s?_VsM##RwG+Wyg_UK3=5{e5Z~8o z-=1TOii#R>6Mm<+-xSBhb@*$>P!__>JXCmIFB{Ens5rVTAYj#1j6fiC`v(X2qr0U7 zXCqrj;?UP;*DDqSOC74(-j)u=IMY=)uI5H2q*n(VMV&DkprWSg+#_DGJNU>!wO;8x z^FQFEgD2A$r<~1ph%8+^KcCYYU8WPT#=b4}8@ztC!8(Hnm1859?Ye>34H~OL%-j|8 zP%yx3$m{Cr9#~Lb81PuW%~c7?H4}FLQ7SU%uTLe{(6ElJ79$g0cUqN)(zHV+k?jy&h>O6kp0ca9#> z(9`-cxN<&RBW2l_0EvgegdHcV*ZLRMg0iv=W9~n1?8b>mVWtlw6GYQK!o(%S(}OqY zAk|*n_EYEApj0$8VsEz|o)AcyZYK@ev1I8B!{|MsJKvPpF zjGFu0r>UiL?Oe=rG@dj)<%bu_svtlg2MpC9EQ}TyW76rV z$tfS)A4CVTm5p>f?P!!`Dxv2&-m)+h%GVOz4NI0Zte-vH)3Q8c*YH z1Z~762bgnK7!hfE2h$UsiQ|M8RIIpLNCOJoC(MoB)p6`lKlKHd`-@nHtkpAmwU5oK!`LbV$FKZ zEI+)mw|YYBzmU^+EWylGq9<;4Ivl=05lLV3N>ft?%9>ycMlkaE!XM8qA|hhgi8Bq% z(e0iG{;IRh9chExkZ|yA{m8&7akNANDKcmdJ|AIAH%TwyEsL|ms zx4a8YjkKhp3k&zwT7UfTLT|X}oOn5Iij9K>bFI-Ds~dnm?TSmHrn%?6-sijbFP;-~ zu9Nx7q&AVy=ilG?`3ybAKTj)Ofdf2&HFHG zbkv}(;rCtEfAI09dMgzb52%li*=ut0d(_k;Y-Ia9X+_Rq-50x_tZ1V@4tydMRn6vY zwE?oM36^i)Qd3ds;0C@uci$Karxun5w#>*Oi zrrwJS3zWU{iIENmFQ9#*%My4!HUu*yOu8Y+;m{@o3aQ$3S-nrPVGm<~(a@6p+QqGJ z2LJl^uVjv-=|^t5IgUray3oI(7%O%1V{-VMb=KwC0aH?YYUug>^jsv8wC1}At>FFG z)1j>wq@>=Gn4rnUxz#3CvbCF$RuyJ*i*dipW@TqvccrL8r;r8`%WaWn zW|`3Wg=J5B$LQ<`yhy1j-f(V)UefgqmV2Yk4rRbaeL~S~h}psDZIAqW&Ad@Mp~`Il zEjqD98Swyw<9Xf~R%YEl(n_Y5la_wAGj57@b~2}p-KzH_a`Z_N5<;!LkN)rKF7hI* zGBOiRU7g6Zch){YuO{2_0F=1Yi5aQu%W=<3Ok}%u^;BYfqRhP*Bfg|f<*o+b^x!%HqY50kZ+E?M?{yFY!g)@DJtdh z_xpzr!fO@@=!ho)fq}!V*KZR?-^ch~C!7fQbI};4y!JCFHSmOV2*E`)5peixQj(ZB zdPRbsj)n%yV^r@pCKD68wY5bn>ai6(>F?dWVJhN$4Cfa2=BXYS7B4n8Nqd6vDT?fKH?_j69O5XmiVIhI{?ReouE~- z-eC4#NJSfmvQoF=^>dJ{-@Txb+#8i8S$Hkl{QW)O=_-1Lj^y>mc zdjDAn3@o44SZDew^b)=aQo<6>XL&z`2u3s z-woz^uKLBc@a$~Na^1UfMZL^RPoo4$pfZ)#Td zsJLrgqBj|Yms)ef1jP-~@AYtbRHtt8MMg%V?}3%b)G6atgJdHiF%3eCW30fr-WYY< zit5kBY_*kk!+r)|1UVd4xi6z5o=38a;9F=+i;ebXT0>XV;6RW z>~Ub(6?2)UbS>eFh>(EpAzNvG$K-_^-L-sd+b94GppE-o4%9 z*n?{X`e|aGBzNy}aB_;!#xTprTHxWnNA3BVc{0-PM|_{^UpYVT&y@fG6rs&A_=_AM zIl7S|WtuvZi?f2kH3wZMC#SVHawvnqo%tt_`(o#9eA)7z%j3&_P8+VJiB%iFBm*&ubJERIIGhTJ=pfvk&=>&dNsGxaD*oY zz`jI9%8`^ZE47RLiZrF1l~GjT408(!9X|~AkwIO;nmWp>yHGvF=fK`Rl1F`#2+>?zSpP^MT5lrf{;}D)*&02C0|O07)s91w}8e zt0&xKy{95mRRG>&Fc~_Cv0ttN{=V|D54zA2g4@XEZrsxFwpNl-Qi6M3ZtlT$>xrMQ zAEQ)!yPCQ*lv$p{{`k_SFGBn~pzwHK-@iTaYdbCbQu}Kop=oNa$1ybYYC3=Cwk7pT zeQGBtLy`58!Pp{y0<7X=gUYQlk;OVoDM}Wx&Hi0Y>w&SE_oS0!)s94B3t5h=O4+@K zEB3p=XO7lQ35rT~n1j|$2?>c2yUWZ6pDwkb=Q!wXTIT(%Y((7a^vB-bE*iBOY+~Ub z(wG}x#UJ@EV$go_;?3(OI6HG=lkUYY9 z1V{$=Hsaax#BIlxCakg?QosJ%$Qwj&C#jy=Klj}kD5v9PPb&B?4*~=8x7+!V8=MC? z0@BJ>l|*1&IOLK^cil@U&fR_FMBYZ>@6Y%qaR~{!aK4n707;}~-d&jplJmE1=E6 zK+YS(D0G_LObo!k4ZZXHP1YkXP5#7#8W&y+jVKT)lP`47t)_ zSw0TGYLDrD6iU?6+zq#lQaqola+lQ(tmu|Cef* zCrN-FI6s_=Yj0O78+kxR_SEBF4-9q_QmV%#AIDhdwQk_+Ta}SvDlILYAsKiWaK1g) z>b*WHWYzK2qV?n8;2^ht=?88&zon9&pP!oAM?J2Mu?pv89%GmS62~O&^AwP9Xtc1n zxHu~-$dM(*8IiTMK)HobB2pesPHb1#T@sQ9eC8jdGlwz3!62zuQ20CFmM}3fL4Rv* z6XOTez0gp#d#6|9?Nd@xBATZ5n1YTZyu7^pE`ulu?b8t0Xt{0nEB3zrexPWV6By>jM+ULc z%>vM>Lu7I?u0R{Hv%7nCdfF1lgw#O<1_nm5^YLx3S4@E~ROZ-MY+Ms`c1#|xkw0GN zk57D}X@GW6gaXz;#A*CtmR#esTkp$jAf8lIdS7i=a7+07xhq2o=uprXK?*|#>@c9d;msRb0Ck3S{&|QVq^m+H zSygNX3$$OpeCg`Couiz5mr3%pofG-1;z%;^_)kmA=w`21ob&B)Go4~1eLX!|n*XkQ z_fS@y_aoH4>o=aVPfTU)OXgUidy(wV=Ur|O}P6WW3fXNUlCktq|g z<5OE^-Mw>ky0zsJ3GJ&7JkiWk1Ig{DPoDrulPw=7Coiv_CW=~)powk<%PKQ7v$*)T zz^{$<_kKJ{VIct>y4=iQ=sO&>N3KuQ;2Xn;br4Am9nWlf9x^d8L3R!f(t*vTOZW$! zZp$CgT*S}`)YjFF6vu{ygcKGQ0{e5T8b<@d^}bMhW)pE(BOox>f&Sogr>Cc9%f*h? z2i9aSZQ+{!`-U#G;l4e5;PyB34N_4+w*Q}m8Yh4FP#1}#U*@@a)8caSyQuH~k?z{LCE1O4 znJW}8@bR@M`lQZ8g7-sVh;Svk`hfHQjd`DxX?e21v{oRHvW!rae1ID{@}o>R&~a&X z=?(pO2eis)w{-i?gcOfEwM(TRG3()ha7`I3=Tlo7o5 z5v&y$7T|;a4V>Zgj`0U?LRe?rY^a#Q7cC@=BqWK22wzYMv z%uQxk;Uq*$(n)h^MdJ52q~5_ke$+t>ig|B14Eca})8xM~o7`d2W=NB|kBkzdz)S#r z?q{#c+TWWk5BNwUZEY6n=o1hM%B8+aLB2Fd`+Ue4=Sy@(*gDv9s-u_HT=s=z`CojsK~g)oG=-i1|Eh> zEOvKyf#)<2>6cll0@M)4D8Z`ob$Vd|<+s1m*VlLQks5TD(i_N1_TX--c?pO~Dqo+z zSUy7Y}VZ{E1LxPS(=I0i$mTr)B< zqM)P%AJh2Ns~1of7M7fxoHDD<6@a3or5k}J*;!ej!}HNLFp)2IE4z6}!p(-mKy`&Z z4n+SV(8q{6h&E)oXFoLZ&6Bo2MQ-QwVgKi9$WP=q4-N+PlmmBPb6=G*^zvvEa+-p6~kXlPzP2-)Jh9Rff>E5o~hjgUD3 znaq$K>iqm1jgGR%6%!H>UF_Qc3!X;Wr^DGPDK0ON>Wsue;{Ud_JpqF%E-wE5{SEdD zw_n|4CIbj#6}J6WAgaLO@cXfgTQg0dna<8m$1ndi!)pgH#L!nh3#akQ2G-j~JR&SC zmP=h}Ou?rmsi|XNiA59?JeV+3Qfdd*hC`ZmKaHuWIRVYA-YEm)2q6j!jgJI3)9ke> z#Gw2?_m}^zpS2We88CWWI>;7#zLr5H9ZL%l!wv8#uEax4Uzw~#LMnP$>yv22F6@Gl z$`njt@_2YD4(ow`9n9x&iXS*_D6S@A_#Y>moYL1dvRg=O$jCjyzDZA)@JN2WfsmRjR!wYp#8w zjhk-8A%R)yo6Kb5To@1sk?*yBDTfMVqbv%z?@qyr zrtdKk!+)EWKm1xn&-QAAKM}>joLI>Uzj5?O8l6S1R#R3TQ_!V%{?{V$?kqmWb%kzj zT%hI2km(yv4EH-wL11&6#N)Dzc=X@n|E~(yNSpcg_DoY9!N63w6pqAb=0#w{T1U@q ztHo7S70OlTX9zEN+Jy+=<&y3Ff%@j5=73?dGQN$;g9=RE&Hvd5zH86*jy*}=M9-9d zSFPlYRZtCK4pEuMK8F@N*L(QU)B{b{pjVJ@Nxc0AojL|+)AZUODmxl4E8^M#Fl-xN zi3T%Ed!m|jVIKy>n=l0(2)?ofIRWl*=G9+62Q1VYdU!cGs6?=3oT@;;A8F$lG3KMg z{VmCqr$Ug#v(3bJt){uSj98S5LlrCXE6k6w%qFg|DNCcb@btS#RQ`^pLhMT**#2p`wWO!7)pUvF0`>sgIdq%~t=HaL ziyPFxCvq*lWCQJ#o6)C*{+ND%Rw z1*9-T0j*M(sY<*0_D=!=YPZX?07J%$ZZHXwgD5gdr8Ac%k<-GfZU+Ip7 z!Lvcc(F!FaLz67@ZE25gM3R)mrbt#AG5H-;D#~7w)wWawwIo9Lfo(*zfGr1;mm(RUpyqW|r zzFfHq0Z32DMB|I5r32+-BQXBXm+9{|L`aX1ULNHKoLO+ES?_(}8snAvRn5DMN|F{XO-xQVbo< zua#99Y9Z7M0Qy72uSiRhzsB3-F{yLhv6B0<6yU43J@Yd)83ubV?xPlPm;CHg0R{2% zF=t2q;!yjJ8d>neMahlH!wSiQI^vUcm6f&XvszJZ^$WKte+RIVzd@ETrCepSZqnDz z?+C~q4R|as550fJVDQiZ-&lA%Tf>|PXulFvj-Gg|K{r4P?LToe_Xiaozb7e zGS`%{mAb{LmmCmN6**e+V!yBZBOB%uvK@m|`TRonEKaX0B2%Jg(gjtBiWayYI=C)uEBCVvoW= zLq}mFVScGMj|S?(H0_Ev(r-b(pJYtcM10lFheiN=`fRcI78KR~x%Nj#cPtK*bO?8H z67W+z4%C@Etmh&j?;Tw_yZU(FpYG$^Rk=Xp=Z_n5jDK#S&KcFq`RnVo+YdI%q*fi* zsN1yvz+*FVO4hAVa73i5 zf2sXdznI$A(VeGl43Tv>H#7TbTDx0&h4Db_7QVjdY+8~>nKIja!wu3g(Oojb>Rm-K zFc4u5&`Qds(ssr5--iNbE~JfRA9yLuN#O6TbCMB1H@ds;=)1eL(?E=X_QecQEJv%;T}VwY~JS#K5svoln|6&$C$Oi|m2$nQA>=bus(#t-BOK55^iSyK+Y-;G^8jDPhdD!;J@-Sw+h$t+U@oYD$wb9hU~% zdbf*3{e|~<>PQyHVERquG__`!uoe#@hTS$E$x&7Dr99&U8H=+}(Fx7-t?m z!pgXnB^?@>x4!?@(citi^b*PgamtzP-t9!Xa;X7O(K6vlX89p51SWYdqk}*@NgzvN z9UVTTQd%$TZ36g!5zYGfTHnj4C+66BfhK|qO0~#csbebVc!Az|^JU|=(8S>;hw+E;<+{hv0p`(VyQ*!VJkUHzKB-0kg})sO`-=@r z)i&dbhwroIR(6d$yRXSpM&cA%z1-4D(W29+M$l36$=Qe^hQArVe}yrF z=@rRg?W_xWBh+yHif}iZau*F<`ufabPvmx0xob+WV%qb?v{^O`Vuf;Poa2>^shLvg zymX0cfV{4CLuYt!Bqjdu>PxpG6rOE?dy=OM$#-^O{^A4PF;)=ot)D`Xgn*R=JH1I8 z6}6FVE|b#1mJc!TUjHQ{l((PAMLQ#8t+@^WhU5fq#*pSt3jygEs|L!mCn~DJREynn zNCrt*qRMRX*=Uk>db`<7w!NSLn-e|~CuAEl-xzO#G46xbX$RB~f9HGpJgG(cNw_@7 zlNnrS7tJ=AE;cH6VjcSm)S16s_g6ClTXtMu}9M5~NM z(!PA~-y6ZNdyB&Q{hL+oXn0B60@naYSLwIc z)lo*+S$ewVAfolSynykyE^YgWOUAV>WbpyiIc;W*GU%j^ZU&4P7YUz1ky=r&pTE*+ zVC`vOT}pjME-P8iy+M)N$`eLLY0YXp=6n%NN5l=_m%p3do6*L~0ZvKKz}N$-;1Z&) ziLUm(`dXV4+sR8FomML@(c1yk+rqTlp@!P#Me8J4A%D7C%eLF`3J>|qc2!uTBO+=- zf-|yNfY>y-IX~RetxADi!G>;70ae3FMvd6-2t$~pGlC6dXJQH<#3*M_gjOs5mokvz zpZt>8TkWBTU(<*(PC7L7(B_gXIMn5bcQof%PKcJfQrx01u9LZ%Ik=28;oZaUp5;LR zl^qhHrR}MjTB%^o_PTE8I26LIvs$IB|M!wC34xuS1#}zL+Bx=&zmj_QEbv@O!KFuv z_VNIy_yFjn%c#--xrOG5=_nL`_v+vK*9oBJjeK^mK}W8zSGVQq$mRG2;N1T;_nnc3 zrli59Aimv!C76}0ijakoaf6iol3?}u>FZOuM4dP)IjQonox}c5SJ(d#EgK3C$zaf| zopytRxz6j5ithv1{<(KIF6Wt4h*!OPyfHPF%;?VMQ;7Ul*wy&IPas?atz!s%i{KB&^(kkq=Q2>L=}r81iz|mrfAr|W zY3r4;=eD=@SYPb++j+KXsvB~D`KahPXOZn2P~ZDjG=K|LS}MwhmqUCh>Gb@5Sov>p z^d7-)3D-&^lpOYXa{L0|Bi25=OeE-B(^wo-hU-EaWlr|ioH!GQ+SD>?rq`E8Q0^4u z+BgNI<&qX|z<^uFlxefF^53WLFCf)lU0LRK1~-gFhmO4rGWPsl`vn&wlRv z^!>YzE1y!1=$W*ft1TTlu$831v+y?akB;Qa8;d%6TA%$^RO)2d6Wgyv#Odm2M_ecJ z_4WIao8~xcS!?J(U3Rp!Jy5W6pUD~d_2#q#hxiH#zu7@XNYYMZFGL5;$;UP0XO zE>f;L9=7_vdJ4ZxkZf6BO86ojXCoC1be9%abT-NKN2;I$XjKF{vM7?BbEyUHzLe@l zw@Gd2^6GorTQ4uSm85Z1cB*n^2S>pa77ATwUfv`FO?`a^+G!k5iK^z^D+G6Ec75cs zZ*lt*1k=PPz+ft@{SzDm149(A-}`>9z>yUQ$%vsOm94T1gUH2Nt^>a zrPkKgUhOoIsi|stxaGar$7EJzRcfqkS*l|}WxTotTKjg@%H!a`G@e1+4*fO9(+yJk zl^Z$$;n+EjeNUXsudJk8S66#>766tF`fr7aFbH#_Q@R7xMw0?2XC6%v7IA|pDKW|U z!m|SG;a4PEL-p7h*{>b8ZiQW~A2BDNUFHp0T%23i*3;6^(xPCzZ_ZhWNXSX6nO=Uh zJefl&1BD(`h=-ePRC3-Gx(JMa)DqMn%eB#4ln_WLqDylU=N7hu4~&SBvv>>rni{S9 z9b>wf#O0NQjKjkoT_cWGUU^%3nw7P-TJ^k2k@%%*c`)J_yQRPc2`vc%i1;>SUx^t&D>ZSB{#wy8R< zar-NLeur-|U%b-S)?JM^WEG#WM9ZR--<+5KR`b%Vl7TlU8Ec%x{szi3e zq+jB?6lHE4s5>w?qYOrrW4&&MV3L~6$s<1m#J_k`^6(vjO;A@?SKa3tMu~W@L%o8E zWVYm1&HMqtxK4Byc?LWnN2$ViIi9rDx@jYE?!M#W;{oqNrZNM^{5M9L2f>lKyfj5p z;yX^qq)t*y8KtFB1N}?w92|H2K(r|-QnFzP7CxKZs3S6fh8uDXeH#~l_k!#mxf7-; zI-2;BhZc+e#-*d&l7v8Ohc!3j+;^j+l-^&vM#X{ZeY>->=Ry&=CgMy-Z>*=~wm42` zYs`?`#LZLXxT0d}yt&g}kcMG`kq|#Y7S0ae>8Lhz@C%*sIF^M(MMja`yzWaF2Bo5< zeE}^l=D9o!ZjRC$t5AL+d8MbegF5=v@1;7v3)RpzqsY>40f8Y|V!Veh%{@ax*zIO^mtg98f2M$Li#Y-OZ8Iy&0A+PLe4Hx7@E$>PDe zSS+tca%G8#r><^tU*FD3ib1#;q)}Rc#}sV9-YS}l{2K&^InCd*5ge`jN^Nd!ZOY`F zq{N-jX31ZbeCZPkzOwiHZ-TeFG&I=_dPW*L+QxIMa|z=p<}s@`b2B`A@Q#Ih+Oo|o zqG|P>h%c54_W-_{F?;mx$`!fnl7ebKr(x9t%jMesFJ_v;s$Pq zy)QNvl?#g~u@L&vSDj*D8(Yo;flyswt_bqhnQX4i{)um=sv^vJcjf$Y$Lf$NPlWbR zoF{|#AtXr&byhJYG0wsqamTO6^-72Z2M5ADTl&a^k)lYg`zeXR5yAAPrh!4w*1yOX z!BSw@;jr~a*iBOZ|D+3b5Nb%ghxgo5k_32C>k_e%Gn4Bbf}Yx9OVjR7&QERgYkRas zb92LyTvw*avo!1rsokZq?BKuIBnf$1CZXyjCrL#){zGen14=U20?z`(s{lS(&C#(h z)L9Mvc)2siXhIz=n>~1ZsruF`wJ+#ny_Gfy#5o7`X(aOcfhCOH;jzFdt*F@+VIa zht}5sRu75LRn+&z-E~~K=XSqdQqTFRNmmyhAYvkm|7u?YL~M>_k5|w`)&>z@Cx0IK z;TCuH&h4n4WH6E~q@^GnJZyZG7_TCHZ)y2efTq~b&j;4r6&(EOQZKUC2qC4U0%eWW znmYE?;#=2O+xM@AHq6iPkl(x>ta-U5u(kCxMdfyt^ka(h$Y{GCqY@N1ApjV=5x0N) z22E#V6rW_M76)?E!6D7&@oo3~K(@d?a30wy26Bo94rDy2}UkX{>xZeHBPvF$Hi`6I+y5D&tq0L5c z#xfn~Mk>XV%t^H+D-BK&Bv)1|j2nqhOl(+4;o&bp?Qaa-F~enPrtt9pxws(Zw+?Qc TsGt1|WC#>x)u6@FZ{Ghe!d@GJ literal 0 HcmV?d00001 diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..d0c9b4fbd732b5d6fa10a5c014fa56bc7bfc4010 GIT binary patch literal 4101 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 Date: Wed, 10 Sep 2025 01:49:15 +0000 Subject: [PATCH 064/211] Bump the npm_and_yarn group across 1 directory with 2 updates Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.1.6 to 6.3.6 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite) Updates `esbuild` from 0.24.2 to 0.25.1 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.24.2...v0.25.1) --- updated-dependencies: - dependency-name: vite dependency-version: 6.3.6 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: esbuild dependency-version: 0.25.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index d2a071d7b8..07c619bf69 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -18,7 +18,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.1.6", + "vite": "^6.3.6", "vite-plugin-node-polyfills": "^0.23.0", "vite-plugin-svgr": "^4.3.0" }, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 7df4196570..2a29df3eb0 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -25,7 +25,7 @@ "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.1.6", + "vite": "^6.3.6", "vite-plugin-node-polyfills": "^0.23.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 6d01895cb3..6cb69130c0 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -18,7 +18,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.1.6", + "vite": "^6.3.6", "vite-plugin-node-polyfills": "^0.23.0" }, "dependencies": { From 56c7bca29dd8184e14557eacd8ca3b378b9c1c44 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 15 Sep 2025 20:57:45 +0100 Subject: [PATCH 065/211] Update screenshot for new room list --- .../preview-bar-linux.png | Bin 4101 -> 4134 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index d0c9b4fbd732b5d6fa10a5c014fa56bc7bfc4010..63cc4cb5eca57a62cbf707e4a524fbb220e9c529 100644 GIT binary patch literal 4134 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 literal 4101 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 Date: Wed, 17 Sep 2025 15:40:10 +0000 Subject: [PATCH 066/211] Update vector-im --- modules/banner/element-web/package.json | 4 ++-- modules/restricted-guests/element-web/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 07c619bf69..87e214ab1d 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -24,8 +24,8 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", - "@vector-im/compound-design-tokens": "^4.0.3", - "@vector-im/compound-web": "^7.11.0", + "@vector-im/compound-design-tokens": "^6.0.0", + "@vector-im/compound-web": "^8.0.0", "framer-motion": "^12.4.10", "styled-components": "^6.1.18", "zod": "^3.24.2" diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 6cb69130c0..154291c820 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -22,8 +22,8 @@ "vite-plugin-node-polyfills": "^0.23.0" }, "dependencies": { - "@vector-im/compound-design-tokens": "^4.0.3", - "@vector-im/compound-web": "^7.11.0", + "@vector-im/compound-design-tokens": "^6.0.0", + "@vector-im/compound-web": "^8.0.0", "styled-components": "^6.1.18", "zod": "^3.24.2" } From d8da6652b8c413b6bb997130618aebc36eef3d5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 01:53:33 +0000 Subject: [PATCH 067/211] Update dependency zod to v4 --- modules/banner/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 07c619bf69..8bce4293ad 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -28,6 +28,6 @@ "@vector-im/compound-web": "^7.11.0", "framer-motion": "^12.4.10", "styled-components": "^6.1.18", - "zod": "^3.24.2" + "zod": "^4.0.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 6cb69130c0..3874fec0ff 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -25,6 +25,6 @@ "@vector-im/compound-design-tokens": "^4.0.3", "@vector-im/compound-web": "^7.11.0", "styled-components": "^6.1.18", - "zod": "^3.24.2" + "zod": "^4.0.0" } } From 34eb5e9a32a8bea6f25277763b23365032183aeb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:29:18 +0000 Subject: [PATCH 068/211] Update dependency @vitejs/plugin-react to v5 --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 07c619bf69..d2009dff85 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -14,7 +14,7 @@ "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 2a29df3eb0..fdccede221 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -20,7 +20,7 @@ "@nordeck/element-web-widget-toggles-module": "^0.1.0", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.0.0", "react": "^19", "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 6cb69130c0..cc06e2dacd 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -14,7 +14,7 @@ "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", From 552ef769d045f5f8cc639ea4cac8180cd72b833e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 21 Sep 2025 05:26:51 +0000 Subject: [PATCH 069/211] Update dependency vite to v7 --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 07c619bf69..2e45423cf4 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -18,7 +18,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.3.6", + "vite": "^7.0.0", "vite-plugin-node-polyfills": "^0.23.0", "vite-plugin-svgr": "^4.3.0" }, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 2a29df3eb0..ed3beb96b8 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -25,7 +25,7 @@ "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.3.6", + "vite": "^7.0.0", "vite-plugin-node-polyfills": "^0.23.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 6cb69130c0..c186385b7b 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -18,7 +18,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^6.3.6", + "vite": "^7.0.0", "vite-plugin-node-polyfills": "^0.23.0" }, "dependencies": { From 501bf9e7695ef785bc2496c0cf6ed6d339263296 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 23 Sep 2025 16:23:45 +0100 Subject: [PATCH 070/211] Make styling more consistent under test --- modules/banner/element-web/src/Banner.tsx | 4 ++++ .../banner/element-web/tests/banner.spec.ts | 2 +- .../banner.spec.ts/static-nav-hover-linux.png | Bin 3346 -> 3341 bytes .../banner.spec.ts/static-nav-linux.png | Bin 3347 -> 3343 bytes .../univention-nav-hover-linux.png | Bin 2583 -> 2512 bytes .../banner.spec.ts/univention-nav-linux.png | Bin 2597 -> 2515 bytes 6 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 81193fbaa2..92087743b6 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -21,6 +21,10 @@ const Root = styled.nav` border-bottom: "var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary)"; display: flex; gap: var(--cpd-space-3x); + + h1 { + align-self: center; + } `; const LogoContainer = styled.div` diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index fe60dfc3a4..8049f059bd 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -33,7 +33,7 @@ test.describe("Banner", () => { }, }); - test("should error if config is missing", { tag: ["@screenshot"] }, async ({ page }) => { + test("should error if config is missing", async ({ page }) => { await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); await expect(page.getByText("Errors in module configuration")).toBeVisible(); // We don't take a screenshot as we don't want to assert Element's styling, only our own diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png index 06f9925da1ae9d9f53ed1f9defb0779308f3754e..dfd945eff1d0a973fb955547cdd68080294468f6 100644 GIT binary patch literal 3341 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 TrnQn<_)A!t+nV8jb$|FT`}A4w literal 3346 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 T<*P-r()<@KVNl|4-bw!eb;(jH diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png index 4c19ec5b692250d19d24d5f7224944d42767cd67..3f0e72f4e7588f642bab9be9edc5b00a1440d9be 100644 GIT binary patch literal 3343 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 XxfMA1JEH5C{9mUnZIR_CJZ}FJ_DDMT diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png index 2c62995b7d27a7edbed008f8dcc2ac10eedf0bea..01bf1563e1095900838819ab480c766497404458 100644 GIT binary patch literal 2512 zcmc&$`8O0^AD%>!hDONOGE*Z&3R%b4<0}b?A)}!v`_iJZor$CjLbA(Jwz5WLkQimn zjCJf|DBI9r%wVjO*Er|>7vA@m`+3giJonz`b3f;~_eNj0GzA_LKL!8*fM!>WZvX&1 z`w*bR%e6n^j)eE^heMBUm>K~px+NC?0KOPAV?*1ZtR+g=wSf((cE-jbrmlo;*-~OT z*oUS9@3Wwf*#=~LEOz3Kxn2HZ^O*=Cq;~~)skx{bp$0rl?tuc{d??=ZPOZozAIs#s zD|+*S`wf^~bB0*F->kM{(mYy+^P)V#F=j zHmg<-IwHf72{5v~S;>EZPM_dyIsmBCfWsUR@Dy;I6MK2MlsVDh)c=40jX)WEs1GSF zpL6yd)>|vyqXjWr?13shVpBz8vX+|Mby^%PP2r&Moi&GN-x#H-2^Xs=>t^jqW=djq zOL1cCpkL+(`j3m7zh$pZye0+*?iNm7wEsJblUcVU#Q{7)U5s%u_x{}Q-^#mEzidL0d#P0Tj;XUD+8f_|{r+d`Zaigs zIR8R?tdtC}ARBJ9pyE@v8eX#6<7ofjtg3GCHn(}%;P&FnFE3pYK2<`Tk?DuC+Q23I&=h;26>W6m~(UI?vMuE?q24n)+&ztmi{`_blvc zw%7N>dyIcUpmJ?}45RH*0`^M5=)mHGL1DKG7vzy?d+*o$%00UTMp5Q9n?()#l+{erlPU?ZHR;n+#F)uFj(Pxh^Gaa2X_|wJx|&dJS9M|3G@6ntsCs?LK-=GJN$!eE*}MWu;X_w+xe)va<;6@m&<_U3|$;&T1Ba-4f7cvwTC zK9kmSiG8XylI35K)3sA&A@1HWCIc}j36^bux3Ff4eFrm8vU@{83SuYeplt|ZB!d+K zJgrIntCnD7(g}GePGpDQ>eNg{Iq+Akjz-4 z@*@I46y*NE5K+S|nfvWh*Ti3;)iROuHj2>9`myo}lmbU*q;LZyVs>?1)o{lsYgX5Y zIeTMrE5DIU5t@vR6`rG;&-RaE2vEauoD1zU=GMs~#Qc^>&#Rs>&N)MSlv1poM>$GpCk>{K5jA`0W&jzKr)*(Vh7M<%ktu&Z{nkrG@( z0MS-qU!h`6Irmeu#Ap`0l_t6CZ6Rw@fF+KjSxqjbeRN?zSB{BRUXgar+;lddZkPH)Kn&*{kKp~Bv+-6!=&D(F*iE>y z-jwHOoW6N~lEq9GVX4F+;VXZWC#`cV9W!9r;8j?rU(_! z%wH>%so+vzWM?McQi4+_SN!Elu%0z*?QmEwGCA-ZzP79rK^IbSa95nD^t8AEAqO5c z&Pd1M&oyf4(^nR5M3E3mLY`*Vx<)FWEE|(fJAFF06TV37g4-{bG14o7zX@R=>YQh% zN%@i@)>i-xY@Tab2R&maj-A(QZ;dZ~mDd)tRL@o<#;b|vwFy+~aby^*=rqan3O>hz zQ07$<{#SlxJlXDdmhcaHVmH6+bc91$lc|KGF855&eP1#G!#lP(x$qP3HUN_9b~)SI z2C7RP?K2XS{X3cmbV2#4tg}P`h(2tws&6ARU#yk+7 literal 2583 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≪k4I5(;1{L))%96a2C#*tgyz0eqE-USB zgtm+vJLJm_5si`e9)D#oD=(Dc%FJiTvTCr8SytSwb{>85nI92Ye-7kG=S9@vvLNQg_8@rn;G#-F;BDTN2^XLR%cu~yZf>{PCk!&LZom@*Cm;3d1 zi@lS8zYrRuO|~WNV{T@JrO$VJr~M^D+_Fv$D`vO79X6)iFZWHXhKL?L@P-j$W Date: Tue, 23 Sep 2025 17:06:57 +0100 Subject: [PATCH 071/211] Switch to zod/mini --- .../element-web/src/Univention/navigation.ts | 4 +-- modules/banner/element-web/src/config.ts | 26 ++++++++-------- modules/banner/element-web/src/theme.ts | 30 +++++++++---------- .../banner/element-web/tests/banner.spec.ts | 4 ++- .../element-web/src/config.ts | 17 +++++------ 5 files changed, 42 insertions(+), 39 deletions(-) diff --git a/modules/banner/element-web/src/Univention/navigation.ts b/modules/banner/element-web/src/Univention/navigation.ts index 96b035d3ed..7acde49b04 100644 --- a/modules/banner/element-web/src/Univention/navigation.ts +++ b/modules/banner/element-web/src/Univention/navigation.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z } from "zod"; +import { z } from "zod/mini"; import { StaticConfig } from "../config"; @@ -44,7 +44,7 @@ const UniventionCentralNavigation = z.object({ /** * It’s usually empty. */ - keywords: z.object({}).optional(), + keywords: z.optional(z.object({})), }), ), }), diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts index 1066a85bc2..4ab5fbb9a6 100644 --- a/modules/banner/element-web/src/config.ts +++ b/modules/banner/element-web/src/config.ts @@ -5,10 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z, ZodSchema, ZodTypeDef } from "zod"; +import { z, ZodMiniType, input } from "zod/mini"; import { Theme } from "./theme.ts"; +z.config(z.locales.en()); + const StaticConfig = z.object({ type: z.literal("static"), @@ -16,7 +18,7 @@ const StaticConfig = z.object({ * Alternative logo URL to display in the popover menu. * Will use the main logo url if omitted. */ - logo_url: z.string().url().optional(), + logo_url: z.optional(z.url()), /** * Categories of links to display in the menu. @@ -35,7 +37,7 @@ const StaticConfig = z.object({ /** * The URL to the icon. */ - icon_uri: z.string().url(), + icon_uri: z.url(), /** * The label of the link. */ @@ -43,11 +45,11 @@ const StaticConfig = z.object({ /** * The destination URL of the link. */ - link_url: z.string().url(), + link_url: z.url(), /** * The browsing context in which the browser opens the link. */ - target: z.string().optional(), + target: z.optional(z.string()), }), ), }), @@ -63,13 +65,13 @@ const UniventionConfig = z.object({ * Alternative logo URL to display in the popover menu. * Will use the main logo url if omitted. */ - logo_url: z.string().url().optional(), + logo_url: z.optional(z.url()), /** * Base URL to an Intercom Service * https://docs.software-univention.de/intercom-service/latest/architecture.html#endpoints */ - ics_url: z.string().url(), + ics_url: z.url(), }); export type UniventionConfig = z.infer; @@ -79,13 +81,13 @@ export const ModuleConfig = z.object({ * The URL of the portal logo.svg file. * @example `https://example.com/logo.svg` */ - logo_url: z.string().url(), + logo_url: z.url(), /** * The URL of the portal. * @example `https://example.com` */ - logo_link_url: z.string().url(), + logo_link_url: z.url(), /** * Configuration for the menu. @@ -95,17 +97,17 @@ export const ModuleConfig = z.object({ /** * Theme variable overrides, optional. */ - theme: Theme.default({}), + theme: z.prefault(Theme, {}), }); export type ModuleConfig = z.infer; -export type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; +export type ConfigSchema = ZodMiniType, z.input>; export const CONFIG_KEY = "io.element.element-web-modules.banner"; declare module "@element-hq/element-web-module-api" { export interface Config { - [CONFIG_KEY]: ConfigSchema["_input"]; + [CONFIG_KEY]: input; } } diff --git a/modules/banner/element-web/src/theme.ts b/modules/banner/element-web/src/theme.ts index aadc572c3f..5a13f27b9f 100644 --- a/modules/banner/element-web/src/theme.ts +++ b/modules/banner/element-web/src/theme.ts @@ -5,23 +5,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z } from "zod"; +import { z } from "zod/mini"; export const Theme = z.object({ - textColor: z.string().default("var(--cpd-color-text-primary)"), - subheadingColor: z.string().default("var(--cpd-color-text-secondary)"), - bannerBackgroundColor: z.string().default("var(--cpd-color-bg-canvas-default)"), - bannerHeight: z.string().default("60px"), - triggerWidth: z.string().default("68px"), - triggerBackgroundColor: z.string().default("var(--cpd-color-bg-subtle-secondary)"), - triggerBackgroundColorHover: z.string().default("var(--cpd-color-bg-accent-hovered)"), - triggerBackgroundColorPressed: z.string().default("var(--cpd-color-bg-accent-pressed)"), - triggerColor: z.string().default("var(--cpd-color-icon-primary)"), - triggerColorContrast: z.string().default("var(--cpd-color-icon-on-solid-primary)"), - menuWidth: z.string().default("320px"), - menuBackgroundColor: z.string().default("var(--cpd-color-bg-canvas-default)"), - menuButtonBackgroundColorHover: z.string().default("var(--cpd-color-bg-action-secondary-hovered)"), - menuButtonBackgroundColorPressed: z.string().default("var(--cpd-color-bg-action-secondary-pressed)"), + textColor: z.prefault(z.string(), "var(--cpd-color-text-primary)"), + subheadingColor: z.prefault(z.string(), "var(--cpd-color-text-secondary)"), + bannerBackgroundColor: z.prefault(z.string(), "var(--cpd-color-bg-canvas-default)"), + bannerHeight: z.prefault(z.string(), "60px"), + triggerWidth: z.prefault(z.string(), "68px"), + triggerBackgroundColor: z.prefault(z.string(), "var(--cpd-color-bg-subtle-secondary)"), + triggerBackgroundColorHover: z.prefault(z.string(), "var(--cpd-color-bg-accent-hovered)"), + triggerBackgroundColorPressed: z.prefault(z.string(), "var(--cpd-color-bg-accent-pressed)"), + triggerColor: z.prefault(z.string(), "var(--cpd-color-icon-primary)"), + triggerColorContrast: z.prefault(z.string(), "var(--cpd-color-icon-on-solid-primary)"), + menuWidth: z.prefault(z.string(), "320px"), + menuBackgroundColor: z.prefault(z.string(), "var(--cpd-color-bg-canvas-default)"), + menuButtonBackgroundColorHover: z.prefault(z.string(), "var(--cpd-color-bg-action-secondary-hovered)"), + menuButtonBackgroundColorPressed: z.prefault(z.string(), "var(--cpd-color-bg-action-secondary-pressed)"), }); export type Theme = z.infer; diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/tests/banner.spec.ts index fe60dfc3a4..22d57e32df 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/tests/banner.spec.ts @@ -5,6 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import { input } from "zod/mini"; + import { test as base, expect } from "../../../../playwright/element-web-test.ts"; import { type ConfigSchema } from "../src/config.ts"; @@ -39,7 +41,7 @@ test.describe("Banner", () => { // We don't take a screenshot as we don't want to assert Element's styling, only our own }); - const configs: ConfigSchema["_input"][] = [ + const configs: input[] = [ { logo_url: "http://localhost:8080/logo.svg", logo_link_url: "https://example.com/portal", diff --git a/modules/restricted-guests/element-web/src/config.ts b/modules/restricted-guests/element-web/src/config.ts index f5f03a3236..6fef66945e 100644 --- a/modules/restricted-guests/element-web/src/config.ts +++ b/modules/restricted-guests/element-web/src/config.ts @@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z, ZodSchema, ZodTypeDef } from "zod"; +import { z, ZodMiniType, input } from "zod/mini"; + +z.config(z.locales.en()); export const ModuleConfig = z.object({ /** @@ -13,33 +15,30 @@ export const ModuleConfig = z.object({ * must have the `synapse-restricted-guests-module` installed. * @example `https://synapse.local` */ - guest_user_homeserver_url: z.string().url(), + guest_user_homeserver_url: z.url(), /** * The username prefix that identifies guest users. * @defaultValue `@guest-` */ - guest_user_prefix: z - .string() - .regex(/@[a-zA-Z-_1-9]+/) - .default("@guest-"), + guest_user_prefix: z._default(z.string().check(z.regex(/@[a-zA-Z-_1-9]+/)), "@guest-"), /** * If true, the user will be forwarded to the login page instead of to the SSO * login. This is only required if the home server has no SSO support. * @defaultValue `false` */ - skip_single_sign_on: z.boolean().default(false), + skip_single_sign_on: z._default(z.boolean(), false), }); export type ModuleConfig = z.infer; -type ConfigSchema = ZodSchema, ZodTypeDef, z.input>; +type ConfigSchema = ZodMiniType, z.input>; export const CONFIG_KEY = "io.element.element-web-modules.restricted-guests"; declare module "@element-hq/element-web-module-api" { export interface Config { - [CONFIG_KEY]: ConfigSchema["_input"]; + [CONFIG_KEY]: input; } } From c956daf0c477ca8925fe03422b153b486cd3595c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 Oct 2025 14:25:16 +0100 Subject: [PATCH 072/211] Specify licenses more explicitly --- modules/banner/README.md | 11 +++++++++++ modules/banner/element-web/package.json | 1 + modules/opendesk/README.md | 11 +++++++++++ modules/opendesk/element-web/package.json | 1 + modules/restricted-guests/element-web/README.md | 11 +++++++++++ modules/restricted-guests/element-web/package.json | 1 + 6 files changed, 36 insertions(+) diff --git a/modules/banner/README.md b/modules/banner/README.md index c62890c417..bd18b28b25 100644 --- a/modules/banner/README.md +++ b/modules/banner/README.md @@ -43,3 +43,14 @@ The `Link` type is fulfilled by the following interface: | name | string | The name to render for this link | | link_url | string | The URL to link to | | target | string, optional | The `target` to use for this link | + +## Copyright & License + +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 832f733c7d..89f4f25f66 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -4,6 +4,7 @@ "version": "0.0.0", "type": "module", "main": "lib/index.js", + "license": "SEE LICENSE IN README.md", "scripts": { "prepare": "vite build", "lint:types": "tsc --noEmit", diff --git a/modules/opendesk/README.md b/modules/opendesk/README.md index a5fd105d0a..d313c219b9 100644 --- a/modules/opendesk/README.md +++ b/modules/opendesk/README.md @@ -1 +1,12 @@ Example module to load all the nordeck opendesk legacy modules & deprecated customisations as a single modern module. + +## Copyright & License + +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index e8bf689b0e..07400a564e 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -6,6 +6,7 @@ "sideEffects": [ "**/*.css" ], + "license": "SEE LICENSE IN README.md", "scripts": { "prepare": "vite build", "lint:types": "tsc --noEmit", diff --git a/modules/restricted-guests/element-web/README.md b/modules/restricted-guests/element-web/README.md index 75a29f2eed..3a925d018d 100644 --- a/modules/restricted-guests/element-web/README.md +++ b/modules/restricted-guests/element-web/README.md @@ -9,3 +9,14 @@ Supports the following configuration options under the configuration key `io.ele | guest_user_homeserver_url | string | URL of the homeserver on which to register the guest, must be running the synapse module. | | guest_user_prefix | string | Prefix to apply to all guests registered via the module, defaults to `@guest-`. | | skip_single_sign_on | boolean | If true, the user will be forwarded to the login page instead of to the SSO login. This is only required if the home server has no SSO support. | + +## Copyright & License + +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index c21d15d6b5..74be8f6a44 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -4,6 +4,7 @@ "version": "0.0.0", "type": "module", "main": "lib/index.js", + "license": "SEE LICENSE IN README.md", "scripts": { "prepare": "vite build", "lint:types": "tsc --noEmit", From b9792ef2e228a5828cde308e359dd04053e6af20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:54:30 +0000 Subject: [PATCH 073/211] Bump vite in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 7.1.6 to 7.1.11 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 7.1.11 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 89f4f25f66..6b62066422 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -19,7 +19,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.0.0", + "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.23.0", "vite-plugin-svgr": "^4.3.0" }, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 07400a564e..4c5fc04069 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -26,7 +26,7 @@ "react-dom": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.0.0", + "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.23.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 74be8f6a44..4d48c54ba4 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -19,7 +19,7 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.0.0", + "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.23.0" }, "dependencies": { From 58ea9f373f11dde10ac2d490607469681b2ac334 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Nov 2025 12:16:31 +0000 Subject: [PATCH 074/211] Delint --- modules/banner/element-web/src/Menu.tsx | 4 ++-- modules/banner/element-web/src/Univention/Menu.tsx | 6 +++--- modules/banner/element-web/src/Univention/SilentLogin.tsx | 2 +- modules/banner/element-web/src/Univention/navigation.ts | 2 +- modules/banner/element-web/src/config.ts | 2 +- .../restricted-guests/element-web/src/RegisterDialog.tsx | 4 ++-- .../restricted-guests/element-web/src/RoomPreviewBar.tsx | 4 ++-- modules/restricted-guests/element-web/src/config.ts | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 4649f64a77..50d3b10925 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -10,9 +10,9 @@ import { AnimatePresence, motion } from "framer-motion"; import * as Dialog from "@radix-ui/react-dialog"; import styled, { useTheme } from "styled-components"; import { InlineSpinner } from "@vector-im/compound-web"; +import { type Api } from "@element-hq/element-web-module-api"; -import { StaticConfig } from "./config"; -import type { Api } from "@element-hq/element-web-module-api"; +import { type StaticConfig } from "./config"; import Logo from "./Logo.tsx"; import TriggerIcon from "./trigger.svg?react"; diff --git a/modules/banner/element-web/src/Univention/Menu.tsx b/modules/banner/element-web/src/Univention/Menu.tsx index 7416c99232..c1b9ecc89c 100644 --- a/modules/banner/element-web/src/Univention/Menu.tsx +++ b/modules/banner/element-web/src/Univention/Menu.tsx @@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { FC, useEffect, useState } from "react"; +import { type FC, useEffect, useState } from "react"; +import { type Api } from "@element-hq/element-web-module-api"; -import type { Api } from "@element-hq/element-web-module-api"; -import { StaticConfig, UniventionConfig } from "../config"; +import { type StaticConfig, type UniventionConfig } from "../config"; import StaticMenu from "../Menu"; import { fetchNavigation } from "./navigation"; import SilentLogin from "./SilentLogin"; diff --git a/modules/banner/element-web/src/Univention/SilentLogin.tsx b/modules/banner/element-web/src/Univention/SilentLogin.tsx index 46e0f39eb0..cc2a0f7e1b 100644 --- a/modules/banner/element-web/src/Univention/SilentLogin.tsx +++ b/modules/banner/element-web/src/Univention/SilentLogin.tsx @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { FC, useEffect } from "react"; +import { type FC, useEffect } from "react"; import styled from "styled-components"; const HiddenIFrame = styled.iframe` diff --git a/modules/banner/element-web/src/Univention/navigation.ts b/modules/banner/element-web/src/Univention/navigation.ts index 7acde49b04..9bc413aa8a 100644 --- a/modules/banner/element-web/src/Univention/navigation.ts +++ b/modules/banner/element-web/src/Univention/navigation.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { z } from "zod/mini"; -import { StaticConfig } from "../config"; +import { type StaticConfig } from "../config"; /** * Univention Central Navigation API diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts index 4ab5fbb9a6..3a01e3e8ef 100644 --- a/modules/banner/element-web/src/config.ts +++ b/modules/banner/element-web/src/config.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z, ZodMiniType, input } from "zod/mini"; +import { z, type ZodMiniType, type input } from "zod/mini"; import { Theme } from "./theme.ts"; diff --git a/modules/restricted-guests/element-web/src/RegisterDialog.tsx b/modules/restricted-guests/element-web/src/RegisterDialog.tsx index ec82a54631..504f266600 100644 --- a/modules/restricted-guests/element-web/src/RegisterDialog.tsx +++ b/modules/restricted-guests/element-web/src/RegisterDialog.tsx @@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { FC, useState, type JSX, FormEvent } from "react"; +import { type FC, useState, type JSX, type FormEvent } from "react"; import { type Api, type AccountAuthInfo, type DialogProps } from "@element-hq/element-web-module-api"; import { Form } from "@vector-im/compound-web"; -import { ModuleConfig } from "./config.ts"; +import { type ModuleConfig } from "./config.ts"; interface RegisterDialogProps extends DialogProps { api: Api; diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx index db303d95c8..679818eb29 100644 --- a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -5,13 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { FC, type JSX } from "react"; +import { type FC, type JSX } from "react"; import { Button } from "@vector-im/compound-web"; import { type Api } from "@element-hq/element-web-module-api"; import styled from "styled-components"; import { useWatchable } from "@element-hq/element-web-module-api"; -import { ModuleConfig } from "./config.ts"; +import { type ModuleConfig } from "./config.ts"; import RegisterDialog from "./RegisterDialog.tsx"; interface RoomPreviewBarProps { diff --git a/modules/restricted-guests/element-web/src/config.ts b/modules/restricted-guests/element-web/src/config.ts index 6fef66945e..07a86902c5 100644 --- a/modules/restricted-guests/element-web/src/config.ts +++ b/modules/restricted-guests/element-web/src/config.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { z, ZodMiniType, input } from "zod/mini"; +import { z, type ZodMiniType, type input } from "zod/mini"; z.config(z.locales.en()); From 7f26ecd35fe5b8d5f61c896030d85c1b5c07394c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Nov 2025 17:28:14 +0000 Subject: [PATCH 075/211] Disable CORS to fix test --- modules/opendesk/element-web/tests/opendesk.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts index 5c7a79e926..7df5502fc7 100644 --- a/modules/opendesk/element-web/tests/opendesk.spec.ts +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -105,6 +105,10 @@ test.use({ }, }, }, + bypassCSP: true, + launchOptions: { + args: ["--disable-web-security"], + }, }); test.describe("Opendesk", () => { From c430d786e7418338c5bfc6837d976fcba4adb81d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 20:09:05 +0100 Subject: [PATCH 076/211] Pin dependencies (#63) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- modules/restricted-guests/synapse/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/Dockerfile b/modules/restricted-guests/synapse/Dockerfile index dc39a088c6..1e1d1e557c 100644 --- a/modules/restricted-guests/synapse/Dockerfile +++ b/modules/restricted-guests/synapse/Dockerfile @@ -1,7 +1,7 @@ ARG DEBIAN_VERSION_NUMERIC=12 # Now copy it into our base image. -FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC}:debug AS build +FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC}:debug@sha256:12dbb4f46c5f712fe3da1c7a441602ee91eb87a5d46b0e725b4440b852000538 AS build FROM gcr.io/distroless/base-nossl-debian${DEBIAN_VERSION_NUMERIC} From ae17873568e89f4de1ee5fb02eaaad8fd251f4a1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 Dec 2025 09:40:02 +0000 Subject: [PATCH 077/211] Fix loading compound --- modules/banner/element-web/package.json | 1 + modules/banner/element-web/src/index.tsx | 3 +++ modules/banner/element-web/src/vite-env.d.ts | 1 + modules/banner/element-web/vite.config.ts | 2 ++ modules/restricted-guests/element-web/package.json | 1 + modules/restricted-guests/element-web/src/index.tsx | 4 ++++ modules/restricted-guests/element-web/src/vite-env.d.ts | 9 +++++++++ modules/restricted-guests/element-web/vite.config.ts | 2 ++ 8 files changed, 23 insertions(+) create mode 100644 modules/restricted-guests/element-web/src/vite-env.d.ts diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 6b62066422..a513f03e46 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -12,6 +12,7 @@ "test": "echo no tests yet" }, "devDependencies": { + "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index f6baa9c896..447fbf4969 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { ThemeProvider } from "styled-components"; +import compound from "@vector-im/compound-web/dist/style.css" with { type: "css" }; import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; import Translations from "./translations.json"; @@ -21,6 +22,8 @@ class BannerModule implements Module { public constructor(private api: Api) {} public async load(): Promise { + document.adoptedStyleSheets.push(compound); + this.api.i18n.register(Translations); try { diff --git a/modules/banner/element-web/src/vite-env.d.ts b/modules/banner/element-web/src/vite-env.d.ts index ef030aec3d..bfeaf6b7af 100644 --- a/modules/banner/element-web/src/vite-env.d.ts +++ b/modules/banner/element-web/src/vite-env.d.ts @@ -6,3 +6,4 @@ Please see LICENSE files in the repository root for full details. */ /// +/// diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index f57993abbd..d7ae139b11 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -12,6 +12,7 @@ import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; +import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -31,6 +32,7 @@ export default defineConfig({ }, }, plugins: [ + importCSSSheet(), react(), svgr(), nodePolyfills({ diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 4d48c54ba4..130ade1ec1 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -12,6 +12,7 @@ "test": "echo no tests yet" }, "devDependencies": { + "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", diff --git a/modules/restricted-guests/element-web/src/index.tsx b/modules/restricted-guests/element-web/src/index.tsx index 564945ffe3..a28b5c6c5f 100644 --- a/modules/restricted-guests/element-web/src/index.tsx +++ b/modules/restricted-guests/element-web/src/index.tsx @@ -5,6 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import compound from "@vector-im/compound-web/dist/style.css" with { type: "css" }; + import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; import Translations from "./translations.json"; import { ModuleConfig, CONFIG_KEY } from "./config"; @@ -28,6 +30,8 @@ class RestrictedGuestsModule implements Module { public constructor(private api: Api) {} public async load(): Promise { + document.adoptedStyleSheets.push(compound); + this.api.i18n.register(Translations); try { diff --git a/modules/restricted-guests/element-web/src/vite-env.d.ts b/modules/restricted-guests/element-web/src/vite-env.d.ts new file mode 100644 index 0000000000..bfeaf6b7af --- /dev/null +++ b/modules/restricted-guests/element-web/src/vite-env.d.ts @@ -0,0 +1,9 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/// +/// diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 3663c873e1..24798ac03f 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -11,6 +11,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; +import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -30,6 +31,7 @@ export default defineConfig({ }, }, plugins: [ + importCSSSheet(), react(), nodePolyfills({ include: ["events"], From fe3e52b7bac9caa4c18d83fd24c8484e36c7fc41 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 Dec 2025 09:40:09 +0000 Subject: [PATCH 078/211] Update screenshots --- .../banner.spec.ts/univention-error-linux.png | Bin 8861 -> 8538 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 26520 -> 25409 bytes .../preview-bar-linux.png | Bin 4134 -> 4302 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png index 48f4242c8596de74960859779108a0506b33aeb7..64d098731f9a403a30ae288937c1103640898847 100644 GIT binary patch delta 6533 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 literal 8861 zcmeHt`9D8RFWiAvae+=V~NZd%SbAf5JL7MTSAsGm@(D} z!`#R=ma&g*#y&F!<2il*hv$dqdEMui^E%ggUDs!QzdzTx&gb1dWBubiXLvv$&~by? zHy?mN`z%4A{o_aX10(jK>^R`I&+mc$4N!5H#5@S}56Ix=^@jmz^wAK@hyIOwtK<)p z_hzf!O{HJ?)-hh0h1y>kImly~Wv{(C{Au)#dx(Nw=hfaNw|!3@Th-9hZ{f~)X@APH zdu^_5TUhV))pgwaxVz8~VZs9$)0QKy(~mHre1d`(FXf6a56pm@(o|`|ZsP=v_4I}- zRij?}*yv|?xPB$RriK%&=c1?gnid$|@bCDc17|@P=7<+c&n2gPWL|^f!OzuH{O8~` z!wDpwoLS;iV|%>f#YvFj9T%6rAwQbBG|0MUVPU~W39*%y_}RlWjM$yMd3v}&{tt+@`RY*pG|x*7es zB_>B-h?iuQwR3mp+rNKz9m%y=G?(=DruEgBnVP#sgQ`}$67eax{TWqhiYUl4mm8oF z$Kv;QT9aqdx9-JE-9_uU#B*){i!3cw=W@r0Jb7fz)G|tfk)m2G-a0w;d-+%)(qh2Y z>GB*dJZcxLmf}?{rFD=So?enqz=)URBYIj=L(5jEhZxXY3*o)Ou+B=nT`xCp6TZvW ziPs<3v0g1*E}^E?cDw)NT10dd_mj^0r2T6eD@>D!S|;af6j~qI^tMvGC?_YUpz!Gr z9<_|5Q%2_|#%Fz}hlK|Gw^9Dg>Ltz2oj>DE31(Pf_Drbw%WZMKTV@sojxpOEWLPHd zOP!24^Zka($W=MZGG~Lpz`#+D54W7C#1JPZr)A;%VxyQ-Xqm0)MaW z+8`8pZ>|b4RMwrXPn~4C;(rUFWhoT{^M59>*K1d)qlb7kcTC5;JoL z_4?4@S#LxR|DKs&_0r9zv-$b??1jP-CSx>chrQau3}7w|g={k=HGHQpxk7=IfAnag zA^b>TVPPm?qX;+F{`-FynXD7A;uyg%dERrRY89~NJD!rP>IN*cy*c1up>`GQfg6S! zE;1V3ulD18mIkkoiGO9}u=Xq7L!;5{@24%|L;{Gm8$FS7@^SKoDfF2)9z55FSk`;D ze(h~nP`I9*SDp~2w=ndu;qikFDG-m~;9y4G9`{LauK#?`1sH7ScdT@V93%N8mR8DI;p(7z8f@F=Nz?#qJ1a;}a7y%90H%360(;BIJTviaHI?oP|4B>37 zvRKQ5rA{|)+@QA_%E-uAD|POIqO-oleixs(OU`s~C}U%PMUL4rlVsgU?^88vt1Cl|&i>vFGTTbdEnd>h zTaNCx^&0q!Ii`jfyzEJ-FiKTR`1EOM7`Hs#77r*xr`FYVY&Hcc=9*-3*;6s0>zz;y z4UN_qFm4E;8^OORVGYFS%w@-G4<5vgdhG!@W@Tx~o=I}(N>|T1HUu7#tqVUI_slyD`^Y={d}xU<)1U zFh(H13+dhRspLq0Gcz+PX1mC3 z=26p|&Qz!}1mX`E3%DwyJdUCmy1O|5KnfnXkw1j!B9WGQ^T-K03+~mFiYt&uMTOm@ z^$DzgkLG=EEw{{mD_7?vAs(a%`qh9zGF@6bkpS@j{A&>?y z^7c#F)jW^g=!8<)Z1z>;t$X6J(UMv29>0P$%~4KF|oJL zpk4c`IQ_0okrtz>$qQ$w8N#Xf^|%1H@mkb=Zm=|)R@V2l2%irC0ZSef*OLC-)y1@( z>&fZZTBpIu%*B>0KN=7o6cdr>Ib1n3Gz9R2CC+PfqA3Ek1c0_RTGS!aQ-@^Z=&c; zXBBefH|M%_9~c`O*T%ox-DU-e7-(X4*ue6Ps;TH2dBEfK>kp}}<%bR*?uSeJ;755y zl{O^2y>R%kFq(#n@@kFl_3PIGP|}*utEu%nt5_BF$HfVP!I>XE@R2ovdc`E{`wEzn zoSY0isGW(*3}Gg>Idi7tO&&Wr85$Y_7~iAgsQogtfM;&sxx?XZFE=$c>FjRwzIgGX z1NP;``L^NVp!SM9S8`EuJ?8AQum(r>7F5+$zoT#9V+$k6hohCk8yxAmV+Sw+^4IME zcZE#H>f2+sW{yY&d|?i7-7-Te0TsyF+obgwWz1@1RaI3*MTL3MQ{#)~KP>NCTFxD7 z$iJTrR6vMZR~ki@`L`3R8bKRJO6TPFcTEcb*`@dRLN zxr-M+!-JR~&+0BAw*fXYNmGARJ~TNs6(a>t`TY6fJizYduAT4SzWr%kl#`Q##Md%i z4jBNXwmZ6vfj*zRhaVj)aj08bS}L}~tA)IBFU`Fh^nDCMmL++&(4l~p#)S5v)&ct< zMHmdGqOw-nDurL~(qhqvaRIB7N%nySsO7N?;hN|3JvoL+#E@;{H1$$|zin_{cBPMh zbtu;@EiWs}$;i0&eK&jb=n(*RAh=pRy}dxXr^m%jqLr@+%}h`K+3j(^strJoSL6@D zSIeXS2f)mpz&2rZ@>is&GShbGyIEl!ceCVh_yh~63IH470fzbWzl#zD+gWL{x3~At zJDZi2r42lM)s2wSLXvu42AbHs$Q9cSc#d;*suuU=AoQvnqWid1j zla!1M46N!fwRyaN;s6};-D11lcZR84CdH$~#lZ!R&H27yFBB=n1#7SF>ZZt+7x0@XN$Sw0vw#40)Zb13q4*qw&<{BVjYHG?O?-F1KK=g?T2{UMnfVg-N9Be(l zRy<#F0#{tH!|CXt5|-=DYo&|l{o4W5dXEvd>cb8UlnIQ_K3Cn@oPbDXN-n$ifcdQ$ z1S>A_=(X@-xr5hP*qWkUm;U)R`6j#3V#qKs;0SEP61)Izc{fcdfO!U=9&)t4^GmrOWy+l(R$ zq2NgV@%LBsLBI#YKL3K0GB`lIHPe+`7b?V5yx zzj=1bA?;Z3U4`n@$%idzmqQ)uBKHo#Dkgc|oNyQS9{2qOg;J$r5bBe#&G{eqboLli zP{}7fT~8&b%$pxGL+3uwTT7XI+Xl-O*NGH)=EN$7qgxZe4uhT zTM?~pnKfNO9IK}x7rN)fGy*NZY&BZY59li7wwP~L9yhd|YrpcEe@(Su+SIOp^MOh?+_%<4fgOS(P@RHSRDW0lJvGQgu_57AJwjXn zx_tr@CAA~ETKVdx2~X_#)uk^()wW7W&ZYi1WX(JIe+*>me(LAF+C3Gt#xEu&zGAvG z=QdBF9hE>oS$}*ug2E^p;_!>>K)1qS5$6-UB&ox$^lcKOhXcLXX$97CalUDiinW=m zA4H487Y%(W3jZfq!DrS|7E+NU%8Ut1nw=6XM;Vytncs$?F1HX5eW0mD-VaHnY0r#q^~nC6uXyb6 z@7EsjZZNT@iz1rAvz)Y^xX}*T<4d09NZvE*e!DK0B%C9&Bxpts@-eHAM7uWq)Ztpd=UCz49U> z#!CS?eL>G^xlAuFfasD`y?vYy>w<4NS;aG89E0YhBP<*@s;*pVXt&1&%=)z@u?>7@ zbBxhTTc0E^q;vS9q}=_N&~CF}*&>qP?dUYK_{SMRvf0goYOqW730UVRiM3-%x9oyP#AavRAnDC}cSUE^5Pz!-Z+9v;tvr<+ z9&t;5P$GHx0!&R!Zdj>6K^EQKeCSDa?$7&9EhlgZi~!k*d0H=zoza<8S_EPtTh)S2 zf*v|?M$Eb0$e7~0(U=u?B12qjrc)upe~{hgbgr}7gPI0+Lg#h}p3zcjca59Ya^DaV zI#-n2L;Ch^Dz5U=XKAT-;*v8K8xw0ZS6O=g`gFUqB#is+^V{gSyJ7dIWKF;VmVVK$ z)Y3rD*bBaY1_jjO;hsp+A6$=bpA%a*9ML{h5zy$gUMW0)q4IxIuy%NSJc{r6j8~vG zqqNkocWIx%{f5Net<;QAq#dJALag|Jp`Zgf7IOY)df~T!6rWuWd^BoX$txisd~wFj zOYu_t`MeNsPDXFp8-0CC@x20fL`Uw6>rPIbig;&!DNSuJ=M#RTLS`1LGYsy92+BFg z6Pd5BWh=Ps%#hWb*BPP8b+!1PBXlJlGxmHXJrj(o8TDcnq$M5R+CB{1V4ls}W+y&7 z%OyT4M@p7xS` z-2*rZ@zYlH!;j8H^hYr!mc`ZRPUNfO!1}k?+nnP8^OL84fRERQ4wgxf4-Utk$T+&D za0!L)Fsx%g?`U-h$i>3qO=??0?k#yy2HSN)HQLEk=+ZkDJY0iOZPw{LQ6Q}W_U=%B zz(D?$VIIY^!jjxrqvee-riL?l)>+5VlT#_48Zp7KtE&uxspy-5QL@g)(1$=FM}4*E zd3(3VB)=meO}X?n#5dS-R7x^|mo+nlA!4}M=CcV&qD0AEzzF+zor{0~hO|w~Yc}AQ zHNY>!bZu;qd46VHD5*wT)JVtPt8ArFtreZC*AYpLS)5s9Ej|;ER8}~%(nE7$tPzB} z>xh@@=3o`8vAn3F)y(&Xh#~sU;~8BphI=34m(!&z!oRY)4#t-KBpgH4eLY_3ZgJI$ zZP!xMcUqMAlM;SdkD`giS$75vD5QLP=&Xzt6;8Mp=id$NHAWNG*TZ= zOWF4vEFmywwIB*pXeRgtaWihT-mWtiDa^5BQns$Vi!h{L*wV|(QBz7vV`dwjPHn;r zDp|dZc#S^XE5~loCVE6ZHI}5<{7APgFw7;Ix#NZ-`ID-?y#dR{XZFS6+;&Gky?l(X zy||aJs{-Y|?RVk-aCGjxfpO4vWUMnUSYj?YtNaxo54Jc+Ss7oit@+z8s_$vyc~h-& zgR z-_nmc>P2OnyzX%E=t)$becCwc?Gp}VAv!kw_2OTDHPkk)4JiAU=*h?y>X%G}xHs4T zd%JFhfc4IC;6nEAeIA&FyYD{Ik}2stze*M> z&S*oZ8Sp1%cV$obeyTYiUmaf^);4u(Hr}ZJZS14O+F@1PEFaHZU`d{6PSc5!8xXeX zM1I{n*D9?6RQTT4V$bL=3Ods3KNWN>h;?XmL5%3M$&^*Zu%G4H z3I2+!wkU6p+doSl{!}b?#pJ7_Jfd}H>$Uf}28}V{7uF>AnEhBo5g1hI(s(0=kgBFu z{)?KW87|kk>uAy`aoy^^`7H+8uX$(Vcw6LbS2Vv`;lz=47xHL|r^QNnYOjToahjGI zsU%idxox-yPBM3^FQXx-Hs>~vE@I4-C$083?(fvhEJcPOJO7jvs;ffa4rPa5Tb7L9 zTVwLZFnaIn1@-ozW;H0UK8U$`gcfAJFWeUPd^(G#p0n*X4F_Mb+EIAmW!~QN(Bhkh z>`X#MoFTMdP()S5r*&bYK$?E~eweF%SKZR+Q;+By9%!J?kzoAyoOTgC*g?pxy>POjJn_C){?fQDmFubTlt)6V8=eNW9Kr6p1HT3}~J>}y^Y=o#LAUgh5rGz^vWPu9(r zMvT==o4no>OjWT`5GntZS*diSjhH|{Svm+1&6n@9{KLhKYPOV39 z)fQK&z)Jo zX$0(SxToG>5^%v=vso&BgCl>i=A(-iR#-x3GEGdpZv1@a`DW z1!0|D=H(U(ximqaK6ci%O+tj#J`zS`N*2K4>je&K53yzFprw>w+mevW$t+zt_Wv3z4m~9Gh7g%osM_eEW;kTD^MQa z^4j_iQU(r3dr`K2SLGW@@^VUw z!b(kP#8uPolfy>`-!TcXgcW9g!qkD1jU;5=cx*Vrx7vALRb{Esz+y^AI7#YHyy_9; z!~6Y|oVL??qB+&c%NSlq;7Ao3$OrPe%;lT}ex_bK3Of1UDa!vh=SRXQvJcQDVEDHe z;J-7m{|8S4zZVt)Ee$MoNDFKXXq#K7fTmzaL3TtX8bfn)c zP=))<%*^5NFrfEgMl*+4HMNKi^DWU@}93^ diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png index c28ec77d312082f7872ba395f41456b0c506b343..682cf7b206083e42f85ed4abfbcbafeb2e42e8b5 100644 GIT binary patch literal 25409 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 literal 26520 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 Uh1{v@a6p;EwvGp1S)YviSFRG7od5s; diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index 63cc4cb5eca57a62cbf707e4a524fbb220e9c529..17b2bd13c3b90a1922b93d46616c0b8af172178e 100644 GIT binary patch literal 4302 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 literal 4134 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 From 89bfa4047487d3d1d278598fd19d71068d0212ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 Dec 2025 09:51:35 +0000 Subject: [PATCH 079/211] Iterate --- modules/restricted-guests/element-web/src/vite-env.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/restricted-guests/element-web/src/vite-env.d.ts b/modules/restricted-guests/element-web/src/vite-env.d.ts index bfeaf6b7af..c718659504 100644 --- a/modules/restricted-guests/element-web/src/vite-env.d.ts +++ b/modules/restricted-guests/element-web/src/vite-env.d.ts @@ -1,9 +1,8 @@ /* -Copyright 2025 New Vector Ltd. +Copyright 2025 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -/// /// From d4392723ae490ba925adf884a2293c50e58998b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:14:54 +0000 Subject: [PATCH 080/211] Update npm non-major dependencies --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index a513f03e46..3b57ac1d63 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -21,7 +21,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.23.0", + "vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-svgr": "^4.3.0" }, "dependencies": { diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 4c5fc04069..57511e311b 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -27,6 +27,6 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.23.0" + "vite-plugin-node-polyfills": "^0.24.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 130ade1ec1..00e82e31f0 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -21,7 +21,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.23.0" + "vite-plugin-node-polyfills": "^0.24.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^6.0.0", From 335bf9d947717fb305b189b828e68360d8d760f4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 11 Dec 2025 11:16:42 +0000 Subject: [PATCH 081/211] Update screenshot based on compound design tokens update --- .../dialog-linux.png | Bin 14020 -> 13313 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png index 74f69152f6d3ea0cde6eecabaca6ee8ca4c53359..b95326afceafe9bc2cf2b42f53ecb0b49b9c5ece 100644 GIT binary patch literal 13313 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 TsGt1|WC#>x)u6@FZ{Ghe!d@GJ From 03958a054c3e7909afc12fdd02bf4ae475623032 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 17 Dec 2025 18:01:21 +0100 Subject: [PATCH 082/211] style: set font on the sidebar --- modules/banner/element-web/src/Menu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 50d3b10925..03bb37c686 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -27,6 +27,7 @@ const Sidebar = styled(motion.div)` width: ${({ theme }): string => theme.menuWidth}; background: ${({ theme }): string => theme.menuBackgroundColor}; border-radius: 0 16px 16px 0; + font: var(--cpd-font-body-md-regular); `; const SidebarHeading = styled.div` From ec74fc8235ccf3c5329d6d0042cec7f475db9d98 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 17 Dec 2025 18:13:52 +0100 Subject: [PATCH 083/211] style: set font size on preview bar --- modules/restricted-guests/element-web/src/RoomPreviewBar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx index 679818eb29..1d3317a4b0 100644 --- a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -25,6 +25,7 @@ interface RoomPreviewBarProps { const Container = styled.aside` margin: auto; + font: var(--cpd-font-body-md-regular); `; const RoomPreviewBar: FC = ({ api, config, roomId, roomAlias, promptAskToJoin, children }) => { From 056ef3656a43e5e8046825b7153b336d31eec606 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 17 Dec 2025 18:15:10 +0100 Subject: [PATCH 084/211] test: update screenshots --- .../preview-bar-linux.png | Bin 4302 -> 3921 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index 17b2bd13c3b90a1922b93d46616c0b8af172178e..b402a642ad764a6e91a9d249034f9af9961990f3 100644 GIT binary patch literal 3921 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

e zAU00t7#JMV6}<(*ETygW=jqBcz%tk(Qy@$L zXo_f7-opbnzt#i0(uuPFn4bO%*xBsf{nBs8KZuOn+$Xi`hP}_iU{_jKcoOV*igbmn zRS#hK!_;H{txkCDFxbHye)SME0$?$0Ut9r(u-6so_wPRdJCA+)_Fez=0L#}-WczfE zsHolDogKS&?YQ-i#_aIL?IZVx6t_9++5Fvi@*3~+sq6NwhLS?TBJLv%{>+ov8IW( z>>*S?b@2JjOiRlzFZ`cr8+_^@FHciYaQvUSPx~+|D+&3Jt-SyKb?ex;ePSZyKDLsO z@S04Tn3ndl@W)oRo@JkVdie0+g9i^;^|`0Ld-uSB!SLsv!h~7E-v9sr|Njk#2%`W1 f00v1!K~w_(C<+;IVsU}n00000NkvXXu0mjf!?B4Q literal 4302 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 From 6c38cb076849d92e40eac2f6cc17eb8487943787 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 17 Dec 2025 18:26:08 +0100 Subject: [PATCH 085/211] test: update screenshots --- .../banner.spec.ts/static-menu-linux.png | Bin 13121 -> 12708 bytes .../banner.spec.ts/univention-error-linux.png | Bin 8538 -> 8218 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 25409 -> 25415 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png index 9970b9ebdec6c8b4fd916936811d1980e0cacdd7..e6e47a126002dfe46ef192060a8866f8ea713261 100644 GIT binary patch literal 12708 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 diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png index 64d098731f9a403a30ae288937c1103640898847..5d1e4d92bea2179108e9217073e0f872004b632a 100644 GIT binary patch delta 6192 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

@b?Q``I7Aa;ZL-bUT*H<^_wCy^c<^8W9bjXA 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 From 39a61cdd1f2418cf6d174a88a2e6b5d677dc3d26 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 18 Dec 2025 10:41:45 +0100 Subject: [PATCH 086/211] fix: set font spacing --- modules/banner/element-web/src/Menu.tsx | 1 + .../banner.spec.ts/univention-error-linux.png | Bin 8218 -> 8472 bytes .../banner.spec.ts/univention-menu-linux.png | Bin 25415 -> 25423 bytes 3 files changed, 1 insertion(+) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 03bb37c686..1fa20bd815 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -28,6 +28,7 @@ const Sidebar = styled(motion.div)` background: ${({ theme }): string => theme.menuBackgroundColor}; border-radius: 0 16px 16px 0; font: var(--cpd-font-body-md-regular); + letter-spacing: var(--cpd-font-letter-spacing-body-md); `; const SidebarHeading = styled.div` diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png index 5d1e4d92bea2179108e9217073e0f872004b632a..96a8a1f80238bb009da09e24c4ddff5ce99682c3 100644 GIT binary patch delta 6457 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 T`gFqI00000NkvXXu0mjfc%YV` delta 6194 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 QegFUf07*qoM6N<$f)eo&IsgCw diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png b/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png index 646c92dbc1c76af560b9b92b113cec8ce0c77ad9..0467cd03b27f34d5a873727ff716f87225581bf8 100644 GIT binary patch literal 25423 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 Date: Thu, 18 Dec 2025 10:52:10 +0100 Subject: [PATCH 087/211] fix: set font feature settings --- modules/banner/element-web/src/Menu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index 1fa20bd815..db46c76a82 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -29,6 +29,7 @@ const Sidebar = styled(motion.div)` border-radius: 0 16px 16px 0; font: var(--cpd-font-body-md-regular); letter-spacing: var(--cpd-font-letter-spacing-body-md); + font-feature-settings: normal; `; const SidebarHeading = styled.div` From 929a1d4d000fbe6151935fcd8ceb125a5c713e50 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 18 Dec 2025 11:38:40 +0100 Subject: [PATCH 088/211] style: set font spacing and font feature settings --- modules/restricted-guests/element-web/src/RoomPreviewBar.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx index 1d3317a4b0..75a2b9741e 100644 --- a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -26,6 +26,8 @@ interface RoomPreviewBarProps { const Container = styled.aside` margin: auto; font: var(--cpd-font-body-md-regular); + letter-spacing: var(--cpd-font-letter-spacing-body-md); + font-feature-settings: normal; `; const RoomPreviewBar: FC = ({ api, config, roomId, roomAlias, promptAskToJoin, children }) => { From 0783237f5686bcda289d9428861d48a867a53de4 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 18 Dec 2025 11:49:32 +0100 Subject: [PATCH 089/211] test: update snapshot --- .../preview-bar-linux.png | Bin 3921 -> 4055 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index b402a642ad764a6e91a9d249034f9af9961990f3..1771b8e67d756bdab76549a8b0fe543af2a71d07 100644 GIT binary patch literal 4055 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 MqH-eDLiz#!4>{+5umAu6 literal 0 HcmV?d00001 diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index 1771b8e67d756bdab76549a8b0fe543af2a71d07..f556a718060ad9b4e345320a45276b49af262163 100644 GIT binary patch literal 5179 zcmV-B6vXR^P)K*)c|pDKO>` z17{c*#NjcN6eupp${CrCEz^kwX)HWrBwAOZbtTVqB;Pbz{wPcAvRoUMw5O$+8GCp9-dg)19*7ifL9=%xZo9tCoXse;)x4h zfq3HL3lILV;I7Nf%s2bP539unEPCla{ZNPnF)?G;i}#gXnFBnrvEb>@?#roiP#YJC zS4NM?7eFi^7?CLvX9`f{aceAiPv&M7_65UWM?+sRW{XRPXP&||R&|ZwLZL8Af}*1^QkOy;7VBIXkNM=ZW_c!hI@3(}-`ar(5pS)KRxTn|LX!+yU#dA!mP8 zNu*Y;#?Ul;uU44ySWf5)vWdN!(jtAG&Hb7ESZ<_gxVKs*-kT{dIzHqfef9=jZWjI$ zELk?>TfO)=BPu;SI#0VBW&fP1vy)sibN-7>Wrh1PiM`qRCC7W6-Oge_Y&gV;wsC^7yvux8$NkhCEZKF^B^s0xe{9Ll%A;05$|t_jE#`joH4ELdVy;* zr|vWJaNpebY08_1Ju~b&GHMzgtky_5QRW#;@cgW}wgvJ^-*GKBYXW~+Rb}zcg}J(t z@1FA0vz7y5Jh#EKu%TQYra&xu#_zGN3tU zi>_igglkMa-A#rpcD%t*KO9=eWi0x~r%;2{bz`haj?K9m3>E#6T0b!xvVglqS7VLT zVtid?x~`k3y0Ww9RJAh6*IriM+dc(__}J% zUOHWC^}TmOjHMYa_^F9jS@MQ*d58;R$#JU(d9CwwYi=s{cY!>EJy2;ICzS@_>XHdW z{!yb+m<-LPw$kXFG<&8QgD??13DQaDg_`PvazXNP1N8cX2ve?AbC*km87>lo1A1VgBhs?=wqg-1BPcT?q_$?* zNvZ@QEZ02dt3#5LBd1r0`W$+fhYBmO2YcB-E)P*4I^dxYp{u9gD{*}i2)H5N0CVe&X^%R!TI2Lsp#zeMN>jFJcex8DXc}Z z>4q$9rN?9jO;Sm6q9(n=8(!rZF znkgezBH2VtBqM;u8)DWd5RxL|)N~n?P-J13ZUrb-2F1owPKz}#LsP+4Gfj^+oH?&$zeUV`vot4W=9p9m04h=N@P%d!#gqpKKUr7ERNs#a>$ z!84{luvH*njW)93aL^9jt{+zxX3G?Z3e>90?G3(lDsLzkE(W55UJtZd%Re&nGy{@T zp}^pJ!r7oZa5oyA3wnaAK$f|MiiQ#*9AVickxVL!{1X#M7_P+RN7RxL$P6@%D~^AG zgoXzb3h`7tTZB--;N2rET<8>%`OpjC#{{0fER4D3Ay;DUFtphuAf$q|ZL{+M7r7F6 zF{BHcE}Xzv(m6>Hm3=*@>J6F#nOK0ZQE%F;nR__ozU=g_*=SfI{Zn@%{J9|X&M1y! zfV%1M17%x(UsG*~S}qf(KviMa>t!Rk?1AP?Eye8T8=C~ji?Ai1rNhYzO;TtP5mDiB zrM=Qr-(k|_q=MNAONRqd^%g!NxLF?rO+{HcFip*%D28uXERoRG6+4eK8k^>;b$K{5 z*;(Jet7}JFpXpsVW4ZIDS%?>V!cs%)l^fPTJ?qqV`rRa%wGKPK;AlG69TC5 zR#R%3R-D8JN6cq$VFyphQV}X0V=s*ijb2qm za_NKih8%8cMhxmqgr2mu|BNDz?iZqoYzl_Vdu-MTLN8#Z9JYQZxrEA>J4BONh9s#u z97EGLlnac3u-v*16H{rfIc9^^&`iVW4nxE>f>e3aK&!c?!T5tS@GneysB@q;Hxj2SmBm@KL7?Ln(+8ntH|BSCI)Z(FS_`4P>-T9(tGy1urAbJ7XI z*IO+oRS`iVB3-wIt*vT4X5-?hH1)}Tvo5AJz=4Vlm1=LT_M1Nw)?IRxi;LR&+2%@9 zMJcyx@f=gn@@2sqkThj^F=B5hX-oEpv z+qZAux$~#L{q1jnXY+XGnP*;n@de0>FFc=?miFqauSliYiAanO13f-l7z2Iq!3SgS zzUOjJPEGxWWtRZYlLbK%q}kaDd5)^^dr-B&*03cC^yfeS`A6M7L$=|+|NZZP=V=4& zP-*F*qi-6Xd+s@~*=(``{q?VZ{Yn48PX{c#huUs@=}TXF^N0be(U-scWw3c{rUHdR zq~S;zNlpWv?aJQ0g165OeD$j@flVT|hw9him%qHOQoqJ)&o<}%_d`%jzxnMAut{u= z0>NgXd4HTc=aPy8``m;A}8>3-Ii40)b#(-x&}q zF}kq1Ia;3koyA4|vTHYhqDh|XlgY`kJTMuf1?uhV=e1|oz%YwYpdcn@vbEJ3f9DL{BUVbj2AP{I zUwgb@n}J|i>e8cHY1)=QK(%{;VqIE_+Ob5fK-VT*PwpunBuzcwrT!SSmu5axGZKix z6P-=9U2S^Fu6v38i2=oW{nwsoYelUXsTa3?l6GTbW&qUABG}wNy^pvfA3h>0o3`Ip-C29*q%|zYD=8=HQ4|Sd!^}rSR z2e0cwx2Z65*=1pc-M&APp$mryBuUoh=Dc3r8>XI1%O#zsUt1?X43Z)%?RH%EhhW7U z4X@KG&cm5>V+0=dVu{aIewJyC!b=PUmfD z4xSi<8?^#GMGJ&VD=hm9(0jg>Y`&1ZqzP}rsu&^0U8XQN|XpfmNIqo6vO2^>bINa zYD3Fv`(fX&Rqp(KI2B&=j>wa+q+xR-6c^i#5fj(ODMB&<%{VD;^oZClL8> zC)k;oQc>qM6{Im_kD1B+B(mf>#q zJeVzxrN|iuQ`T9X*Ty~9tR?B(b}W~~iwQc$VZ-?T!K<$K&JWfVOK^l`R!I)QGDUDK zb2pvdSvJy^$LW8JJKCr~!X>&(*Y^A0=lmmiC6f0DO~DhxKF4GT0FhzXb^X%N=~|@_ z13O{2HfjrmVJUDc2y78ZhT+^;vy7dnRvo@HyEZZ6CU&GfZS7LyU@kb}o$^qV#OwN% z)JKb+_F}b9Riwy~h=mHBv_PjnY^&5Zb2j5{{RbOdH?h}x9dkM4?Jd2|`FrJAIzW9y zkqz3St7>(gOqeOjFV0mLSNr-)esIbMo*YoPAWEXPK&jZ4hH)&-aP}fqgmWT7Faei~ zj$MF1vZN;nLp(yET#jfH2x*=$5roO95FPbOg(gHP#leER>VT%CqS0&*-V-O9p?F=R zz-EVV~r!TCvdon` zOw3G(3d7D3#^=839KY)FGboXPK?9eNBFxulrAfiQhQkL6MG|s_000BzNkli^Z}O(z|*47H537W`HnV`Ih4yjgQ7;_YN(Q zzk500~)cI^2vvU0JWj!a5CfivZ8&TwCa-hgxJ=E^8$aNR203 zF3`L!X)|owJMo_@!3uo>ffbS z$!*@%US=?l`53%FtC68oon7v`ITtB+hpEpy2hWvAuE43@_OZL!=2-IQ)d$Mj>|O?! z!^U%Q+GB4h@0w!RS!Z+ENjrC4R;1Mw$*>uZ)o`SR%UaI(2m3}sVML(N7YOMGn-~kC3jar}gpZ?fFrWx-hCLQM<5Q zh2o1}{Nm^RrFSGn**0R)VjS?F*=FYv90>%36Z{>n0iCTeBG-;nq z>Yn^1_&meTySi#mg5xFmni2)~LwLqJ*xuSXI{T#J-OUGzHHO#bW62Lxpv?QcD-gH! zGi9kcXaTziC{_?Xv1}yf<$nk8>^hXnd=NdcD9{VfKVS93O|J@@RdCa)zWC4uOP@m?4VrH$ps0MD+V!dM=g_JDY9rXKotUG_I# z1w1>PSHJ!W6eWn2xRNVC7xq0>(FY&g20S~H7hinw$}cX*lRGi4$2n3`p0f<}@7cpA zo8FNmB8b&;CeF;8@jegqwXeP0()^!*X9v>R+41trA`l;&@IYBf$w6~l3)~0?JUav^ ztd9R`Qx~d0+-zEw)*Cl|cc}Eu4?p}6@N943e$U`feD0A6h!KCCY*d-ETYAM4elsMy+%t-Sm06_ p00960lDmvj00006NklUJ1b#xw_vtwjI^$kE110)hlJQsD4=jQ zf{PIm=7<8zEl^T0?ghuIgm5Z9x{ZmH*z9I)wplx;l^v<|@JDfetj)D$XnP)7wfowX zGz3giSe7yPqe#K_`_4P>^M2=fo_S~Hoo(NI^9@LlV6|)m2@tciqx z60C`YfD){UA3XlWj6;{h$uoMwE7hXBCY9iTs^3q6RWUW7;vXo#8UzVe$BeT@aUi>L zI1szYTF2-q`2koB2!?Yq`5ZQiJZ+5`*Xf*$f-YYe#5JL-+AMyFb!r2;v8ZW;#pQA{ zGFfQs^51`q78CY%7wpSE?Ic$&w#pF36xVc#L4u7UIbGV&D_yzF-)4-S;DKhmqfJ`5 zXfKZM%T{PxhP>bE@w*r_<7vvzC@>CQHmLIUaaen^3iN~i#Rq6a%P$w3h*?i_0eg?M z(;IcFRLI^It@0Ai&sTLiLkq=8zpbV6U@m_T&OIPi8wTA=Bi($X`WD^6Ec~Sm%`mJ> z`EA{mCDB?ra<#$wU9E7yX*#9M&%*a`1cfS{#qq8GnBR!O+F2##@8R$ZPxd6Na)5|zCg{0rC@(m`!S`h4m7MIf5lekj*lSko z%hg$|x`Wx=Jvcw7sM55s4}c1~t*4Jj1$%I42SG{WRsVdWp&M1%{DY0reZxi7=k<%5 zo{4NTTlcMdc%<(KR+*R9{vxOma$^+B~#;j*-0#%#sw?+*jENHJ&pXiR(def-R0FFjw zwK-agbhni&+is(>iq?*^Rgx5UbGfF?GxOa>MuDu|cHOLH-8LMOR@zwVo@>ra^+L+g z+BUiyzmS!_4 zX&k;j)~Za6>_cB9%MAOiRb`EKL~ginao|FWj2f(}eR%j$A=jvzeQMyQ%kuB98y7eI zXjpor?|q%RT*}d?sU2Kmu~|=F%Mg&az2g{ek^@J*x_Z7$=ti?jYr4zVv=3bB>#f}F zZmTi6Lg@;#`@K_qEKPmM6S#3k$cQ;ED}*qL7&AMOpSPYnlf#UB&KK>b4pmsj2#K1z zxMUKZcT6kcGN9X3RA_DEM&~4{<|c!cKoE}hT#GgnMsc~A3uI*z&H$N%lJR1ew-Ih%(pY4qfLqtM4(wv1DG^5*3R#9TyKX6^UL8*o?uSRhk3 z*s8>%`;(iAsuU)(Q_asuq8kw%Q;Km0z?GV!A%~f*l;^N9z?N63N9}DcFIkixyBjke zYrhW)uDv!hnv00CSd`pg8P^KrOKWjoYc#3_aBtb_GmPM*a$wL?%onm%z1O9Hfz20Z zk=KTsC%mMJ9sP`Qp+fHC`haNl75kYx#QxOlAVV4^b zLQ!qC5mGh}%Q23+bqIqIH7^%+53B6XKv;}D-b##dSwTP{uQPyfRXpax7fh~@i`blZ zLXQgwLDR8?;YAP>p+Y1D)xENUFc!X$10pC3MaD_kW0G+CrhQ|jyuL?^ruswhHza1G zuogk3t23~L78!UJ54HMyfgl3MgC*uZ`~_y88E~B30Wu#4kL`If5@G&t_zM7YE1tkm zA>Zu~$`RrVnQSt~6XL++@{>yhgrxE!GMxv-#TnR@JKg{#gu_M!Y$mgJDirYDw!0=t zG<#v>5d>pBIRjqWcXnPOJMzcWqH;+%H;c7UhRGH2kZVpKspUm>KI9p+IEi3r((M}e z1OU!ou9X<$vJC_sO|@@R%9e)5M7J?O<0Wy6&*)`oj|>5VaZ#|zpeQODeUv8#DS=cX z6v!lUneV*53#=CiSfWNM9QF;suZ+q zjSGQ)hJcO-<8oO{Ru&Hl_I&d$4M;Y+F|Ik34jaOOSer; zd+o$R;zbg6=(<=qMiI900AA76akf@1&lmF92=%=uZI<*s>~{>=TuUYz<_OQk!wf&@ zgV7mfu`m$0J?sIJrtU67O^Hk-EONZHwr&_HvRtSauArf0mP8}(FRQC+rZkc?0Mqj}GbuQGgpYz`(Gd+_*;Ziy zCkt_8h8S&aBY2f{Vi=caz$&`IKoJs##!vvDSUO*&68?Yz;&I?|K7 zOxlEiz}Oi*vs}SXp?rhJzB^d)DIpUHq+`^T!TymftpM>mtR|C(qy$gXG!~=p$>ika z=0+Q_S6+}{vZLj$6gagflsrTNzZ{MbP*noOl)$h6a9zJaUQFhwOs_PYf_0c4^sI#d?$auo$VJ*`+P*qz~ z5(&~t+}F;SPD`T$iSU%|CaR{g;kYG|N2SS5cN>+fItC)3e07D)RipUvuerJ_!y0~J zQ#aL6p+8X?Icizi`i}Y2f+Zkn5={{@P*>7Ci{e=lT_4PjVV@YqjYfZsvHCS=`N)Zg zQsnQVo~G#m(eiyqSi3X%s!m-IOv4xFKq+`Zxr8)V9h#)D-NL*^OQk$BVpWm8>6pI3 zT=LK7;m|8}DjCt}j8;fy=W2@w)wKngSZ?P#-Gwp6#9;oCOX1rsxS-3%& z9GnL_q;krv1DaJIIWb9GOii#R5&}xFCK4em!J0^Tpag3oA)o|nVv`6cOi?b^#QX36 z@$TKb_aFTE{{8z89{l->FTMcu;Mr%N-L-2cWY^9eX=!Qu_wN%3vXYTi-l;LVF&;4{w)wc#EpPp zx>HI%%Kn!Dy`j5+;7m{ctA+5S9c(>bc;SVszZrV*#TNmM zp>+tGot-^gdUR{52Sn$(RO(~@AEx!wytEe3TRnX~-yN`p@p^q-UFQLfp>GHa21BBp zpUlo~^3$#-f}$zTo8t@yO`l7luLtVv>Q1N!LXxx46o9VK)B{2HBm8l8d|e-1Kj8JU zo4>Q~*s;3j8P*q?dLWx^ETNu>8jYG!@WQ8%^!CQP9>PbQBOgd;h)D1?WW* zE_z*4n!8cYSVM!%&C$^%(@{XzZ`l8Kcllu1tv89lM`L-pOB}wxrO9~h8&^wU2`xtZujjRu?Wx%ItnNl3IWl-<5 z7y%o|R3HH82OUFLU~1zmg`^6L8++ezct0Tge{u}>)G7F>k(h;WH1GKWA32Po)NlX( zV8mE@to72E_aj(xthY2{y|bf@G)rvgD4>n=KuO%<_FiLAJ{R~LV@?8aC91Zb78#zj zOxa|vLZ`|E0iT~tX5jg{Iwdy+tS7W|%(N6xC^QW=1eAYJDPVxWL0Zt~UsalgWZd`fZ!*ZX{Et z?z(NBAOJX@gDvBOLVhBeP*WiyKt@+U+7u6ZeNX5pAPh?d>juL5S??_(@WB-|H|eR$ z1G=!TW?40K6c7_z$3^rgYBu6Gk}+O%l{*C6oeuvGlR=WA0(aanPi1CE_|eH8BrQ|I zX8=HW?u2MChqta~Sv7P#jmMblX(`fYcLhq)SqD2t*&YJr3fK$~c3yJ^<^bvGZyS|# zN)MjvM#t`uScXiD0smmD#X|xdY&7d@mesQ5JP@F!Z4DK=VIrK2WeOwe3A)YNTC*>* zROTEpQm3x6x&b#|AwMX_Nw-x~R%Z*);u-=yJrEsl=NK9u(Vln%Bv?#m$1e+I1%PJI zQ9vX)`%nD+@&3^JUzcZ|dFD_5dy9q-KGQLTp=inhp#UU!1ZWU?J~mI&d7ykrE=ceQ zv^5A#1(cij6OiB$B$7NpOK2*fojZ0^zHt&HhyqQ)q~$q0pz~urFqO_0|I76B|ADQ| z_U$kHZv1*m%KBcZT{ZN*76wykt>!q`@|@6WXsaGT^NXp6|4o_o%6_ng+5hrBXaqoG z=)SlD3}LTI3-8^#54Ik=cI~?Qn<1L7ok;iFIZ{)%n|ivpZQF9|Stlci*>s-%D{scZ z{XjqZ(cZ>}{{UNz*4CE2dwGEN#K#R~B_+kirbalO54HfXn=Sv@BrA}Dcp#ouX}xvp zkB3W-e){RBVDkd!dwSn`Gc%J9;)FO`=67l;aOCK5uh$1QBOJ~@S}vIBxrjn6;%}kx zxw$#>+ZX=pSDiC6B-ku480ZP@adqk8q@<_1(Uv~M>#GhvotbHG|JB?7ZP^5`IyiDf zRbGDd?-@^eF)S^K`z)!MSj1ld00960KUQ!M00006Nkl Date: Wed, 4 Mar 2026 10:32:58 +0000 Subject: [PATCH 124/211] Tidy up --- .../tests/restricted-guests.spec.ts | 72 ++++++++----------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index 341c023fe5..2478cc55ae 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -6,16 +6,14 @@ Please see LICENSE files in the repository root for full details. */ import { - MatrixAuthenticationServiceContainer, type MasConfig, type StartedMatrixAuthenticationServiceContainer, type StartedSynapseContainer, type SynapseContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; -import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; -import { type StartedNetwork } from "testcontainers"; -import { type Logger } from "@element-hq/element-web-playwright-common/lib/utils/logger.ts"; import { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; +import { makePostgres } from "@element-hq/element-web-playwright-common/lib/testcontainers/postgres.ts"; +import { makeMas } from "@element-hq/element-web-playwright-common/lib/testcontainers/mas.ts"; import { RestrictedGuestsSynapseContainer, RestrictedGuestsSynapseWithMasContainer } from "./services"; import { test as subBase, expect } from "../../../../playwright/element-web-test"; @@ -91,22 +89,6 @@ declare module "@element-hq/element-web-module-api" { } } -async function getMas( - name: string, - postgres: StartedPostgreSqlContainer, - network: StartedNetwork, - logger: Logger, - config: Partial, -): Promise { - const container = await new MatrixAuthenticationServiceContainer(postgres) - .withNetwork(network) - .withNetworkAliases(name) - .withLogConsumer(logger.getConsumer(name)) - .withConfig(config) - .start(); - return container; -} - // We do some wacky things here in order to run the test suite against multiple homeserver configurations const base = subBase.extend< { @@ -154,15 +136,21 @@ const base = subBase.extend< if (auth !== "mas" || synapseConfig.allow_guest_access !== false) { return use(undefined); } - const container = await getMas("mas", postgres, network, logger, { - ...BASE_MAS_CONFIG, - matrix: { - kind: "synapse", - homeserver: "homeserver", - endpoint: "http://homeserver:8008", - secret: MAS_SHARED_SECRET, + const container = await makeMas( + postgres, + network, + logger, + { + ...BASE_MAS_CONFIG, + matrix: { + kind: "synapse", + homeserver: "homeserver", + endpoint: "http://homeserver:8008", + secret: MAS_SHARED_SECRET, + }, }, - }); + "mas", + ); await use(container); await container.stop(); }, @@ -176,21 +164,23 @@ const base = subBase.extend< } // We need a separate postgres so it doesn't fight with the default MAS - const postgres = await new PostgreSqlContainer("postgres:13.3-alpine") - .withNetwork(network) - .withNetworkAliases("guest-mas-postgres") - .withLogConsumer(logger.getConsumer("guest-mas-postgres")) - .start(); + const postgres = await makePostgres(network, logger, "guest-mas-postgres"); - const container = await getMas("guest-mas", postgres, network, logger, { - ...BASE_MAS_CONFIG, - matrix: { - kind: "synapse", - homeserver: GUEST_HOMESERVER_NAME, - endpoint: "http://guest-homeserver:8008", - secret: MAS_SHARED_SECRET, + const container = await makeMas( + postgres, + network, + logger, + { + ...BASE_MAS_CONFIG, + matrix: { + kind: "synapse", + homeserver: GUEST_HOMESERVER_NAME, + endpoint: "http://guest-homeserver:8008", + secret: MAS_SHARED_SECRET, + }, }, - }); + "guest-mas", + ); await use(container); await container.stop(); await postgres.stop(); From 9829314c53c7981e072c4a7bcaade02d7560ffae Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 Mar 2026 11:59:18 +0000 Subject: [PATCH 125/211] Iterate --- .../tests/restricted-guests.spec.ts | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index 2478cc55ae..47ac837bc9 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -12,8 +12,8 @@ import { type SynapseContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; import { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; -import { makePostgres } from "@element-hq/element-web-playwright-common/lib/testcontainers/postgres.ts"; -import { makeMas } from "@element-hq/element-web-playwright-common/lib/testcontainers/mas.ts"; +import { makePostgres } from "@element-hq/element-web-playwright-common/lib/testcontainers/postgres.js"; +import { makeMas } from "@element-hq/element-web-playwright-common/lib/testcontainers/mas.js"; import { RestrictedGuestsSynapseContainer, RestrictedGuestsSynapseWithMasContainer } from "./services"; import { test as subBase, expect } from "../../../../playwright/element-web-test"; @@ -224,12 +224,9 @@ const base = subBase.extend< login_for_welcome: true, }, }, - page: async ({ page }, use) => { - await page.goto("/"); - await use(page); - }, }); +base.slow(); for (const auth of ["mas", "legacy"] as const) { for (const guestsEnabled of [true, false]) { const test = base.extend({ @@ -241,15 +238,18 @@ for (const auth of ["mas", "legacy"] as const) { test.describe(`Restricted guests auth=${auth} guests=${guestsEnabled}`, () => { test("should error if config is missing", async ({ page }) => { + await page.goto("/"); await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); await expect(page.getByText("Errors in module configuration")).toBeVisible(); }); test.describe("with config", () => { - test.beforeEach(({ config, guestHomeserver }) => { + test.beforeEach(async ({ config, guestHomeserver, page, testRoomId }) => { config["io.element.element-web-modules.restricted-guests"] = { guest_user_homeserver_url: guestHomeserver.baseUrl, }; + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); }); if (guestsEnabled) { @@ -268,10 +268,7 @@ for (const auth of ["mas", "legacy"] as const) { test( "should show the module's room preview bar for guests", { tag: ["@screenshot"] }, - async ({ page, testRoomId }) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); - + async ({ page }) => { const button = page.getByRole("button", { name: "Join as guest", exact: true }); await expect(button).toBeVisible(); await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot(`preview-bar.png`); @@ -287,17 +284,6 @@ for (const auth of ["mas", "legacy"] as const) { }, ); } else { - test.use({ - page: async ({ page, testRoomId }, use) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); - await use(page); - }, - user: async ({ pageWithCredentials, credentials }, use) => { - await use(credentials); - }, - }); - test("should show the module login ux", { tag: ["@screenshot"] }, async ({ page }) => { const button = page.getByRole("button", { name: "Join as guest", exact: true }); await expect(button).toBeVisible(); From 64d5bef3e72fc3b0ef444a3b745247a01463d6d6 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 4 Mar 2026 15:12:28 +0000 Subject: [PATCH 126/211] Migrate playwright tests from tests/ to e2e/ convention All modules now use e2e/ for playwright tests, consistent with element-modules-private. This avoids conflicts between vitest (which uses tests/) and playwright project discovery. --- .../element-web/{tests => e2e}/banner.spec.ts | 6 +++--- .../element-web/{tests => e2e}/fixture/app1.png | Bin .../element-web/{tests => e2e}/fixture/app2.png | Bin .../element-web/{tests => e2e}/fixture/logo.svg | 0 .../{tests => e2e}/fixture/navigation.json | 0 .../{tests => e2e}/fixture/opendesk/app1.svg | 0 .../{tests => e2e}/fixture/opendesk/app10.svg | 0 .../{tests => e2e}/fixture/opendesk/app11.svg | 0 .../{tests => e2e}/fixture/opendesk/app2.svg | 0 .../{tests => e2e}/fixture/opendesk/app3.svg | 0 .../{tests => e2e}/fixture/opendesk/app4.svg | 0 .../{tests => e2e}/fixture/opendesk/app5.svg | 0 .../{tests => e2e}/fixture/opendesk/app6.svg | 0 .../{tests => e2e}/fixture/opendesk/app7.svg | 0 .../{tests => e2e}/fixture/opendesk/app8.svg | 0 .../{tests => e2e}/fixture/opendesk/app9.svg | 0 .../{tests => e2e}/fixture/opendesk/logofull.svg | 0 .../{tests => e2e}/fixture/opendesk/logomark.svg | 0 .../{tests => e2e}/fixture/silent/index.html | 0 ...ner-static-config-should-render-1-linux.aria.yml | 0 ...ner-static-config-should-render-2-linux.aria.yml | 0 ...univention-config-should-render-1-linux.aria.yml | 0 ...univention-config-should-render-2-linux.aria.yml | 0 .../snapshots/banner.spec.ts/static-menu-linux.png | Bin .../banner.spec.ts/static-nav-hover-linux.png | Bin .../snapshots/banner.spec.ts/static-nav-linux.png | Bin .../banner.spec.ts/univention-error-linux.png | Bin .../banner.spec.ts/univention-menu-linux.png | Bin .../univention-menu-loading-linux.png | Bin .../banner.spec.ts/univention-nav-hover-linux.png | Bin .../banner.spec.ts/univention-nav-linux.png | Bin .../element-web/{tests => e2e}/fixture/app1.png | Bin .../element-web/{tests => e2e}/fixture/app2.png | Bin .../element-web/{tests => e2e}/fixture/logo.svg | 0 .../{tests => e2e}/fixture/navigation.json | 0 .../{tests => e2e}/fixture/silent-login.html | 0 .../element-web/{tests => e2e}/fixture/widget.html | 0 .../element-web/{tests => e2e}/opendesk.spec.ts | 8 ++++---- .../Opendesk-should-render-top-bar-1-linux.yml | 0 .../Opendesk-should-render-top-bar-2-linux.yml | 0 .../snapshots/opendesk.spec.ts/menu-linux.png | Bin .../snapshots/opendesk.spec.ts/nav-hover-linux.png | Bin .../snapshots/opendesk.spec.ts/nav-linux.png | Bin .../opendesk.spec.ts/nav-loading-linux.png | Bin .../{tests => e2e}/restricted-guests.spec.ts | 0 .../element-web/{tests => e2e}/services.ts | 0 .../restricted-guests.spec.ts/dialog-linux.png | Bin .../restricted-guests.spec.ts/preview-bar-linux.png | Bin 48 files changed, 7 insertions(+), 7 deletions(-) rename modules/banner/element-web/{tests => e2e}/banner.spec.ts (97%) rename modules/banner/element-web/{tests => e2e}/fixture/app1.png (100%) rename modules/banner/element-web/{tests => e2e}/fixture/app2.png (100%) rename modules/banner/element-web/{tests => e2e}/fixture/logo.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/navigation.json (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app1.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app10.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app11.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app2.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app3.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app4.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app5.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app6.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app7.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app8.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/app9.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/logofull.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/opendesk/logomark.svg (100%) rename modules/banner/element-web/{tests => e2e}/fixture/silent/index.html (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/static-menu-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/static-nav-hover-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/static-nav-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/univention-error-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/univention-menu-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/univention-menu-loading-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/univention-nav-hover-linux.png (100%) rename modules/banner/element-web/{tests => e2e}/snapshots/banner.spec.ts/univention-nav-linux.png (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/app1.png (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/app2.png (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/logo.svg (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/navigation.json (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/silent-login.html (100%) rename modules/opendesk/element-web/{tests => e2e}/fixture/widget.html (100%) rename modules/opendesk/element-web/{tests => e2e}/opendesk.spec.ts (95%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml (100%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml (100%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/menu-linux.png (100%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/nav-hover-linux.png (100%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/nav-linux.png (100%) rename modules/opendesk/element-web/{tests => e2e}/snapshots/opendesk.spec.ts/nav-loading-linux.png (100%) rename modules/restricted-guests/element-web/{tests => e2e}/restricted-guests.spec.ts (100%) rename modules/restricted-guests/element-web/{tests => e2e}/services.ts (100%) rename modules/restricted-guests/element-web/{tests => e2e}/snapshots/restricted-guests.spec.ts/dialog-linux.png (100%) rename modules/restricted-guests/element-web/{tests => e2e}/snapshots/restricted-guests.spec.ts/preview-bar-linux.png (100%) diff --git a/modules/banner/element-web/tests/banner.spec.ts b/modules/banner/element-web/e2e/banner.spec.ts similarity index 97% rename from modules/banner/element-web/tests/banner.spec.ts rename to modules/banner/element-web/e2e/banner.spec.ts index 2f8f3ac53f..eddc2d67e1 100644 --- a/modules/banner/element-web/tests/banner.spec.ts +++ b/modules/banner/element-web/e2e/banner.spec.ts @@ -26,7 +26,7 @@ test.describe("Banner", () => { for (const path of ["logo.svg", "app1.png", "app2.png", "opendesk/"]) { await context.route(`/${path}*`, async (route) => { const file = new URL(route.request().url()).pathname; - await route.fulfill({ path: `${moduleDir}/tests/fixture/${file}` }); + await route.fulfill({ path: `${moduleDir}/e2e/fixture/${file}` }); }); } @@ -107,13 +107,13 @@ test.describe("Banner", () => { await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { await navigationJsonResolver.promise; await route.fulfill({ - path: `${moduleDir}/tests/fixture/navigation.json`, + path: `${moduleDir}/e2e/fixture/navigation.json`, contentType: "application/json", }); }); await context.route("http://localhost:8080/ics/silent", async (route) => { await route.fulfill({ - path: `${moduleDir}/tests/fixture/silent/index.html`, + path: `${moduleDir}/e2e/fixture/silent/index.html`, contentType: "text/html", }); }); diff --git a/modules/banner/element-web/tests/fixture/app1.png b/modules/banner/element-web/e2e/fixture/app1.png similarity index 100% rename from modules/banner/element-web/tests/fixture/app1.png rename to modules/banner/element-web/e2e/fixture/app1.png diff --git a/modules/banner/element-web/tests/fixture/app2.png b/modules/banner/element-web/e2e/fixture/app2.png similarity index 100% rename from modules/banner/element-web/tests/fixture/app2.png rename to modules/banner/element-web/e2e/fixture/app2.png diff --git a/modules/banner/element-web/tests/fixture/logo.svg b/modules/banner/element-web/e2e/fixture/logo.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/logo.svg rename to modules/banner/element-web/e2e/fixture/logo.svg diff --git a/modules/banner/element-web/tests/fixture/navigation.json b/modules/banner/element-web/e2e/fixture/navigation.json similarity index 100% rename from modules/banner/element-web/tests/fixture/navigation.json rename to modules/banner/element-web/e2e/fixture/navigation.json diff --git a/modules/banner/element-web/tests/fixture/opendesk/app1.svg b/modules/banner/element-web/e2e/fixture/opendesk/app1.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app1.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app1.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app10.svg b/modules/banner/element-web/e2e/fixture/opendesk/app10.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app10.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app10.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app11.svg b/modules/banner/element-web/e2e/fixture/opendesk/app11.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app11.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app11.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app2.svg b/modules/banner/element-web/e2e/fixture/opendesk/app2.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app2.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app2.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app3.svg b/modules/banner/element-web/e2e/fixture/opendesk/app3.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app3.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app3.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app4.svg b/modules/banner/element-web/e2e/fixture/opendesk/app4.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app4.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app4.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app5.svg b/modules/banner/element-web/e2e/fixture/opendesk/app5.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app5.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app5.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app6.svg b/modules/banner/element-web/e2e/fixture/opendesk/app6.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app6.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app6.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app7.svg b/modules/banner/element-web/e2e/fixture/opendesk/app7.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app7.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app7.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app8.svg b/modules/banner/element-web/e2e/fixture/opendesk/app8.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app8.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app8.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/app9.svg b/modules/banner/element-web/e2e/fixture/opendesk/app9.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/app9.svg rename to modules/banner/element-web/e2e/fixture/opendesk/app9.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/logofull.svg b/modules/banner/element-web/e2e/fixture/opendesk/logofull.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/logofull.svg rename to modules/banner/element-web/e2e/fixture/opendesk/logofull.svg diff --git a/modules/banner/element-web/tests/fixture/opendesk/logomark.svg b/modules/banner/element-web/e2e/fixture/opendesk/logomark.svg similarity index 100% rename from modules/banner/element-web/tests/fixture/opendesk/logomark.svg rename to modules/banner/element-web/e2e/fixture/opendesk/logomark.svg diff --git a/modules/banner/element-web/tests/fixture/silent/index.html b/modules/banner/element-web/e2e/fixture/silent/index.html similarity index 100% rename from modules/banner/element-web/tests/fixture/silent/index.html rename to modules/banner/element-web/e2e/fixture/silent/index.html diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-static-config-should-render-1-linux.aria.yml diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-static-config-should-render-2-linux.aria.yml diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-univention-config-should-render-1-linux.aria.yml diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/Banner-univention-config-should-render-2-linux.aria.yml diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-menu-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/static-menu-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-menu-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-nav-hover-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-hover-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-nav-hover-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-nav-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/static-nav-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-nav-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-error-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-error-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-error-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-loading-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-menu-loading-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-loading-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-nav-hover-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-hover-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-nav-hover-linux.png diff --git a/modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-nav-linux.png similarity index 100% rename from modules/banner/element-web/tests/snapshots/banner.spec.ts/univention-nav-linux.png rename to modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-nav-linux.png diff --git a/modules/opendesk/element-web/tests/fixture/app1.png b/modules/opendesk/element-web/e2e/fixture/app1.png similarity index 100% rename from modules/opendesk/element-web/tests/fixture/app1.png rename to modules/opendesk/element-web/e2e/fixture/app1.png diff --git a/modules/opendesk/element-web/tests/fixture/app2.png b/modules/opendesk/element-web/e2e/fixture/app2.png similarity index 100% rename from modules/opendesk/element-web/tests/fixture/app2.png rename to modules/opendesk/element-web/e2e/fixture/app2.png diff --git a/modules/opendesk/element-web/tests/fixture/logo.svg b/modules/opendesk/element-web/e2e/fixture/logo.svg similarity index 100% rename from modules/opendesk/element-web/tests/fixture/logo.svg rename to modules/opendesk/element-web/e2e/fixture/logo.svg diff --git a/modules/opendesk/element-web/tests/fixture/navigation.json b/modules/opendesk/element-web/e2e/fixture/navigation.json similarity index 100% rename from modules/opendesk/element-web/tests/fixture/navigation.json rename to modules/opendesk/element-web/e2e/fixture/navigation.json diff --git a/modules/opendesk/element-web/tests/fixture/silent-login.html b/modules/opendesk/element-web/e2e/fixture/silent-login.html similarity index 100% rename from modules/opendesk/element-web/tests/fixture/silent-login.html rename to modules/opendesk/element-web/e2e/fixture/silent-login.html diff --git a/modules/opendesk/element-web/tests/fixture/widget.html b/modules/opendesk/element-web/e2e/fixture/widget.html similarity index 100% rename from modules/opendesk/element-web/tests/fixture/widget.html rename to modules/opendesk/element-web/e2e/fixture/widget.html diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/e2e/opendesk.spec.ts similarity index 95% rename from modules/opendesk/element-web/tests/opendesk.spec.ts rename to modules/opendesk/element-web/e2e/opendesk.spec.ts index 7df5502fc7..46d4506b32 100644 --- a/modules/opendesk/element-web/tests/opendesk.spec.ts +++ b/modules/opendesk/element-web/e2e/opendesk.spec.ts @@ -51,20 +51,20 @@ test.use({ page: async ({ context, page, moduleDir }, use) => { for (const path of ["logo.svg", "app1.png", "app2.png"]) { await context.route(`/${path}*`, async (route) => { - await route.fulfill({ path: `${moduleDir}/tests/fixture/${path}` }); + await route.fulfill({ path: `${moduleDir}/e2e/fixture/${path}` }); }); } await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { await route.fulfill({ - path: `${moduleDir}/tests/fixture/navigation.json`, + path: `${moduleDir}/e2e/fixture/navigation.json`, contentType: "application/json", }); }); await context.route("http://localhost:8080/ics/silent", async (route) => { - await route.fulfill({ path: `${moduleDir}/tests/fixture/silent-login.html`, contentType: "text/html" }); + await route.fulfill({ path: `${moduleDir}/e2e/fixture/silent-login.html`, contentType: "text/html" }); }); await context.route("http://localhost:8080/widget.html*", async (route) => { - await route.fulfill({ path: `${moduleDir}/tests/fixture/widget.html`, contentType: "text/html" }); + await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" }); }); await page.goto("/"); diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png similarity index 100% rename from modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png rename to modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts similarity index 100% rename from modules/restricted-guests/element-web/tests/restricted-guests.spec.ts rename to modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts diff --git a/modules/restricted-guests/element-web/tests/services.ts b/modules/restricted-guests/element-web/e2e/services.ts similarity index 100% rename from modules/restricted-guests/element-web/tests/services.ts rename to modules/restricted-guests/element-web/e2e/services.ts diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png similarity index 100% rename from modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png rename to modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png similarity index 100% rename from modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/preview-bar-linux.png rename to modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png From 30abf86c4b87ae162534367bf04bdaaa2f24d22f Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 6 Mar 2026 11:02:11 +0000 Subject: [PATCH 127/211] Add widget toggles module * API: https://github.com/element-hq/element-modules/pull/219 * Element Web API impl: https://github.com/element-hq/element-web/pull/32734 --- modules/widget-toggles/README.md | 16 ++++ .../element-web/external_all.diff | 28 ++++++ .../widget-toggles/element-web/package.json | 34 +++++++ .../widget-toggles/element-web/src/config.ts | 27 ++++++ .../widget-toggles/element-web/src/index.tsx | 64 +++++++++++++ .../widget-toggles/element-web/src/toggle.tsx | 93 +++++++++++++++++++ .../widget-toggles/element-web/tsconfig.json | 8 ++ .../widget-toggles/element-web/vite.config.ts | 51 ++++++++++ 8 files changed, 321 insertions(+) create mode 100644 modules/widget-toggles/README.md create mode 100644 modules/widget-toggles/element-web/external_all.diff create mode 100644 modules/widget-toggles/element-web/package.json create mode 100644 modules/widget-toggles/element-web/src/config.ts create mode 100644 modules/widget-toggles/element-web/src/index.tsx create mode 100644 modules/widget-toggles/element-web/src/toggle.tsx create mode 100644 modules/widget-toggles/element-web/tsconfig.json create mode 100644 modules/widget-toggles/element-web/vite.config.ts diff --git a/modules/widget-toggles/README.md b/modules/widget-toggles/README.md new file mode 100644 index 0000000000..e117e706e2 --- /dev/null +++ b/modules/widget-toggles/README.md @@ -0,0 +1,16 @@ +# Widget Toggles Module + +Adds room header buttons for widgets in the room. + +This module needs to be configured to control what widget types get buttons added for them. +The following config snippet enables the module and configures it to add buttons for both +custom and jitsi widgets: + +``` +"modules": [ + "/modules/widget-toggles/lib/index.js" +], +"io.element.element-web-modules.widget-toggles": { + "types": ["m.custom", "jitsi"] +} +``` diff --git a/modules/widget-toggles/element-web/external_all.diff b/modules/widget-toggles/element-web/external_all.diff new file mode 100644 index 0000000000..ed1bc76ecb --- /dev/null +++ b/modules/widget-toggles/element-web/external_all.diff @@ -0,0 +1,28 @@ +diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts +index b65e3ec..9d1ba49 100644 +--- a/modules/widget-toggles/element-web/vite.config.ts ++++ b/modules/widget-toggles/element-web/vite.config.ts +@@ -28,7 +28,14 @@ export default defineConfig({ + target: "esnext", + sourcemap: true, + rollupOptions: { +- external: ["react"], ++ // make sure to externalize deps that shouldn't be bundled ++ // into your library ++ external: [ ++ "react", ++ "react-dom", ++ "@vector-im/compound-design-tokens", ++ "@vector-im/compound-web" ++ ], + }, + }, + plugins: [ +@@ -41,6 +48,7 @@ export default defineConfig({ + externalGlobals({ + // Reuse React from the host app + react: "window.React", ++ "@vector-im/compound-web": "window.CompoundWeb", + }), + ], + define: { diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json new file mode 100644 index 0000000000..2a9c15e3ae --- /dev/null +++ b/modules/widget-toggles/element-web/package.json @@ -0,0 +1,34 @@ +{ + "name": "@element-hq/element-web-module-widget-toggle", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "lib/index.js", + "license": "SEE LICENSE IN README.md", + "scripts": { + "prepare": "vite build", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", + "test": "echo no tests yet" + }, + "devDependencies": { + "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", + "@element-hq/element-web-module-api": "^1.0.0", + "@types/node": "^22.10.7", + "@types/react": "^19", + "@vitejs/plugin-react": "^5.0.0", + "react": "^19", + "rollup-plugin-external-globals": "^0.13.0", + "typescript": "^5.7.3", + "vite": "^7.1.11", + "vite-plugin-node-polyfills": "^0.25.0", + "vite-plugin-svgr": "^4.3.0" + }, + "dependencies": { + "@vector-im/compound-design-tokens": "^6.0.0", + "@vector-im/compound-web": "^8.0.0", + "matrix-widget-api": "^1.17.0", + "styled-components": "^6.3.11", + "zod": "^4.3.6" + } +} diff --git a/modules/widget-toggles/element-web/src/config.ts b/modules/widget-toggles/element-web/src/config.ts new file mode 100644 index 0000000000..54e2d075a4 --- /dev/null +++ b/modules/widget-toggles/element-web/src/config.ts @@ -0,0 +1,27 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { z, type input } from "zod/mini"; + +z.config(z.locales.en()); + +export const WidgetTogglesConfig = z.object({ + /** + * The widget types to show a toggle for. + */ + types: z.array(z.string()), +}); + +export type WidgetTogglesConfig = z.infer; + +export const CONFIG_KEY = "io.element.element-web-modules.widget-toggles"; + +declare module "@element-hq/element-web-module-api" { + export interface Config { + [CONFIG_KEY]: input; + } +} diff --git a/modules/widget-toggles/element-web/src/index.tsx b/modules/widget-toggles/element-web/src/index.tsx new file mode 100644 index 0000000000..d9a8d99d49 --- /dev/null +++ b/modules/widget-toggles/element-web/src/index.tsx @@ -0,0 +1,64 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type JSX } from "react"; +import { TooltipProvider } from "@vector-im/compound-web"; + +import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; +import { CONFIG_KEY, WidgetTogglesConfig } from "./config"; +import { WidgetToggle } from "./toggle"; + +class WidgetToggleModule implements Module { + public static readonly moduleApiVersion = "^1.0.0"; + private config?: WidgetTogglesConfig; + + public constructor(private api: Api) {} + + public async load(): Promise { + try { + this.config = WidgetTogglesConfig.parse(this.api.config.get(CONFIG_KEY)); + } catch (e) { + console.error("Failed to init module", e); + throw new Error(`Errors in module configuration for widget toggles module`); + } + + this.api.extras.setRoomHeaderButtonCallback((roomId: string) => { + const widgets = this.api.widget.getWidgetsInRoom(roomId); + const toggleElements: JSX.Element[] = []; + + for (const widget of widgets) { + if (this.config?.types.includes(widget.type)) { + toggleElements.push( + , + ); + } + } + + if (toggleElements.length === 0) return undefined; + + // XXX: We shouldn't have to add another TooltipProvider here, it should + // be using the one in MatrixChat in Element Web, but thanks to the fact + // that contexts are "magically" identified by class, it doesn't pick up the + // context because we use a different copy of compound-web. We'll probably + // need to fix this at some point, possibly by moving compound's tooltip stuff + // out to its own mini-module that can be provided at runtime by Element Web, + // unless React make contexts more sensible. + // Annoyingly this does actually cause the tooltips to behave a bit weirdly: + // there's a delay before the tooltip appears when yo move between these buttons + // and the rest of the header buttons, whereas moving between the other header + // buttons, the tooltips appear straight away once one has appeared. + return {toggleElements}; + }); + } +} + +export default WidgetToggleModule satisfies ModuleFactory; diff --git a/modules/widget-toggles/element-web/src/toggle.tsx b/modules/widget-toggles/element-web/src/toggle.tsx new file mode 100644 index 0000000000..741904c442 --- /dev/null +++ b/modules/widget-toggles/element-web/src/toggle.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2024 Nordeck IT + Consulting GmbH + * Copyright 2026 Element Creations Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type I18nApi, type WidgetApi } from "@element-hq/element-web-module-api"; +import { IconButton, Tooltip } from "@vector-im/compound-web"; +import { type IWidget } from "matrix-widget-api"; +import React, { type JSX } from "react"; +import styled from "styled-components"; + +type Props = { $isInContainer: boolean }; + +const Img = styled.img` + border-color: ${(): string => "var(--cpd-color-text-action-accent)"}; + border-radius: 50%; + border-style: solid; + border-width: ${({ $isInContainer }): string => ($isInContainer ? "2px" : "0px")}; + box-sizing: border-box; + height: 24px; + width: 24px; +`; + +const Svg = styled.svg` + height: 24px; + fill: ${({ $isInContainer }): string => ($isInContainer ? "var(--cpd-color-text-action-accent)" : "currentColor")}; + width: 24px; +`; + +function avatarUrl(app: IWidget, widgetApi: WidgetApi): string | null { + if (app.type.match(/jitsi/i)) { + return "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgcng9IjQiIGZpbGw9IiM1QUJGRjIiLz4KICAgIDxwYXRoIGQ9Ik0zIDcuODc1QzMgNi44Mzk0NyAzLjgzOTQ3IDYgNC44NzUgNkgxMS4xODc1QzEyLjIyMyA2IDEzLjA2MjUgNi44Mzk0NyAxMy4wNjI1IDcuODc1VjEyLjg3NUMxMy4wNjI1IDEzLjkxMDUgMTIuMjIzIDE0Ljc1IDExLjE4NzUgMTQuNzVINC44NzVDMy44Mzk0NyAxNC43NSAzIDEzLjkxMDUgMyAxMi44NzVWNy44NzVaIiBmaWxsPSJ3aGl0ZSIvPgogICAgPHBhdGggZD0iTTE0LjM3NSA4LjQ0NjQ0TDE2LjEyMDggNy4xMTAzOUMxNi40ODA2IDYuODM1MDIgMTcgNy4wOTE1OCAxNyA3LjU0NDY4VjEzLjAzOTZDMTcgMTMuNTE5OSAxNi40MjUxIDEzLjc2NjkgMTYuMDc2NyAxMy40MzYzTDE0LjM3NSAxMS44MjE0VjguNDQ2NDRaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K"; + } + + return widgetApi.getAppAvatarUrl(app); +} + +function isInContainer(app: IWidget, widgetApi: WidgetApi, roomId: string): boolean { + return widgetApi.isAppInContainer(app, "center", roomId) || widgetApi.isAppInContainer(app, "top", roomId); +} + +type WidgetToggleProps = { + app: IWidget; + roomId: string; + widgetApi: WidgetApi; + i18nApi: I18nApi; +}; + +export function WidgetToggle({ app, roomId, widgetApi, i18nApi }: WidgetToggleProps): JSX.Element { + const appAvatarUrl = avatarUrl(app, widgetApi); + const appNameOrType = app.name ?? app.type; + const inContainer = isInContainer(app, widgetApi, roomId); + + const label = i18nApi.translate(inContainer ? "Hide %(name)s" : "Show %(name)s", { name: appNameOrType }); + + const onClick = React.useCallback( + (event: React.MouseEvent) => { + event.stopPropagation(); + if (inContainer) { + widgetApi.moveAppToContainer(app, "right", roomId); + } else { + widgetApi.moveAppToContainer(app, "top", roomId); + } + }, + [app, inContainer, roomId, widgetApi], + ); + + return ( + + + {appAvatarUrl ? ( + {appNameOrType} + ) : ( + + + + )} + + + ); +} diff --git a/modules/widget-toggles/element-web/tsconfig.json b/modules/widget-toggles/element-web/tsconfig.json new file mode 100644 index 0000000000..fbda6380e7 --- /dev/null +++ b/modules/widget-toggles/element-web/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts new file mode 100644 index 0000000000..b65e3ec8ea --- /dev/null +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -0,0 +1,51 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; +import svgr from "vite-plugin-svgr"; +import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.tsx"), + name: "element-web-module-widget-toggles", + fileName: "index", + formats: ["es"], + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + rollupOptions: { + external: ["react"], + }, + }, + plugins: [ + importCSSSheet(), + react(), + svgr(), + nodePolyfills({ + include: ["events"], + }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + process: { env: { NODE_ENV: "production" } }, + }, +}); From 4eec6e7a97802bb980d7e0cbcb65457320c4736f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 10 Mar 2026 14:14:44 +0000 Subject: [PATCH 128/211] Update api name --- modules/widget-toggles/element-web/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-toggles/element-web/src/index.tsx b/modules/widget-toggles/element-web/src/index.tsx index d9a8d99d49..9527917f48 100644 --- a/modules/widget-toggles/element-web/src/index.tsx +++ b/modules/widget-toggles/element-web/src/index.tsx @@ -26,7 +26,7 @@ class WidgetToggleModule implements Module { throw new Error(`Errors in module configuration for widget toggles module`); } - this.api.extras.setRoomHeaderButtonCallback((roomId: string) => { + this.api.extras.addRoomHeaderButtonCallback((roomId: string) => { const widgets = this.api.widget.getWidgetsInRoom(roomId); const toggleElements: JSX.Element[] = []; From 6aa454c646e8046d400f6e7344e0f24bb5e36838 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 25 Feb 2026 09:36:58 +0000 Subject: [PATCH 129/211] Add widget lifecycle module --- .../widget-lifecycle/element-web/README.md | 75 +++++++ .../element-web/e2e/fixture/widget.html | 61 +++++ .../element-web/e2e/widget-lifecycle.spec.ts | 210 ++++++++++++++++++ .../widget-lifecycle/element-web/package.json | 24 ++ .../element-web/src/WidgetLifecycleModule.ts | 88 ++++++++ .../element-web/src/config.ts | 54 +++++ .../widget-lifecycle/element-web/src/index.ts | 12 + .../src/utils/constructWidgetPermissions.ts | 27 +++ .../element-web/src/utils/matchPattern.ts | 13 ++ .../src/utils/normalizeWidgetUrl.ts | 16 ++ .../tests/WidgetLifecycleModule.test.ts | 140 ++++++++++++ .../element-web/tests/config.test.ts | 88 ++++++++ .../tests/constructWidgetPermissions.test.ts | 75 +++++++ .../element-web/tsconfig.json | 7 + .../element-web/vite.config.ts | 27 +++ .../element-web/vitest.config.ts | 15 ++ 16 files changed, 932 insertions(+) create mode 100644 modules/widget-lifecycle/element-web/README.md create mode 100644 modules/widget-lifecycle/element-web/e2e/fixture/widget.html create mode 100644 modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts create mode 100644 modules/widget-lifecycle/element-web/package.json create mode 100644 modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts create mode 100644 modules/widget-lifecycle/element-web/src/config.ts create mode 100644 modules/widget-lifecycle/element-web/src/index.ts create mode 100644 modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts create mode 100644 modules/widget-lifecycle/element-web/src/utils/matchPattern.ts create mode 100644 modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts create mode 100644 modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts create mode 100644 modules/widget-lifecycle/element-web/tests/config.test.ts create mode 100644 modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts create mode 100644 modules/widget-lifecycle/element-web/tsconfig.json create mode 100644 modules/widget-lifecycle/element-web/vite.config.ts create mode 100644 modules/widget-lifecycle/element-web/vitest.config.ts diff --git a/modules/widget-lifecycle/element-web/README.md b/modules/widget-lifecycle/element-web/README.md new file mode 100644 index 0000000000..dd4c5ab6d1 --- /dev/null +++ b/modules/widget-lifecycle/element-web/README.md @@ -0,0 +1,75 @@ +# @element-hq/element-web-module-widget-lifecycle + +Widget lifecycle module for Element Web. + +Supports the following configuration options under the configuration key `io.element.element-web-modules.widget-lifecycle`: + +| Key | Type | Description | +| ------------------ | ------ | ------------------------------------------------ | +| widget_permissions | object | Map of widget URL patterns to approval settings. | + +Each widget configuration can use the following options: + +- `preload_approved` - if true, the preload dialog is not displayed for this widget. +- `identity_approved` - if true, requests for an identity token are automatically accepted. +- `capabilities_approved` - a list of capabilities that should be approved for this widget. + +The widget URL and capability strings can use a trailing `*` to match multiple widgets or capabilities. +This is useful when widgets have multiple routes or capabilities include variable state keys. + +## Matching and precedence + +- Widget URLs are normalized by stripping query parameters and hash fragments before matching. +- Patterns ending in `*` are matched by prefix; other patterns must match exactly. +- If multiple rules match, the most specific match wins per field. +- The capabilities allow-list is not merged across rules; the most specific rule that defines it wins. + +## Example configuration (exact match) + +```json +{ + "io.element.element-web-modules.widget-lifecycle": { + "widget_permissions": { + "https://widget.example.com/": { + "preload_approved": true, + "identity_approved": true, + "capabilities_approved": [ + "org.matrix.msc2931.navigate", + "org.matrix.msc2762.receive.state_event:m.room.power_levels" + ] + } + } + } +} +``` + +## Example configuration (wildcards) + +```json +{ + "io.element.element-web-modules.widget-lifecycle": { + "widget_permissions": { + "https://widget.example.com/*": { + "preload_approved": true, + "identity_approved": true, + "capabilities_approved": [ + "org.matrix.msc2931.navigate", + "org.matrix.msc2762.receive.state_event:m.room.power_levels", + "org.matrix.msc2762.send.state_event:net.custom_event#*" + ] + } + } + } +} +``` + +## Copyright & License + +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/widget-lifecycle/element-web/e2e/fixture/widget.html b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html new file mode 100644 index 0000000000..271e18d163 --- /dev/null +++ b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html @@ -0,0 +1,61 @@ + + + + Demo Widget + + + +

Hello unknown!

+ + diff --git a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts new file mode 100644 index 0000000000..268a9dece5 --- /dev/null +++ b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts @@ -0,0 +1,210 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; + +import { test, expect } from "../../../../playwright/element-web-test.ts"; + +declare module "@element-hq/element-web-module-api" { + interface Config { + "io.element.element-web-modules.widget-lifecycle": { + widget_permissions: { + [url: string]: { + preload_approved?: boolean; + identity_approved?: boolean; + capabilities_approved?: string[]; + }; + }; + }; + } +} + +const WIDGET_URL = "http://localhost:8080/widget.html"; + +test.use({ + displayName: "Timmy", + synapseConfig: async ({ synapseConfig, _homeserver: homeserver }, use) => { + (homeserver as SynapseContainer).withConfigField("listeners[0].resources[0].names", ["client", "openid"]); + await use(synapseConfig); + }, + page: async ({ context, page, moduleDir }, use) => { + await context.route(`${WIDGET_URL}*`, async (route) => { + await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" }); + }); + + await page.goto("/"); + await use(page); + }, + bypassCSP: true, + launchOptions: { + args: ["--disable-web-security"], + }, +}); + +test.describe("Widget Lifecycle", () => { + test.describe("trusted widgets", () => { + test.use({ + config: { + "io.element.element-web-modules.widget-lifecycle": { + widget_permissions: { + [WIDGET_URL]: { + preload_approved: true, + identity_approved: true, + capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"], + }, + }, + }, + }, + }); + + test("auto-approves preload and identity", async ({ page, user, homeserver }, testInfo) => { + const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Trusted Widget", + }, + ); + await homeserver.csApi.request<{ event_id: string }>( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, + bot.accessToken, + { + id: "1", + creatorUserId: bot.userId, + type: "custom", + name: "Trusted Widget", + url: `${WIDGET_URL}?hsUrl=${encodeURIComponent(homeserver.baseUrl)}&caps=org.matrix.msc2762.receive.state_event:m.room.topic`, + }, + ); + await homeserver.csApi.request( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, + bot.accessToken, + { + widgets: { + "1": { + container: "top", + }, + }, + }, + ); + await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { + user_id: user.userId, + }); + + await page.getByText("Trusted Widget").click(); + await page.getByRole("button", { name: "Accept" }).click(); + + await expect( + page + .frameLocator('iframe[title="Trusted Widget"]') + .getByRole("heading", { name: `Hello ${user.userId}!` }), + ).toBeVisible(); + }); + + test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver }, testInfo) => { + const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Capabilities Widget", + }, + ); + await homeserver.csApi.request<{ event_id: string }>( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, + bot.accessToken, + { + id: "1", + creatorUserId: bot.userId, + type: "custom", + name: "Capabilities Widget", + url: `${WIDGET_URL}?hsUrl=${encodeURIComponent(homeserver.baseUrl)}&caps=org.matrix.msc2762.receive.state_event:m.room.topic,org.matrix.msc2762.receive.state_event:m.room.name`, + }, + ); + await homeserver.csApi.request( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, + bot.accessToken, + { + widgets: { + "1": { + container: "top", + }, + }, + }, + ); + await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { + user_id: user.userId, + }); + + await page.getByText("Capabilities Widget").click(); + await page.getByRole("button", { name: "Accept" }).click(); + + await expect(page.getByRole("button", { name: "Approve" })).toBeVisible(); + }); + }); + + test.describe("untrusted widgets", () => { + test.use({ + config: { + "io.element.element-web-modules.widget-lifecycle": { + widget_permissions: {}, + }, + }, + }); + + test("shows dialogs for untrusted widgets", async ({ page, user, homeserver }, testInfo) => { + const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Untrusted Widget", + }, + ); + await homeserver.csApi.request<{ event_id: string }>( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, + bot.accessToken, + { + id: "1", + creatorUserId: bot.userId, + type: "custom", + name: "Untrusted Widget", + url: `${WIDGET_URL}?hsUrl=${encodeURIComponent(homeserver.baseUrl)}&caps=org.matrix.msc2762.receive.state_event:m.room.topic`, + }, + ); + await homeserver.csApi.request( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, + bot.accessToken, + { + widgets: { + "1": { + container: "top", + }, + }, + }, + ); + await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { + user_id: user.userId, + }); + + await page.getByText("Untrusted Widget").click(); + await page.getByRole("button", { name: "Accept" }).click(); + + await expect(page.getByRole("button", { name: "Continue" })).toBeVisible(); + }); + }); +}); diff --git a/modules/widget-lifecycle/element-web/package.json b/modules/widget-lifecycle/element-web/package.json new file mode 100644 index 0000000000..b469ee086c --- /dev/null +++ b/modules/widget-lifecycle/element-web/package.json @@ -0,0 +1,24 @@ +{ + "name": "@element-hq/element-web-module-widget-lifecycle", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "lib/index.js", + "license": "SEE LICENSE IN README.md", + "scripts": { + "prepare": "vite build", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", + "test": "vitest" + }, + "devDependencies": { + "@element-hq/element-web-module-api": "^1.0.0", + "@types/node": "^22.10.7", + "typescript": "^5.7.3", + "vite": "^7.1.11", + "vitest": "^4.0.0" + }, + "dependencies": { + "zod": "^4.0.0" + } +} diff --git a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts new file mode 100644 index 0000000000..17453af6bc --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts @@ -0,0 +1,88 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Api, Module, WidgetDescriptor, WidgetLifecycleApi } from "@element-hq/element-web-module-api"; + +import { CONFIG_KEY, parseWidgetLifecycleConfig, type WidgetLifecycleModuleConfig } from "./config"; +import { constructWidgetPermissions } from "./utils/constructWidgetPermissions"; +import { matchPattern } from "./utils/matchPattern"; +import { normalizeWidgetUrl } from "./utils/normalizeWidgetUrl"; + +/** Subset of {@link WidgetLifecycleApi} used by the module for registration only. */ +export type WidgetLifecycleApiAdapter = Pick< + WidgetLifecycleApi, + "registerPreloadApprover" | "registerIdentityApprover" | "registerCapabilitiesApprover" +>; + +type ModuleApi = Pick; + +/** + * Module that auto-approves widget preloading, identity token requests, and capability + * requests based on URL-pattern rules defined in config.json. + */ +export default class WidgetLifecycleModule implements Module { + public static readonly moduleApiVersion = "^1.0.0"; + + private config: WidgetLifecycleModuleConfig = {}; + + public constructor(private api: ModuleApi) {} + + public async load(): Promise { + if (!this.api.widgetLifecycle?.registerPreloadApprover) { + throw new Error( + "Widget lifecycle API is not available. Update Element Web to a build that provides widget lifecycle module support.", + ); + } + + try { + this.config = parseWidgetLifecycleConfig(this.api.config.get(CONFIG_KEY)); + } catch (error) { + console.error("[WidgetLifecycle] Failed to init module", error); + this.config = {}; + } + + this.api.widgetLifecycle.registerPreloadApprover((widget) => this.preapprovePreload(widget)); + this.api.widgetLifecycle.registerIdentityApprover((widget) => this.preapproveIdentity(widget)); + this.api.widgetLifecycle.registerCapabilitiesApprover((widget, requested) => + this.preapproveCapabilities(widget, requested), + ); + } + + private preapprovePreload(widget: WidgetDescriptor): boolean { + const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); + const configuration = constructWidgetPermissions(this.config, normalizedUrl); + return configuration.preload_approved === true; + } + + private preapproveIdentity(widget: WidgetDescriptor): boolean { + const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); + const configuration = constructWidgetPermissions(this.config, normalizedUrl); + return configuration.identity_approved === true; + } + + private preapproveCapabilities( + widget: WidgetDescriptor, + requestedCapabilities: Set, + ): Set | undefined { + const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); + const configuration = constructWidgetPermissions(this.config, normalizedUrl); + const capabilitiesApproved = configuration.capabilities_approved; + + if (!capabilitiesApproved) return undefined; + + const approvedCapabilities = new Set(); + for (const requestedCapability of requestedCapabilities) { + if (capabilitiesApproved.some((capability) => matchPattern(requestedCapability, capability))) { + approvedCapabilities.add(requestedCapability); + } + } + + return approvedCapabilities.size > 0 ? approvedCapabilities : undefined; + } +} + +export { WidgetLifecycleModule }; diff --git a/modules/widget-lifecycle/element-web/src/config.ts b/modules/widget-lifecycle/element-web/src/config.ts new file mode 100644 index 0000000000..0746ae37f1 --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/config.ts @@ -0,0 +1,54 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { z } from "zod/mini"; + +z.config(z.locales.en()); + +/** The config.json key under which widget lifecycle module configuration is stored. */ +export const CONFIG_KEY = "io.element.element-web-modules.widget-lifecycle"; + +export const WidgetConfigurationSchema = z.partial( + z.looseObject({ + preload_approved: z.boolean(), + identity_approved: z.boolean(), + capabilities_approved: z.array(z.string().check(z.minLength(1))), + }), +); + +/** Per-widget approval settings: preload, identity, and capabilities. */ +export type WidgetConfiguration = z.infer; + +const ModuleConfigSchema = z.partial( + z.looseObject({ + widget_permissions: z.record(z.string(), WidgetConfigurationSchema), + }), +); + +/** Map from URL patterns to their widget approval configuration. */ +export type WidgetLifecycleModuleConfig = Record; + +/** + * Parse and validate the widget lifecycle module configuration. + * Returns an empty config if the input is falsy; throws on schema violations. + */ +declare module "@element-hq/element-web-module-api" { + export interface Config { + [CONFIG_KEY]: z.input; + } +} + +export const parseWidgetLifecycleConfig = (value: unknown): WidgetLifecycleModuleConfig => { + if (!value) return {}; + + const result = ModuleConfigSchema.safeParse(value); + if (!result.success) { + throw new Error(`Errors in the module configuration for "${CONFIG_KEY}": ${result.error.message}`); + } + + return result.data.widget_permissions ?? {}; +}; diff --git a/modules/widget-lifecycle/element-web/src/index.ts b/modules/widget-lifecycle/element-web/src/index.ts new file mode 100644 index 0000000000..843638fc29 --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/index.ts @@ -0,0 +1,12 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { ModuleFactory } from "@element-hq/element-web-module-api"; + +import WidgetLifecycleModule from "./WidgetLifecycleModule"; + +export default WidgetLifecycleModule satisfies ModuleFactory; diff --git a/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts b/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts new file mode 100644 index 0000000000..9f4ae1346d --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts @@ -0,0 +1,27 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { WidgetLifecycleModuleConfig, WidgetConfiguration } from "../config"; +import { matchPattern } from "./matchPattern"; + +/** + * Returns the WidgetConfiguration for a widget. + * If multiple WidgetConfigurations match, the most specific match wins per field. + */ +export function constructWidgetPermissions( + config: WidgetLifecycleModuleConfig, + widgetUrl: string, +): WidgetConfiguration { + const widgetPermissionsMatched = Object.keys(config).filter((pattern) => matchPattern(widgetUrl, pattern)); + + return widgetPermissionsMatched.sort(sortLongestMatchLast).reduce((prev, key) => ({ ...prev, ...config[key] }), {}); +} + +/** Sort strings alphabetically so longer, more-specific patterns are applied last. */ +export function sortLongestMatchLast(a: string, b: string): number { + return a.localeCompare(b, "en", { sensitivity: "base" }); +} diff --git a/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts b/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts new file mode 100644 index 0000000000..7531474569 --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts @@ -0,0 +1,13 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Checks if string matches pattern. Pattern can end with '*' to support prefix matching. + */ +export function matchPattern(value: string, pattern: string): boolean { + return pattern.endsWith("*") ? value.startsWith(pattern.slice(0, pattern.length - 1)) : value === pattern; +} diff --git a/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts b/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts new file mode 100644 index 0000000000..794db023bf --- /dev/null +++ b/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts @@ -0,0 +1,16 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Strips query parameters and fragment from a widget URL for matching against config patterns. + */ +export function normalizeWidgetUrl(widgetUrl: string): string { + const url = new URL(widgetUrl); + url.search = ""; + url.hash = ""; + return url.toString(); +} diff --git a/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts b/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts new file mode 100644 index 0000000000..9970a9aa00 --- /dev/null +++ b/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts @@ -0,0 +1,140 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, expect, it } from "vitest"; + +import WidgetLifecycleModule, { type WidgetLifecycleApiAdapter } from "../src/WidgetLifecycleModule"; +import type { + CapabilitiesApprover, + IdentityApprover, + PreloadApprover, + WidgetDescriptor, +} from "@element-hq/element-web-module-api"; + +const createApi = (config: unknown = {}) => { + const handlers: { + preload?: PreloadApprover; + identity?: IdentityApprover; + capabilities?: CapabilitiesApprover; + } = {}; + + const widgetLifecycle: WidgetLifecycleApiAdapter = { + registerPreloadApprover: (approver) => { + handlers.preload = approver; + }, + registerIdentityApprover: (approver) => { + handlers.identity = approver; + }, + registerCapabilitiesApprover: (approver) => { + handlers.capabilities = approver; + }, + }; + + return { + api: { + config: { + get: () => config, + }, + widgetLifecycle, + }, + handlers, + }; +}; + +const widget: WidgetDescriptor = { + id: "widget-id", + templateUrl: "https://example.com", + creatorUserId: "@user-id", + kind: "room", +}; + +describe("WidgetLifecycleModule", () => { + it("approves preload when configured", async () => { + const { api, handlers } = createApi({ + widget_permissions: { + "https://example.com/": { preload_approved: true }, + }, + }); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + expect(await handlers.preload?.(widget)).toBe(true); + }); + + it("approves identity when configured", async () => { + const { api, handlers } = createApi({ + widget_permissions: { + "https://example.com/": { identity_approved: true }, + }, + }); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + expect(await handlers.identity?.(widget)).toBe(true); + }); + + it("approves configured capabilities", async () => { + const { api, handlers } = createApi({ + widget_permissions: { + "https://example.com/": { + capabilities_approved: ["org.matrix.msc2931.navigate"], + }, + }, + }); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + const approved = await handlers.capabilities?.( + widget, + new Set(["org.matrix.msc2931.navigate", "org.matrix.msc2762.timeline:*"]), + ); + + expect(approved).toEqual(new Set(["org.matrix.msc2931.navigate"])); + }); + + it("returns no approvals when not configured", async () => { + const { api, handlers } = createApi({}); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + expect(await handlers.preload?.(widget)).toBe(false); + expect(await handlers.identity?.(widget)).toBe(false); + expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined(); + }); + + it("returns no approvals when config is missing", async () => { + const { api, handlers } = createApi(undefined); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + expect(await handlers.preload?.(widget)).toBe(false); + expect(await handlers.identity?.(widget)).toBe(false); + expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined(); + }); + + it("fails closed on invalid config", async () => { + const { api, handlers } = createApi({ + widget_permissions: { + "https://example.com/": { + preload_approved: null, + }, + }, + }); + + const module = new WidgetLifecycleModule(api); + await module.load(); + + expect(await handlers.preload?.(widget)).toBe(false); + expect(await handlers.identity?.(widget)).toBe(false); + expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined(); + }); +}); diff --git a/modules/widget-lifecycle/element-web/tests/config.test.ts b/modules/widget-lifecycle/element-web/tests/config.test.ts new file mode 100644 index 0000000000..ed131db338 --- /dev/null +++ b/modules/widget-lifecycle/element-web/tests/config.test.ts @@ -0,0 +1,88 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, expect, it } from "vitest"; + +import { parseWidgetLifecycleConfig } from "../src/config"; + +describe("parseWidgetLifecycleConfig", () => { + it("accepts missing configuration", () => { + expect(parseWidgetLifecycleConfig(undefined)).toEqual({}); + }); + + it("accepts empty configuration", () => { + expect(parseWidgetLifecycleConfig({})).toEqual({}); + }); + + it("accepts valid configuration", () => { + expect( + parseWidgetLifecycleConfig({ + widget_permissions: { + "https://localhost": { + preload_approved: true, + identity_approved: false, + capabilities_approved: [], + }, + }, + }), + ).toEqual({ + "https://localhost": { + preload_approved: true, + identity_approved: false, + capabilities_approved: [], + }, + }); + }); + + it("accepts additional properties", () => { + expect( + parseWidgetLifecycleConfig({ + widget_permissions: { + "https://localhost": { + preload_approved: true, + identity_approved: false, + capabilities_approved: ["capability"], + additional: "tmp", + }, + }, + }), + ).toEqual({ + "https://localhost": { + preload_approved: true, + identity_approved: false, + capabilities_approved: ["capability"], + additional: "tmp", + }, + }); + }); + + it.each([ + { preload_approved: null }, + { preload_approved: 123 }, + { identity_approved: null }, + { identity_approved: 123 }, + { capabilities_approved: null }, + { capabilities_approved: 123 }, + { capabilities_approved: [undefined] }, + { capabilities_approved: [null] }, + { capabilities_approved: [123] }, + { capabilities_approved: [""] }, + ])("rejects invalid widget configuration %j", (patch) => { + expect(() => + parseWidgetLifecycleConfig({ + widget_permissions: { + "https://localhost": { + preload_approved: true, + identity_approved: false, + capabilities_approved: ["capability"], + ...patch, + }, + }, + }), + ).toThrow(); + }); +}); diff --git a/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts b/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts new file mode 100644 index 0000000000..e84881acd0 --- /dev/null +++ b/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts @@ -0,0 +1,75 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, expect, it } from "vitest"; + +import { constructWidgetPermissions, sortLongestMatchLast } from "../src/utils/constructWidgetPermissions"; + +describe("constructWidgetPermissions", () => { + it("finds exact match", () => { + expect(constructWidgetPermissions({ "https://a.com/": { preload_approved: true } }, "https://a.com/")).toEqual({ + preload_approved: true, + }); + }); + + it("finds prefix match", () => { + expect( + constructWidgetPermissions({ "https://a.com/*": { preload_approved: true } }, "https://a.com/some"), + ).toEqual({ preload_approved: true }); + }); + + it("merges multiple permissions", () => { + expect( + constructWidgetPermissions( + { + "https://b.com/path": { + preload_approved: false, + capabilities_approved: ["org.matrix.msc2762.timeline:*"], + }, + "https://b.com/*": { + preload_approved: true, + identity_approved: true, + capabilities_approved: ["org.matrix.msc2931.navigate"], + }, + }, + "https://b.com/path", + ), + ).toEqual({ + preload_approved: false, + identity_approved: true, + capabilities_approved: ["org.matrix.msc2762.timeline:*"], + }); + }); + + it("skips unknown url", () => { + expect(constructWidgetPermissions({ "https://a.com/": { preload_approved: true } }, "https://a.com/x")).toEqual( + {}, + ); + }); +}); + +describe("sortLongestMatchLast", () => { + it("sorts longest match last", () => { + expect( + [ + "org.matrix.msc2762.receive.state_event:*", + "org.matrix.msc2762.receive.*", + "org.matrix.msc2762.receive.state_event:m.custom*", + "org.matrix.msc2762.receive.state_event:m.custom#state_key", + "org.matrix.msc2762.receive.state_event:m.custom#*", + "*", + ].sort(sortLongestMatchLast), + ).toEqual([ + "*", + "org.matrix.msc2762.receive.*", + "org.matrix.msc2762.receive.state_event:*", + "org.matrix.msc2762.receive.state_event:m.custom*", + "org.matrix.msc2762.receive.state_event:m.custom#*", + "org.matrix.msc2762.receive.state_event:m.custom#state_key", + ]); + }); +}); diff --git a/modules/widget-lifecycle/element-web/tsconfig.json b/modules/widget-lifecycle/element-web/tsconfig.json new file mode 100644 index 0000000000..8f4f6b8ace --- /dev/null +++ b/modules/widget-lifecycle/element-web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/modules/widget-lifecycle/element-web/vite.config.ts b/modules/widget-lifecycle/element-web/vite.config.ts new file mode 100644 index 0000000000..3e4bed43a0 --- /dev/null +++ b/modules/widget-lifecycle/element-web/vite.config.ts @@ -0,0 +1,27 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vite"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "element-web-module-widget-lifecycle", + fileName: "index", + formats: ["es"], + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + minify: false, + }, +}); diff --git a/modules/widget-lifecycle/element-web/vitest.config.ts b/modules/widget-lifecycle/element-web/vitest.config.ts new file mode 100644 index 0000000000..29cb2a09bd --- /dev/null +++ b/modules/widget-lifecycle/element-web/vitest.config.ts @@ -0,0 +1,15 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["tests/**/*.test.ts"], + exclude: ["./e2e/**/*", "./node_modules/**/*"], + }, +}); From 5d091e91c3306f22a5e258bd26ed97b78e882fc3 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 4 Mar 2026 18:02:17 +0000 Subject: [PATCH 130/211] Update e2e tests and header Don't add normalisation of widget headers either --- .../widget-lifecycle/element-web/README.md | 1 - .../element-web/e2e/widget-lifecycle.spec.ts | 28 ++++++++++++++- .../element-web/src/WidgetLifecycleModule.ts | 11 ++---- .../widget-lifecycle/element-web/src/index.ts | 1 - .../src/utils/constructWidgetPermissions.ts | 1 + .../element-web/src/utils/matchPattern.ts | 1 + .../src/utils/normalizeWidgetUrl.ts | 16 --------- .../tests/WidgetLifecycleModule.test.ts | 2 +- .../tests/constructWidgetPermissions.test.ts | 1 + .../element-web/tests/matchPattern.test.ts | 35 +++++++++++++++++++ 10 files changed, 69 insertions(+), 28 deletions(-) delete mode 100644 modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts create mode 100644 modules/widget-lifecycle/element-web/tests/matchPattern.test.ts diff --git a/modules/widget-lifecycle/element-web/README.md b/modules/widget-lifecycle/element-web/README.md index dd4c5ab6d1..7cc0bc734c 100644 --- a/modules/widget-lifecycle/element-web/README.md +++ b/modules/widget-lifecycle/element-web/README.md @@ -19,7 +19,6 @@ This is useful when widgets have multiple routes or capabilities include variabl ## Matching and precedence -- Widget URLs are normalized by stripping query parameters and hash fragments before matching. - Patterns ending in `*` are matched by prefix; other patterns must match exactly. - If multiple rules match, the most specific match wins per field. - The capabilities allow-list is not merged across rules; the most specific rule that defines it wins. diff --git a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts index 268a9dece5..ed3ac965d8 100644 --- a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts +++ b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts @@ -47,6 +47,8 @@ test.use({ test.describe("Widget Lifecycle", () => { test.describe("trusted widgets", () => { + // Configure the module to pre-approve the widget URL for preloading, identity tokens, + // and the m.room.topic state event capability. test.use({ config: { "io.element.element-web-modules.widget-lifecycle": { @@ -62,6 +64,9 @@ test.describe("Widget Lifecycle", () => { }); test("auto-approves preload and identity", async ({ page, user, homeserver }, testInfo) => { + // A bot creates a room with the widget pinned to the top panel, then invites the test user. + // Because the widget was added by a different user (the bot), Element would normally show a + // preload consent dialog before loading it — this test verifies that dialog is skipped. const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( "POST", @@ -102,6 +107,11 @@ test.describe("Widget Lifecycle", () => { await page.getByText("Trusted Widget").click(); await page.getByRole("button", { name: "Accept" }).click(); + // No preload dialog should appear — the widget loads immediately. + await expect(page.getByRole("button", { name: "Continue" })).not.toBeVisible(); + + // The widget greets the user by ID, proving the identity token was also auto-approved + // and passed to the widget without any consent prompts. await expect( page .frameLocator('iframe[title="Trusted Widget"]') @@ -119,6 +129,7 @@ test.describe("Widget Lifecycle", () => { name: "Capabilities Widget", }, ); + // The widget requests two capabilities: m.room.topic (in the allowlist) and m.room.name (not in the allowlist). await homeserver.csApi.request<{ event_id: string }>( "PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, @@ -150,11 +161,13 @@ test.describe("Widget Lifecycle", () => { await page.getByText("Capabilities Widget").click(); await page.getByRole("button", { name: "Accept" }).click(); + // A capabilities approval dialog should appear since m.room.name was not pre-approved. await expect(page.getByRole("button", { name: "Approve" })).toBeVisible(); }); }); test.describe("untrusted widgets", () => { + // No widget URLs are pre-approved, so all lifecycle prompts should be shown to the user. test.use({ config: { "io.element.element-web-modules.widget-lifecycle": { @@ -163,7 +176,11 @@ test.describe("Widget Lifecycle", () => { }, }); - test("shows dialogs for untrusted widgets", async ({ page, user, homeserver }, testInfo) => { + test("shows preload, capabilities, and OpenID dialogs for untrusted widgets", async ({ + page, + user, + homeserver, + }, testInfo) => { const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( "POST", @@ -204,6 +221,15 @@ test.describe("Widget Lifecycle", () => { await page.getByText("Untrusted Widget").click(); await page.getByRole("button", { name: "Accept" }).click(); + // 1. Preload consent dialog — shown because the widget was added by another user (the bot). + await expect(page.getByRole("button", { name: "Continue" })).toBeVisible(); + await page.getByRole("button", { name: "Continue" }).click(); + + // 2. Capabilities dialog — the widget requests m.room.topic once it loads. + await expect(page.getByRole("button", { name: "Approve" })).toBeVisible(); + await page.getByRole("button", { name: "Approve" }).click(); + + // 3. OpenID identity dialog — the widget requests an identity token after capabilities are granted. await expect(page.getByRole("button", { name: "Continue" })).toBeVisible(); }); }); diff --git a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts index 17453af6bc..ce17d783f8 100644 --- a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts +++ b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts @@ -6,11 +6,9 @@ Please see LICENSE files in the repository root for full details. */ import type { Api, Module, WidgetDescriptor, WidgetLifecycleApi } from "@element-hq/element-web-module-api"; - import { CONFIG_KEY, parseWidgetLifecycleConfig, type WidgetLifecycleModuleConfig } from "./config"; import { constructWidgetPermissions } from "./utils/constructWidgetPermissions"; import { matchPattern } from "./utils/matchPattern"; -import { normalizeWidgetUrl } from "./utils/normalizeWidgetUrl"; /** Subset of {@link WidgetLifecycleApi} used by the module for registration only. */ export type WidgetLifecycleApiAdapter = Pick< @@ -53,14 +51,12 @@ export default class WidgetLifecycleModule implements Module { } private preapprovePreload(widget: WidgetDescriptor): boolean { - const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); - const configuration = constructWidgetPermissions(this.config, normalizedUrl); + const configuration = constructWidgetPermissions(this.config, widget.templateUrl); return configuration.preload_approved === true; } private preapproveIdentity(widget: WidgetDescriptor): boolean { - const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); - const configuration = constructWidgetPermissions(this.config, normalizedUrl); + const configuration = constructWidgetPermissions(this.config, widget.templateUrl); return configuration.identity_approved === true; } @@ -68,8 +64,7 @@ export default class WidgetLifecycleModule implements Module { widget: WidgetDescriptor, requestedCapabilities: Set, ): Set | undefined { - const normalizedUrl = normalizeWidgetUrl(widget.templateUrl); - const configuration = constructWidgetPermissions(this.config, normalizedUrl); + const configuration = constructWidgetPermissions(this.config, widget.templateUrl); const capabilitiesApproved = configuration.capabilities_approved; if (!capabilitiesApproved) return undefined; diff --git a/modules/widget-lifecycle/element-web/src/index.ts b/modules/widget-lifecycle/element-web/src/index.ts index 843638fc29..7cff3faacc 100644 --- a/modules/widget-lifecycle/element-web/src/index.ts +++ b/modules/widget-lifecycle/element-web/src/index.ts @@ -6,7 +6,6 @@ Please see LICENSE files in the repository root for full details. */ import type { ModuleFactory } from "@element-hq/element-web-module-api"; - import WidgetLifecycleModule from "./WidgetLifecycleModule"; export default WidgetLifecycleModule satisfies ModuleFactory; diff --git a/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts b/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts index 9f4ae1346d..07943c1b82 100644 --- a/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts +++ b/modules/widget-lifecycle/element-web/src/utils/constructWidgetPermissions.ts @@ -1,5 +1,6 @@ /* Copyright 2026 Element Creations Ltd. +Copyright 2023 Nordeck IT + Consulting GmbH SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. diff --git a/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts b/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts index 7531474569..8271649291 100644 --- a/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts +++ b/modules/widget-lifecycle/element-web/src/utils/matchPattern.ts @@ -1,5 +1,6 @@ /* Copyright 2026 Element Creations Ltd. +Copyright 2023 Nordeck IT + Consulting GmbH SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. diff --git a/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts b/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts deleted file mode 100644 index 794db023bf..0000000000 --- a/modules/widget-lifecycle/element-web/src/utils/normalizeWidgetUrl.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2026 Element Creations Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -/** - * Strips query parameters and fragment from a widget URL for matching against config patterns. - */ -export function normalizeWidgetUrl(widgetUrl: string): string { - const url = new URL(widgetUrl); - url.search = ""; - url.hash = ""; - return url.toString(); -} diff --git a/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts b/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts index 9970a9aa00..d513a083f9 100644 --- a/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts +++ b/modules/widget-lifecycle/element-web/tests/WidgetLifecycleModule.test.ts @@ -47,7 +47,7 @@ const createApi = (config: unknown = {}) => { const widget: WidgetDescriptor = { id: "widget-id", - templateUrl: "https://example.com", + templateUrl: "https://example.com/", creatorUserId: "@user-id", kind: "room", }; diff --git a/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts b/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts index e84881acd0..25bc60660d 100644 --- a/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts +++ b/modules/widget-lifecycle/element-web/tests/constructWidgetPermissions.test.ts @@ -1,5 +1,6 @@ /* Copyright 2026 Element Creations Ltd. +Copyright 2023 Nordeck IT + Consulting GmbH SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. diff --git a/modules/widget-lifecycle/element-web/tests/matchPattern.test.ts b/modules/widget-lifecycle/element-web/tests/matchPattern.test.ts new file mode 100644 index 0000000000..0cc2c25c03 --- /dev/null +++ b/modules/widget-lifecycle/element-web/tests/matchPattern.test.ts @@ -0,0 +1,35 @@ +/* +Copyright 2026 Element Creations Ltd. +Copyright 2023 Nordeck IT + Consulting GmbH + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, expect, it } from "vitest"; + +import { matchPattern } from "../src/utils/matchPattern"; + +describe("matchPattern", () => { + it.each([ + "*", + "org.matrix.msc2762.receive.*", + "org.matrix.msc2762.receive.state_event:*", + "org.matrix.msc2762.receive.state_event:m.custom*", + "org.matrix.msc2762.receive.state_event:m.custom#*", + "org.matrix.msc2762.receive.state_event:m.custom#state_key", + "org.matrix.msc2762.receive.state_event:m.custom#state_key*", + ])("matches %s", (pattern) => { + expect(matchPattern("org.matrix.msc2762.receive.state_event:m.custom#state_key", pattern)).toBe(true); + }); + + it.each([ + "org.matrix.msc2762.receive.state_event:", + "org.matrix.msc2762.receive.state_event:m.custom", + "org.matrix.msc2762.receive.state_event:m.custom#other_key", + "org.matrix.msc2762.receive.*:m.custom#state_key", + "org.matrix.msc2762.receive.room_event:m.custom", + ])("does not match %s", (pattern) => { + expect(matchPattern("org.matrix.msc2762.receive.state_event:m.custom#state_key", pattern)).toBe(false); + }); +}); From 782b008b54906a90af58cb92929049047d551f00 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 6 Mar 2026 13:56:30 +0000 Subject: [PATCH 131/211] Fix e2e We need wildcard to match query params etc --- .../widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts index ed3ac965d8..83ad62ec65 100644 --- a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts +++ b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts @@ -53,7 +53,7 @@ test.describe("Widget Lifecycle", () => { config: { "io.element.element-web-modules.widget-lifecycle": { widget_permissions: { - [WIDGET_URL]: { + [`${WIDGET_URL}*`]: { preload_approved: true, identity_approved: true, capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"], From c2da26542e12a7844a1dd8e39ea48ae877521a02 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 6 Mar 2026 13:56:44 +0000 Subject: [PATCH 132/211] Remove double export --- .../widget-lifecycle/element-web/src/WidgetLifecycleModule.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts index ce17d783f8..144a799291 100644 --- a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts +++ b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts @@ -79,5 +79,3 @@ export default class WidgetLifecycleModule implements Module { return approvedCapabilities.size > 0 ? approvedCapabilities : undefined; } } - -export { WidgetLifecycleModule }; From 7d183ed15b2024acf2c2db18d14a4751413b68e9 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 6 Mar 2026 16:20:40 +0000 Subject: [PATCH 133/211] Fix coverage reporting --- modules/widget-lifecycle/element-web/package.json | 6 ++++-- modules/widget-lifecycle/element-web/vitest.config.ts | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/widget-lifecycle/element-web/package.json b/modules/widget-lifecycle/element-web/package.json index b469ee086c..4611eaecda 100644 --- a/modules/widget-lifecycle/element-web/package.json +++ b/modules/widget-lifecycle/element-web/package.json @@ -9,14 +9,16 @@ "prepare": "vite build", "lint:types": "tsc --noEmit", "lint:codestyle": "echo 'handled by lint:eslint'", - "test": "vitest" + "test": "vitest run --coverage" }, "devDependencies": { "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", + "@vitest/coverage-v8": "^4.0.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vitest": "^4.0.0" + "vitest": "^4.0.0", + "vitest-sonar-reporter": "^2.0.0" }, "dependencies": { "zod": "^4.0.0" diff --git a/modules/widget-lifecycle/element-web/vitest.config.ts b/modules/widget-lifecycle/element-web/vitest.config.ts index 29cb2a09bd..c9ca3971b0 100644 --- a/modules/widget-lifecycle/element-web/vitest.config.ts +++ b/modules/widget-lifecycle/element-web/vitest.config.ts @@ -6,10 +6,21 @@ Please see LICENSE files in the repository root for full details. */ import { defineConfig } from "vitest/config"; +import { env } from "node:process"; + +const isGHA = env["GITHUB_ACTIONS"] !== undefined; export default defineConfig({ test: { include: ["tests/**/*.test.ts"], exclude: ["./e2e/**/*", "./node_modules/**/*"], + reporters: isGHA + ? ["default", ["vitest-sonar-reporter", { outputFile: "coverage/sonar-report.xml" }]] + : ["default"], + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + reporter: ["lcov", "text"], + }, }, }); From 75464c163493bf1a8ade6c6830390b24856ed994 Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 10 Mar 2026 14:19:31 +0000 Subject: [PATCH 134/211] Make sonar happy --- modules/widget-lifecycle/element-web/e2e/fixture/widget.html | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/widget-lifecycle/element-web/e2e/fixture/widget.html b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html index 271e18d163..1667a73c44 100644 --- a/modules/widget-lifecycle/element-web/e2e/fixture/widget.html +++ b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html @@ -1,4 +1,5 @@ + Demo Widget From 610a9494d141ffa54fe440274ae6a333753a7c6b Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 10 Mar 2026 15:37:50 +0000 Subject: [PATCH 135/211] Fix coverage paths --- modules/widget-lifecycle/element-web/vitest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-lifecycle/element-web/vitest.config.ts b/modules/widget-lifecycle/element-web/vitest.config.ts index c9ca3971b0..7ec0ff7fd7 100644 --- a/modules/widget-lifecycle/element-web/vitest.config.ts +++ b/modules/widget-lifecycle/element-web/vitest.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ coverage: { provider: "v8", include: ["src/**/*.ts"], - reporter: ["lcov", "text"], + reporter: [["lcov", { projectRoot: "../../../" }], "text"], }, }, }); From c058c187c46d0d1516fad859a6cab025600512d7 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 13 Mar 2026 16:19:27 +0000 Subject: [PATCH 136/211] Tidy up --- modules/widget-lifecycle/element-web/README.md | 11 ----------- .../element-web/e2e/fixture/widget.html | 16 +++++++++++++++- .../element-web/src/WidgetLifecycleModule.ts | 2 +- .../widget-lifecycle/element-web/src/config.ts | 8 ++++---- .../widget-lifecycle/element-web/vite.config.ts | 2 +- .../element-web/vitest.config.ts | 2 +- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/modules/widget-lifecycle/element-web/README.md b/modules/widget-lifecycle/element-web/README.md index 7cc0bc734c..cfe2569e4e 100644 --- a/modules/widget-lifecycle/element-web/README.md +++ b/modules/widget-lifecycle/element-web/README.md @@ -61,14 +61,3 @@ This is useful when widgets have multiple routes or capabilities include variabl } } ``` - -## Copyright & License - -Copyright (c) 2025 New Vector Ltd - -This software is multi licensed by New Vector Ltd (Element). It can be used either: - -(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR - -(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). -Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/widget-lifecycle/element-web/e2e/fixture/widget.html b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html index 1667a73c44..4c293c5c6d 100644 --- a/modules/widget-lifecycle/element-web/e2e/fixture/widget.html +++ b/modules/widget-lifecycle/element-web/e2e/fixture/widget.html @@ -1,4 +1,13 @@ - + @@ -19,6 +28,7 @@ let sendEventCount = 0; window.onmessage = async (ev) => { if (ev.data.action === "capabilities") { + // Step 1: Element is asking what capabilities this widget needs. window.parent.postMessage( Object.assign( { @@ -31,6 +41,8 @@ "*", ); } else if (ev.data.action === "notify_capabilities") { + // Step 2: Capabilities have been approved. Request an OpenID token + // so we can identify the current user in step 3. window.parent.postMessage( { api: "fromWidget", @@ -42,6 +54,8 @@ "*", ); } else if (ev.data.action === "get_openid" && ev.data.response?.state === "allowed") { + // Step 3: Token granted — exchange it for the user's Matrix ID and display it. + // Tests assert on this heading to verify identity was passed through correctly. const { access_token } = ev.data.response; const hsUrl = new URLSearchParams(window.location.search).get("hsUrl"); diff --git a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts index 144a799291..ec2ca1034b 100644 --- a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts +++ b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts @@ -30,7 +30,7 @@ export default class WidgetLifecycleModule implements Module { public constructor(private api: ModuleApi) {} public async load(): Promise { - if (!this.api.widgetLifecycle?.registerPreloadApprover) { + if (!this.api.widgetLifecycle) { throw new Error( "Widget lifecycle API is not available. Update Element Web to a build that provides widget lifecycle module support.", ); diff --git a/modules/widget-lifecycle/element-web/src/config.ts b/modules/widget-lifecycle/element-web/src/config.ts index 0746ae37f1..ac22f816b2 100644 --- a/modules/widget-lifecycle/element-web/src/config.ts +++ b/modules/widget-lifecycle/element-web/src/config.ts @@ -32,16 +32,16 @@ const ModuleConfigSchema = z.partial( /** Map from URL patterns to their widget approval configuration. */ export type WidgetLifecycleModuleConfig = Record; -/** - * Parse and validate the widget lifecycle module configuration. - * Returns an empty config if the input is falsy; throws on schema violations. - */ declare module "@element-hq/element-web-module-api" { export interface Config { [CONFIG_KEY]: z.input; } } +/** + * Parse and validate the widget lifecycle module configuration. + * Returns an empty config if the input is falsy; throws on schema violations. + */ export const parseWidgetLifecycleConfig = (value: unknown): WidgetLifecycleModuleConfig => { if (!value) return {}; diff --git a/modules/widget-lifecycle/element-web/vite.config.ts b/modules/widget-lifecycle/element-web/vite.config.ts index 3e4bed43a0..cc1fa98eb7 100644 --- a/modules/widget-lifecycle/element-web/vite.config.ts +++ b/modules/widget-lifecycle/element-web/vite.config.ts @@ -1,5 +1,5 @@ /* -Copyright 2025 New Vector Ltd. +Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. diff --git a/modules/widget-lifecycle/element-web/vitest.config.ts b/modules/widget-lifecycle/element-web/vitest.config.ts index 7ec0ff7fd7..783608fb44 100644 --- a/modules/widget-lifecycle/element-web/vitest.config.ts +++ b/modules/widget-lifecycle/element-web/vitest.config.ts @@ -1,5 +1,5 @@ /* -Copyright 2025 New Vector Ltd. +Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. From 6ccf9c50475c30fd9c33b48952b69e8601c5cfc5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Mar 2026 17:32:06 +0000 Subject: [PATCH 137/211] Add playwright test for widget toggles module --- .../element-web/e2e/fixture/widget.html | 5 + ...idget-toggle-button-default-icon-linux.png | Bin 0 -> 316 bytes .../element-web/e2e/widget-toggles.spec.ts | 170 ++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 modules/widget-toggles/element-web/e2e/fixture/widget.html create mode 100644 modules/widget-toggles/element-web/e2e/snapshots/widget-toggles.spec.ts/widget-toggle-button-default-icon-linux.png create mode 100644 modules/widget-toggles/element-web/e2e/widget-toggles.spec.ts diff --git a/modules/widget-toggles/element-web/e2e/fixture/widget.html b/modules/widget-toggles/element-web/e2e/fixture/widget.html new file mode 100644 index 0000000000..c2c28e414e --- /dev/null +++ b/modules/widget-toggles/element-web/e2e/fixture/widget.html @@ -0,0 +1,5 @@ + + +

This is the content of the widget

+ + diff --git a/modules/widget-toggles/element-web/e2e/snapshots/widget-toggles.spec.ts/widget-toggle-button-default-icon-linux.png b/modules/widget-toggles/element-web/e2e/snapshots/widget-toggles.spec.ts/widget-toggle-button-default-icon-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..a171d0adc8a83fb9044826c752b1388649dd8e3e GIT binary patch literal 316 zcmV-C0mJ@@P)I(iwuFGgMGf-J@l+%4My(OhpCN8G@igq}HZd8rTN|GRzEvFxVcSp~EMvi6b<3YF8Y>=7F{^WX($U&Ad>UYQt zmdfqe9?oO|2u3ie3|Rnzk;;hnxV!#e6OXIKyjm&4;r0H^oF(_cr2hjKkcad;;z(7J zDG0vQffaub8=35v1(VH8rj8(k#ouGtYa4ovbl~oSNo9~}kWSr3aCh0al_cA9&`~U2 ziCL5mui~&M54?)QqNKbMvq; +}>({ + navigationJsonResolver: async ({}, use) => { + await use(Promise.withResolvers()); + }, +}); + +const TEST_WIDGET_NAME = "Name of the test widget"; + +async function makeRoomWithWidgetAndGoTo( + homeserver: StartedHomeserverContainer, + user: Credentials, + page: Page, + avatarUrl?: string, +): Promise { + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + user.accessToken, + { + name: "Come on in we've got widgets", + }, + ); + await homeserver.csApi.request<{ + event_id: string; + }>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, user.accessToken, { + id: "1", + creatorUserId: user.userId, + type: "m.custom", + name: TEST_WIDGET_NAME, + url: `http://localhost:8080/widget.html`, + avatar_url: avatarUrl, + }); + + await page.goto(`/#/room/${roomId}`); + + return roomId; +} + +async function moveWidgetToContainer( + homeserver: StartedHomeserverContainer, + user: Credentials, + roomId: string, + container: Container, +): Promise { + await homeserver.csApi.request( + "PUT", + `/v3/user/${encodeURIComponent(user.userId)}/rooms/${encodeURIComponent(roomId)}/account_data/im.vector.web.settings`, + user.accessToken, + { + "Widgets.layout": { + widgets: { + "1": { container }, + }, + }, + }, + ); +} + +test.describe("widget-toggles", () => { + test.use({ + displayName: "Timmy", + page: async ({ context, page, moduleDir }, use) => { + await context.route("http://localhost:8080/widget.html*", async (route) => { + await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" }); + }); + + await context.route("http://localhost:8080/wigeon.png", async (route) => { + await route.fulfill({ path: `${moduleDir}/e2e/fixture/wigeon.png`, contentType: "image/png" }); + }); + + await page.goto("/"); + await use(page); + }, + }); + + test("should error if config is missing", async ({ page }) => { + await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + await expect(page.getByText("Errors in module configuration")).toBeVisible(); + // We don't take a screenshot as we don't want to assert Element's styling, only our own + }); + + test.describe("with correct config", () => { + test.use({ + config: { + "io.element.element-web-modules.widget-toggles": { + types: ["m.custom"], + }, + }, + }); + + test( + "should render 'show' button for widget not in top", + { tag: ["@screenshot"] }, + async ({ homeserver, page, user }) => { + await makeRoomWithWidgetAndGoTo(homeserver, user, page); + + await expect(page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME })).toBeVisible(); + }, + ); + + test( + "should render 'hide' button for widget in top", + { tag: ["@screenshot"] }, + async ({ homeserver, page, user }) => { + const roomId = await makeRoomWithWidgetAndGoTo(homeserver, user, page); + + await moveWidgetToContainer(homeserver, user, roomId, "top"); + + await expect(page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME })).toBeVisible(); + }, + ); + + test("should move widget to top when 'show' button is clicked", async ({ homeserver, page, user }) => { + await makeRoomWithWidgetAndGoTo(homeserver, user, page); + + await page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME }).click(); + await expect(page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME })).toBeVisible(); + + await expect(page.locator('iframe[title="Name of the test widget"]')).toBeVisible(); + }); + + test("should move widget to left when 'hide' button is clicked", async ({ homeserver, page, user }) => { + const roomId = await makeRoomWithWidgetAndGoTo(homeserver, user, page); + await moveWidgetToContainer(homeserver, user, roomId, "top"); + + await expect(page.locator('iframe[title="Name of the test widget"]')).toBeVisible(); + + await page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME }).click(); + + await expect(page.locator('iframe[title="Name of the test widget"]')).not.toBeVisible(); + }); + + test("uses widget icon for button image if present", async ({ homeserver, page, user }) => { + await makeRoomWithWidgetAndGoTo(homeserver, user, page, "mxc://fakehomeserver/fake_content_id"); + + await expect( + page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME }).getByRole("img"), + ).toHaveAttribute("src", /\/_matrix\/media\/v3\/download\/fakehomeserver\/fake_content_id$/); + }); + + test( + "uses built in icon for widgets with no avatar", + { tag: ["@screenshot"] }, + async ({ homeserver, page, user }) => { + await makeRoomWithWidgetAndGoTo(homeserver, user, page); + + await expect(page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME })).toMatchScreenshot( + "widget-toggle-button-default-icon.png", + ); + }, + ); + }); +}); From 084d74593c83a9ae86e54530749c923c9a9dbc88 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Mar 2026 18:29:39 +0000 Subject: [PATCH 138/211] Add test for config.ts --- .../widget-toggles/element-web/package.json | 5 ++- .../element-web/tests/config.test.ts | 43 +++++++++++++++++++ .../element-web/vitest.config.ts | 26 +++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 modules/widget-toggles/element-web/tests/config.test.ts create mode 100644 modules/widget-toggles/element-web/vitest.config.ts diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 2a9c15e3ae..faef7f8cde 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -9,7 +9,7 @@ "prepare": "vite build", "lint:types": "tsc --noEmit", "lint:codestyle": "echo 'handled by lint:eslint'", - "test": "echo no tests yet" + "test": "vitest run --coverage" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", @@ -22,7 +22,8 @@ "typescript": "^5.7.3", "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.25.0", - "vite-plugin-svgr": "^4.3.0" + "vite-plugin-svgr": "^4.3.0", + "vitest": "^4.0.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^6.0.0", diff --git a/modules/widget-toggles/element-web/tests/config.test.ts b/modules/widget-toggles/element-web/tests/config.test.ts new file mode 100644 index 0000000000..7863ffadce --- /dev/null +++ b/modules/widget-toggles/element-web/tests/config.test.ts @@ -0,0 +1,43 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { describe, expect, test } from "vitest"; + +import { WidgetTogglesConfig } from "../src/config"; + +describe("WidgetTogglesConfig", () => { + test("parses a valid config with an array of widget types", () => { + const result = WidgetTogglesConfig.safeParse({ types: ["m.video", "m.audio"] }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.types).toEqual(["m.video", "m.audio"]); + } + }); + + test("parses a valid config with an empty types array", () => { + const result = WidgetTogglesConfig.safeParse({ types: [] }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.types).toEqual([]); + } + }); + + test("rejects a config missing the types field", () => { + const result = WidgetTogglesConfig.safeParse({}); + expect(result.success).toBe(false); + }); + + test("rejects a config where types is not an array", () => { + const result = WidgetTogglesConfig.safeParse({ types: "m.video" }); + expect(result.success).toBe(false); + }); + + test("rejects a config where types contains non-string values", () => { + const result = WidgetTogglesConfig.safeParse({ types: [1, 2, 3] }); + expect(result.success).toBe(false); + }); +}); diff --git a/modules/widget-toggles/element-web/vitest.config.ts b/modules/widget-toggles/element-web/vitest.config.ts new file mode 100644 index 0000000000..783608fb44 --- /dev/null +++ b/modules/widget-toggles/element-web/vitest.config.ts @@ -0,0 +1,26 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig } from "vitest/config"; +import { env } from "node:process"; + +const isGHA = env["GITHUB_ACTIONS"] !== undefined; + +export default defineConfig({ + test: { + include: ["tests/**/*.test.ts"], + exclude: ["./e2e/**/*", "./node_modules/**/*"], + reporters: isGHA + ? ["default", ["vitest-sonar-reporter", { outputFile: "coverage/sonar-report.xml" }]] + : ["default"], + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + reporter: [["lcov", { projectRoot: "../../../" }], "text"], + }, + }, +}); From 7a73183334b068021952a9a6c6307c881a5d757e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Mar 2026 18:36:55 +0000 Subject: [PATCH 139/211] Add deps --- modules/widget-toggles/element-web/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index faef7f8cde..3ae308984a 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -17,13 +17,15 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", + "@vitest/coverage-v8": "^4.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-svgr": "^4.3.0", - "vitest": "^4.0.0" + "vitest": "^4.0.0", + "vitest-sonar-reporter": "^2.0.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^6.0.0", From 055a127841dfa9aff9d4adb6df302cddbd827551 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Mar 2026 17:58:23 +0000 Subject: [PATCH 140/211] Pin react deps to exact version in package.json Yarn had somehow confused itself and got into a state where it refused to install react 19.2.4, so much so that even renovate thought it had upgraded the package (https://github.com/element-hq/element-modules/pull/208) when it hadn't. Until we switch to pnom and can use catalog:, pin them to exact version which seems to make yarn play ball again. --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 4 ++-- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index c78181b379..df45f81985 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -17,7 +17,7 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "react": "^19", + "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 653df09fa7..c0fa58b261 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -22,8 +22,8 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "react": "^19", - "react-dom": "^19", + "react": "19.2.4", + "react-dom": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 8587318f64..dfe5c0f0f3 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -17,7 +17,7 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "react": "^19", + "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", From dd2a125d52760170d48857c91dbb10ebb136c036 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Mar 2026 18:51:40 +0000 Subject: [PATCH 141/211] Add unit test for the toggle component Using playwright-provided chromium via vitest + testing library --- .../widget-toggles/element-web/package.json | 4 + .../element-web/tests/setupTests.ts | 13 ++ .../element-web/tests/toggle.test.tsx | 111 ++++++++++++++++++ .../element-web/tests/tsconfig.json | 7 ++ .../element-web/vitest.config.ts | 10 +- 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 modules/widget-toggles/element-web/tests/setupTests.ts create mode 100644 modules/widget-toggles/element-web/tests/toggle.test.tsx create mode 100644 modules/widget-toggles/element-web/tests/tsconfig.json diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 3ae308984a..b4f1a3e1bf 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -14,9 +14,13 @@ "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", "@element-hq/element-web-module-api": "^1.0.0", + "@testing-library/dom": "^10.4.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", + "@vitest/browser-playwright": "4.0.18", "@vitest/coverage-v8": "^4.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", diff --git a/modules/widget-toggles/element-web/tests/setupTests.ts b/modules/widget-toggles/element-web/tests/setupTests.ts new file mode 100644 index 0000000000..d7601a1499 --- /dev/null +++ b/modules/widget-toggles/element-web/tests/setupTests.ts @@ -0,0 +1,13 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { afterEach } from "vitest"; +import { cleanup } from "@testing-library/react"; + +afterEach(() => { + cleanup(); +}); diff --git a/modules/widget-toggles/element-web/tests/toggle.test.tsx b/modules/widget-toggles/element-web/tests/toggle.test.tsx new file mode 100644 index 0000000000..3f115b9579 --- /dev/null +++ b/modules/widget-toggles/element-web/tests/toggle.test.tsx @@ -0,0 +1,111 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { type WidgetApi, type I18nApi } from "@element-hq/element-web-module-api"; +import { type IWidget } from "matrix-widget-api"; +import { type PropsWithChildren } from "react"; +import { TooltipProvider } from "@vector-im/compound-web"; +import userEvent from "@testing-library/user-event"; + +import { WidgetToggle } from "../src/toggle"; + +const roomId = "!room:example.com"; + +const mockWidget = (overrides: Partial = {}): IWidget => ({ + id: "widget-1", + creatorUserId: "@user:example.com", + type: "m.custom", + name: "My Widget", + url: "https://example.com", + ...overrides, +}); + +const mockWidgetApi = (overrides: Partial = {}): WidgetApi => + ({ + getAppAvatarUrl: vi.fn().mockReturnValue(null), + isAppInContainer: vi.fn().mockReturnValue(false), + moveAppToContainer: vi.fn(), + ...overrides, + }) as unknown as WidgetApi; + +const mockI18nApi = (): I18nApi => + ({ + translate: vi.fn().mockImplementation((key: string, vars?: Record) => { + let result = key; + if (vars) { + for (const [k, v] of Object.entries(vars)) { + result = result.replace(`%(${k})s`, v); + } + } + return result; + }), + }) as unknown as I18nApi; + +const wrapper = ({ children }: PropsWithChildren): React.JSX.Element => {children}; + +describe("WidgetToggle", () => { + let widgetApi: WidgetApi; + let i18nApi: I18nApi; + let app: IWidget; + + beforeEach(() => { + widgetApi = mockWidgetApi(); + i18nApi = mockI18nApi(); + app = mockWidget(); + }); + + test("displays avatar image when widget has an avatar URL", () => { + (widgetApi.getAppAvatarUrl as ReturnType).mockReturnValue("https://example.com/avatar.png"); + render(, { wrapper }); + const img = screen.getByRole("img", { name: app.name }); + expect(img).toBeDefined(); + expect(img.getAttribute("src")).toBe("https://example.com/avatar.png"); + }); + + test("renders the Jitsi avatar for Jitsi widgets", () => { + app = mockWidget({ type: "m.jitsi", name: "Jitsi" }); + render(, { wrapper }); + const img = screen.getByRole("img", { name: "Jitsi" }); + expect(img.getAttribute("src")).toMatch(/^data:image\/svg\+xml;base64,/); + }); + + test("shows 'Show' label when widget is not in container", () => { + (widgetApi.isAppInContainer as ReturnType).mockReturnValue(false); + render(, { wrapper }); + const button = screen.getByRole("button", { name: "Show My Widget" }); + expect(button).toBeDefined(); + }); + + test("shows 'Hide' label when widget is in container", () => { + (widgetApi.isAppInContainer as ReturnType).mockReturnValue(true); + render(, { wrapper }); + const button = screen.getByRole("button", { name: "Hide My Widget" }); + expect(button).toBeDefined(); + }); + + test("calls moveAppToContainer with 'top' when widget is not in container and button is clicked", async () => { + const user = userEvent.setup(); + + (widgetApi.isAppInContainer as ReturnType).mockReturnValue(false); + render(, { wrapper }); + const button = screen.getByRole("button", { name: "Show My Widget" }); + await user.click(button); + expect(widgetApi.moveAppToContainer).toHaveBeenCalledWith(app, "top", roomId); + }); + + test("calls moveAppToContainer with 'right' when widget is in container and button is clicked", async () => { + const user = userEvent.setup(); + + (widgetApi.isAppInContainer as ReturnType).mockReturnValue(true); + render(, { wrapper }); + const button = screen.getByRole("button", { name: "Hide My Widget" }); + await user.click(button); + expect(widgetApi.moveAppToContainer).toHaveBeenCalledWith(app, "right", roomId); + }); +}); diff --git a/modules/widget-toggles/element-web/tests/tsconfig.json b/modules/widget-toggles/element-web/tests/tsconfig.json new file mode 100644 index 0000000000..f5eb2ca359 --- /dev/null +++ b/modules/widget-toggles/element-web/tests/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "jsx": "react-jsx" + }, + "include": ["."] +} diff --git a/modules/widget-toggles/element-web/vitest.config.ts b/modules/widget-toggles/element-web/vitest.config.ts index 783608fb44..4a5b5a32cc 100644 --- a/modules/widget-toggles/element-web/vitest.config.ts +++ b/modules/widget-toggles/element-web/vitest.config.ts @@ -7,12 +7,13 @@ Please see LICENSE files in the repository root for full details. import { defineConfig } from "vitest/config"; import { env } from "node:process"; +import { playwright } from "@vitest/browser-playwright"; const isGHA = env["GITHUB_ACTIONS"] !== undefined; export default defineConfig({ test: { - include: ["tests/**/*.test.ts"], + include: ["tests/**/*.test.{ts,tsx}"], exclude: ["./e2e/**/*", "./node_modules/**/*"], reporters: isGHA ? ["default", ["vitest-sonar-reporter", { outputFile: "coverage/sonar-report.xml" }]] @@ -22,5 +23,12 @@ export default defineConfig({ include: ["src/**/*.ts"], reporter: [["lcov", { projectRoot: "../../../" }], "text"], }, + browser: { + enabled: true, + headless: true, + provider: playwright({}), + instances: [{ browser: "chromium" }], + }, + setupFiles: ["tests/setupTests.ts"], }, }); From e936ba7d8248b5ca59cb8821b2e3dc207ecb854e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Mar 2026 10:46:28 +0000 Subject: [PATCH 142/211] Also a test for the module entrypoint --- .../element-web/tests/index.test.ts | 175 ++++++++++++++++++ .../widget-toggles/element-web/tests/mocks.ts | 45 +++++ .../element-web/tests/toggle.test.tsx | 33 +--- 3 files changed, 222 insertions(+), 31 deletions(-) create mode 100644 modules/widget-toggles/element-web/tests/index.test.ts create mode 100644 modules/widget-toggles/element-web/tests/mocks.ts diff --git a/modules/widget-toggles/element-web/tests/index.test.ts b/modules/widget-toggles/element-web/tests/index.test.ts new file mode 100644 index 0000000000..d1258e56a2 --- /dev/null +++ b/modules/widget-toggles/element-web/tests/index.test.ts @@ -0,0 +1,175 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { type Api } from "@element-hq/element-web-module-api"; +import { type IWidget } from "matrix-widget-api"; + +import WidgetToggleModule from "../src/index"; +import { CONFIG_KEY, WidgetTogglesConfig } from "../src/config"; +import { mockWidget, mockWidgetApi } from "./mocks"; + +const makeApi = (widgets: IWidget[] = []): Api => { + const addRoomHeaderButtonCallback = vi.fn(); + return { + config: { + get: vi.fn().mockReturnValue({ types: ["m.custom"] }), + }, + extras: { + addRoomHeaderButtonCallback, + }, + widget: mockWidgetApi({ + getWidgetsInRoom: vi.fn().mockReturnValue(widgets), + }), + i18n: { + translate: vi.fn().mockImplementation((key: string) => key), + }, + } as unknown as Api; +}; + +vi.mock("../src/config", async () => { + return { + CONFIG_KEY: "fake_config_key", + WidgetTogglesConfig: { + parse: vi.fn(), + }, + }; +}); + +describe("WidgetToggleModule", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("load", () => { + test("reads config using CONFIG_KEY", async () => { + const api = makeApi(); + + const module = new WidgetToggleModule(api); + await module.load(); + + expect(api.config.get).toHaveBeenCalledWith(CONFIG_KEY); + }); + + test("parses config with WidgetTogglesConfig.parse", async () => { + const api = makeApi(); + const rawConfig = { types: ["m.custom"] }; + (api.config.get as ReturnType).mockReturnValue(rawConfig); + + const module = new WidgetToggleModule(api); + await module.load(); + + expect(WidgetTogglesConfig.parse).toHaveBeenCalledWith(rawConfig); + }); + + test("registers a room header button callback", async () => { + const api = makeApi(); + (WidgetTogglesConfig.parse as ReturnType).mockReturnValue({ types: ["m.custom"] }); + + const module = new WidgetToggleModule(api); + await module.load(); + + expect(api.extras.addRoomHeaderButtonCallback).toHaveBeenCalledOnce(); + }); + + test("throws error when config parsing fails", async () => { + const api = makeApi(); + (WidgetTogglesConfig.parse as ReturnType).mockImplementation(() => { + throw new Error("Invalid config"); + }); + + const module = new WidgetToggleModule(api); + await expect(module.load()).rejects.toThrow("Errors in module configuration for widget toggles module"); + }); + }); + + describe("room header button callback", () => { + const roomId = "!room:example.com"; + + const getCallback = async (api: Api): Promise<(roomId: string) => React.JSX.Element | undefined> => { + (WidgetTogglesConfig.parse as ReturnType).mockReturnValue({ types: ["m.custom"] }); + const module = new WidgetToggleModule(api); + await module.load(); + return (api.extras.addRoomHeaderButtonCallback as ReturnType).mock.calls[0][0]; + }; + + test("returns undefined when there are no widgets in the room", async () => { + const api = makeApi([]); + const callback = await getCallback(api); + + const result = callback(roomId); + expect(result).toBeUndefined(); + }); + + test("returns undefined when no widgets match the configured types", async () => { + const api = makeApi([mockWidget({ type: "m.other" })]); + const callback = await getCallback(api); + + const result = callback(roomId); + expect(result).toBeUndefined(); + }); + + test("renders WidgetToggle for each matching widget", async () => { + const api = makeApi([ + mockWidget({ id: "w1", type: "m.custom", name: "Widget One" }), + mockWidget({ id: "w2", type: "m.custom", name: "Widget Two" }), + ]); + (api.i18n.translate as ReturnType).mockImplementation( + (key: string, vars?: Record) => { + let result = key; + if (vars) { + for (const [k, v] of Object.entries(vars)) { + result = result.replace(`%(${k})s`, v); + } + } + return result; + }, + ); + const callback = await getCallback(api); + + const result = callback(roomId); + expect(result).toBeDefined(); + render(result!); + + expect(screen.getAllByRole("button").length).toBe(2); + }); + + test("does not render WidgetToggle for non-matching widget types", async () => { + const api = makeApi([ + mockWidget({ id: "w1", type: "m.custom", name: "Widget One" }), + mockWidget({ id: "w2", type: "m.other", name: "Widget Other" }), + ]); + (api.i18n.translate as ReturnType).mockImplementation( + (key: string, vars?: Record) => { + let result = key; + if (vars) { + for (const [k, v] of Object.entries(vars)) { + result = result.replace(`%(${k})s`, v); + } + } + return result; + }, + ); + const callback = await getCallback(api); + + const result = callback(roomId); + expect(result).toBeDefined(); + render(result!); + + expect(screen.getAllByRole("button").length).toBe(1); + }); + + test("calls getWidgetsInRoom with correct roomId", async () => { + const api = makeApi([]); + const callback = await getCallback(api); + + callback(roomId); + expect(api.widget.getWidgetsInRoom).toHaveBeenCalledWith(roomId); + }); + }); +}); diff --git a/modules/widget-toggles/element-web/tests/mocks.ts b/modules/widget-toggles/element-web/tests/mocks.ts new file mode 100644 index 0000000000..e37cd67b01 --- /dev/null +++ b/modules/widget-toggles/element-web/tests/mocks.ts @@ -0,0 +1,45 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type I18nApi, type WidgetApi } from "@element-hq/element-web-module-api"; +import { type IWidget } from "matrix-widget-api"; +import { vi } from "vitest"; + +export function mockWidget(overrides: Partial = {}): IWidget { + return { + id: "widget-1", + creatorUserId: "@user:example.com", + type: "m.custom", + name: "My Widget", + url: "https://example.com", + ...overrides, + }; +} + +export function mockWidgetApi(overrides: Partial = {}): WidgetApi { + return { + getWidgetsInRoom: vi.fn().mockReturnValue([]), + getAppAvatarUrl: vi.fn().mockReturnValue(null), + isAppInContainer: vi.fn().mockReturnValue(false), + moveAppToContainer: vi.fn(), + ...overrides, + } as unknown as WidgetApi; +} + +export function mockI18nApi(): I18nApi { + return { + translate: vi.fn().mockImplementation((key: string, vars?: Record) => { + let result = key; + if (vars) { + for (const [k, v] of Object.entries(vars)) { + result = result.replace(`%(${k})s`, v); + } + } + return result; + }), + } as unknown as I18nApi; +} diff --git a/modules/widget-toggles/element-web/tests/toggle.test.tsx b/modules/widget-toggles/element-web/tests/toggle.test.tsx index 3f115b9579..0a0e40449f 100644 --- a/modules/widget-toggles/element-web/tests/toggle.test.tsx +++ b/modules/widget-toggles/element-web/tests/toggle.test.tsx @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { beforeEach, describe, expect, test, vi } from "vitest"; +import { beforeEach, describe, expect, test, type vi } from "vitest"; import { render, screen } from "@testing-library/react"; import { type WidgetApi, type I18nApi } from "@element-hq/element-web-module-api"; import { type IWidget } from "matrix-widget-api"; @@ -14,39 +14,10 @@ import { TooltipProvider } from "@vector-im/compound-web"; import userEvent from "@testing-library/user-event"; import { WidgetToggle } from "../src/toggle"; +import { mockI18nApi, mockWidget, mockWidgetApi } from "./mocks"; const roomId = "!room:example.com"; -const mockWidget = (overrides: Partial = {}): IWidget => ({ - id: "widget-1", - creatorUserId: "@user:example.com", - type: "m.custom", - name: "My Widget", - url: "https://example.com", - ...overrides, -}); - -const mockWidgetApi = (overrides: Partial = {}): WidgetApi => - ({ - getAppAvatarUrl: vi.fn().mockReturnValue(null), - isAppInContainer: vi.fn().mockReturnValue(false), - moveAppToContainer: vi.fn(), - ...overrides, - }) as unknown as WidgetApi; - -const mockI18nApi = (): I18nApi => - ({ - translate: vi.fn().mockImplementation((key: string, vars?: Record) => { - let result = key; - if (vars) { - for (const [k, v] of Object.entries(vars)) { - result = result.replace(`%(${k})s`, v); - } - } - return result; - }), - }) as unknown as I18nApi; - const wrapper = ({ children }: PropsWithChildren): React.JSX.Element => {children}; describe("WidgetToggle", () => { From 242d04cef62869039a3a9ee695101bcd1ab81273 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 23 Mar 2026 12:41:34 +0530 Subject: [PATCH 143/211] Update screenshots --- .../preview-bar-linux.png | Bin 4055 -> 4048 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index 1771b8e67d756bdab76549a8b0fe543af2a71d07..877e466ef5fd5aec6eb00816dd1d9bc1a6e90ede 100644 GIT binary patch literal 4048 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 Date: Mon, 23 Mar 2026 11:38:04 +0000 Subject: [PATCH 144/211] update moduleApiVersion to enforce module is actually present. --- .../widget-lifecycle/element-web/src/WidgetLifecycleModule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts index ec2ca1034b..6ad15e08bd 100644 --- a/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts +++ b/modules/widget-lifecycle/element-web/src/WidgetLifecycleModule.ts @@ -23,7 +23,7 @@ type ModuleApi = Pick; * requests based on URL-pattern rules defined in config.json. */ export default class WidgetLifecycleModule implements Module { - public static readonly moduleApiVersion = "^1.0.0"; + public static readonly moduleApiVersion = "^1.10.0"; private config: WidgetLifecycleModuleConfig = {}; From 5d25bd08a1340f2a3a51b9504a634c1771bf0c18 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 23 Mar 2026 23:46:48 +0000 Subject: [PATCH 145/211] Trigger build From 0607a32595acbb88e74f7e26992fa8639115c0c8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 09:43:09 +0100 Subject: [PATCH 146/211] Stabilise banner h2 --- modules/banner/element-web/src/Menu.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/banner/element-web/src/Menu.tsx b/modules/banner/element-web/src/Menu.tsx index db46c76a82..b984c1b58b 100644 --- a/modules/banner/element-web/src/Menu.tsx +++ b/modules/banner/element-web/src/Menu.tsx @@ -89,6 +89,8 @@ const CloseButton = styled.button` `; const CategoryHeading = styled.h2` + line-height: var(--cpd-font-line-height-tight); + letter-spacing: var(--cpd-font-letter-spacing-heading-lg); font-weight: 700; font-size: 12px; color: ${({ theme }): string => theme.subheadingColor}; From 557f03a80ddf3cf8c2b8587b14d7a341ee3a73fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 09:53:40 +0100 Subject: [PATCH 147/211] Update screenshots --- .../dialog-linux.png | Bin 11810 -> 9971 bytes .../preview-bar-linux.png | Bin 5179 -> 5165 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png index 89cd54c942923977f2fb31d770ff2d2d7d9f8c9c..aa4f5c37ca9bccd885b2ee4fb1e2b22a96ca6b63 100644 GIT binary patch literal 9971 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 H48H#_Bao{5 diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index f556a718060ad9b4e345320a45276b49af262163..d08ce9721c34f7f0b309b1fb26bb969b9dccb7db 100644 GIT binary patch literal 5165 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 p00960lDmvj00006Nkl Date: Tue, 31 Mar 2026 09:56:28 +0100 Subject: [PATCH 148/211] Update copyright --- modules/restricted-guests/element-web/src/AuthFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/element-web/src/AuthFooter.tsx b/modules/restricted-guests/element-web/src/AuthFooter.tsx index 4b908ecb35..4062c57645 100644 --- a/modules/restricted-guests/element-web/src/AuthFooter.tsx +++ b/modules/restricted-guests/element-web/src/AuthFooter.tsx @@ -1,5 +1,5 @@ /* -Copyright 2025 New Vector Ltd. +Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. From 550f9062d52ee38a8c729f462b194e4507324a35 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 10:02:36 +0100 Subject: [PATCH 149/211] Update screenshots --- .../element-web/e2e/restricted-guests.spec.ts | 2 +- .../dialog-linux.png | Bin 9971 -> 12352 bytes .../dialog-login-linux.png | Bin 0 -> 9971 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-login-linux.png diff --git a/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts b/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts index 47ac837bc9..7d7bc3e09e 100644 --- a/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts @@ -291,7 +291,7 @@ for (const auth of ["mas", "legacy"] as const) { await button.click(); const dialog = page.getByRole("dialog"); - await expect(dialog).toMatchScreenshot(`dialog.png`); + await expect(dialog).toMatchScreenshot(`dialog-login.png`); await dialog.getByPlaceholder("Name").fill("Jim"); await dialog.getByRole("button", { name: "Continue as guest" }).click(); diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png index aa4f5c37ca9bccd885b2ee4fb1e2b22a96ca6b63..a2b5cb2be86016be1d0a70ba7169ed55c348870d 100644 GIT binary patch literal 12352 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 Date: Tue, 31 Mar 2026 11:45:36 +0100 Subject: [PATCH 150/211] Debug screenshot --- modules/banner/element-web/e2e/banner.spec.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/banner/element-web/e2e/banner.spec.ts b/modules/banner/element-web/e2e/banner.spec.ts index eddc2d67e1..d60be88334 100644 --- a/modules/banner/element-web/e2e/banner.spec.ts +++ b/modules/banner/element-web/e2e/banner.spec.ts @@ -153,15 +153,7 @@ test.describe("Banner", () => { // Assert the sidebar looks as we expect await expect(axe).toHaveNoViolations(); await expect(sidebar).toMatchAriaSnapshot(); - await expect(page).toMatchScreenshot(`${type}_menu.png`, { - // We exclude this as we don't want to assert Element's styling, only our own - css: ` - #matrixchat { - opacity: 0; - background: orchid; - } - `, - }); + await expect(page).toMatchScreenshot(`${type}_menu.png`); }); await test.step("close menu", async () => { From 21396c25be99b4cb555b6ef9c64fe51887a8347d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 12:05:17 +0100 Subject: [PATCH 151/211] Iterate styles --- modules/banner/element-web/src/index.tsx | 2 ++ modules/banner/element-web/src/style.css | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 modules/banner/element-web/src/style.css diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index 447fbf4969..f4196af06f 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -13,6 +13,7 @@ import Translations from "./translations.json"; import { ModuleConfig, CONFIG_KEY } from "./config"; import Banner from "./Banner"; import { name as ModuleName } from "../package.json"; +import style from "./style.css" with { type: "css" }; class BannerModule implements Module { public static readonly moduleApiVersion = "^1.0.0"; @@ -23,6 +24,7 @@ class BannerModule implements Module { public async load(): Promise { document.adoptedStyleSheets.push(compound); + document.adoptedStyleSheets.push(style); this.api.i18n.register(Translations); diff --git a/modules/banner/element-web/src/style.css b/modules/banner/element-web/src/style.css new file mode 100644 index 0000000000..6e573485f5 --- /dev/null +++ b/modules/banner/element-web/src/style.css @@ -0,0 +1,15 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/* Styles to ensure the banner does not push the app out of the viewport */ +body { + display: flex; + flex-direction: column; +} +#matrixchat { + flex: 1; +} From f2608caf345c94ad2a3d344fa4085f71fd65ba04 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 12:32:27 +0100 Subject: [PATCH 152/211] Iterate --- modules/banner/element-web/e2e/banner.spec.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/banner/element-web/e2e/banner.spec.ts b/modules/banner/element-web/e2e/banner.spec.ts index d60be88334..fd5021e95e 100644 --- a/modules/banner/element-web/e2e/banner.spec.ts +++ b/modules/banner/element-web/e2e/banner.spec.ts @@ -153,7 +153,22 @@ test.describe("Banner", () => { // Assert the sidebar looks as we expect await expect(axe).toHaveNoViolations(); await expect(sidebar).toMatchAriaSnapshot(); - await expect(page).toMatchScreenshot(`${type}_menu.png`); + await expect(page).toMatchScreenshot(`${type}_menu.png`, { + // We exclude this as we don't want to assert Element's styling, only our own + css: ` + #matrixchat { + opacity: 0; + background: orchid; + } + `, + }); + + // Verify that the #matrixchat root got shrunk to fit rather than exploding the viewport + const bodyBoundingBox = await page.locator("body").boundingBox(); + const rootBoundingBox = await page.locator("#matrixchat").boundingBox(); + const headerElement = await page.locator("#matrixchat").evaluate((e) => e.previousElementSibling); + const headerBoundingBox = headerElement?.getBoundingClientRect(); + expect(rootBoundingBox!.height + headerBoundingBox!.height).toEqual(bodyBoundingBox!.height); }); await test.step("close menu", async () => { From e772a104c23e8d70638d601217c8c3ef64b04329 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2026 13:17:57 +0100 Subject: [PATCH 153/211] Iterate --- modules/banner/element-web/e2e/banner.spec.ts | 4 ++-- modules/banner/element-web/src/index.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/banner/element-web/e2e/banner.spec.ts b/modules/banner/element-web/e2e/banner.spec.ts index fd5021e95e..edaf519492 100644 --- a/modules/banner/element-web/e2e/banner.spec.ts +++ b/modules/banner/element-web/e2e/banner.spec.ts @@ -166,8 +166,8 @@ test.describe("Banner", () => { // Verify that the #matrixchat root got shrunk to fit rather than exploding the viewport const bodyBoundingBox = await page.locator("body").boundingBox(); const rootBoundingBox = await page.locator("#matrixchat").boundingBox(); - const headerElement = await page.locator("#matrixchat").evaluate((e) => e.previousElementSibling); - const headerBoundingBox = headerElement?.getBoundingClientRect(); + const headerBoundingBox = await page.getByTestId("banner").boundingBox(); + expect(rootBoundingBox!.height).toBeLessThan(bodyBoundingBox!.height); expect(rootBoundingBox!.height + headerBoundingBox!.height).toEqual(bodyBoundingBox!.height); }); diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index f4196af06f..538fb8b3af 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -36,6 +36,7 @@ class BannerModule implements Module { } const div = document.createElement("div"); + div.dataset.testid = "banner"; this.api.rootNode.before(div); const root = this.api.createRoot(div); From 60698a2b7824b6cf6a36e9b3816a7d8a31b19718 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:34:08 +0000 Subject: [PATCH 154/211] Update dependency @vector-im/compound-design-tokens to v8 --- modules/banner/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index df45f81985..0fa0eecb03 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", - "@vector-im/compound-design-tokens": "^6.0.0", + "@vector-im/compound-design-tokens": "^8.0.0", "@vector-im/compound-web": "^8.0.0", "framer-motion": "^12.4.10", "styled-components": "^6.1.18", diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index dfe5c0f0f3..ab4c4d9469 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -24,7 +24,7 @@ "vite-plugin-node-polyfills": "^0.25.0" }, "dependencies": { - "@vector-im/compound-design-tokens": "^6.0.0", + "@vector-im/compound-design-tokens": "^8.0.0", "@vector-im/compound-web": "^8.0.0", "styled-components": "^6.1.18", "zod": "^4.0.0" From 52233836989ce9c311fe2b86e95d8360b5259b2c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 2 Apr 2026 15:26:26 +0100 Subject: [PATCH 155/211] Update screenshots --- .../banner.spec.ts/static-menu-linux.png | Bin 12708 -> 12675 bytes .../banner.spec.ts/univention-error-linux.png | Bin 8472 -> 8523 bytes .../univention-menu-loading-linux.png | Bin 6278 -> 5813 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-menu-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/static-menu-linux.png index e6e47a126002dfe46ef192060a8866f8ea713261..81982d60c09dfb43a77e82ac92b3f40bbb6c230d 100644 GIT binary patch literal 12675 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%0AdrNrRBLAsCquxkQkvFnQRZs2x3lzuVavm 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 literal 12708 zcmeHuX;f3$wr-Fr5a}!xQ4r873ls!Y1QbY6kwQd31&K%#ktWhcB=n)N6sQ6f6(J%` z5NV`GdI%&41xOnN=}7`2P3RJu5E2sdRy_BN_r|#IHowjocij4wuy^*_Ykq6aZ_fFx z#h=$KOeA;7?SeoclBSn0-GD%T1YdXb?%V-B&MRFJf!Q8WX5akpkr@NAH|;+R*Jb`bHJtBVx%6c)dw}Fk$g51L@~AAWhy-+a+-jVzxD6@vT`1BniDEx$=y?+_KLU|hWSLIMn?9C^es&ssI5JE>KP za7YdUsXHJfalIhF@G9)?AY4Ki(%Z80&L6j5|4>}`{_@}^0|~zXu4#j74#vhUF$+US zPeLFL@sQ|YSJl(m4^=LLyOm<#t{#&ldO>%#1No03KbU3Tm|lr73+i@~0PtKkHVe3o zO!KcA^TD5dea8I;+sH}}-`d&=WsPe|q=$28*=AT}#=&dH_N`44vuYlE*yy8KzwVKY zJ9&?NeHtR=iXS~CFgh>k>WqZEj@L|AvZ?GqCEp+Yb1epj)jYcN_UDA+PcF9!eD06p zR(!iM)hC^g`h1iWf!nC`WgfhfVH8C)D*7}%w$k@?cOU!Why0)WV{fSQ4rB{=3j-?> zYeWyg!qhZFedzL@&0@{&36&M2O3miPe_#!BJrX{)&@n5$GuPV*zn_1^{h%oT6nQjq z(#qyoz-3-i#oawMSEqLX1ko=b%L4^)+Cyl6j=5Qe)p4{7a{r|h5D2#!c_z}*VCAnH zy^3M$pT*GbMO%NplY+8M9HhK04nQI;isB4@48b}C$h&{k4Xax$A9!QTqo+K?=$wbv+IE^U?yU8;oBtl^Hq(D zDbDfGrlkD*{9Igk$X|c_McQzT z2-wE(e11nf8M$1~&WnG}eHWXexi=5EiprOcm^+CPEXf9dwozF)Zvmm{9n2hn8}XzR z^w!t=Ee*f^@yiu?*%PNK(3!*<9brclfDi`rGTd zh6>sq*0jbKznDZ1oNP`#lajT|kC@Hj9aF(!RsleAB2LlguAaaqWn+8v>Gd;A@wBE` z8FN_#<}=NZZR*afP~x90Ow#ZJ#CrRE5epL2E16KNt*z~35V$;k@}Rtk6Z5l)Qc+Qn zxy|-hLgpDbw@Zukc-lxBYOTk3?fmSZVK~N}JM0`EWlBD|Q|rT92P2+_LtO=0W<6lc zn^lhE6;{~Q)-~&Lun}zR^D-y58uav<=wm3VW-@xRIY#q##j!4)z%#&ti;b};p>zPZ z&cmLWb=-D^53P@y>KEYeuY=P}4O~WTPqpQyp;wQKK_I<%y(K4vrOswp6WjAgMsGmq zfL5|Ol7!X7l22A;bdqcGO~GbrEK zxdWn6eUG(F?v(FTm%FjnEZ-Spz~`=21K;x_L1pjOeE4w6{llsR4zgBwDb~_E9N1HM{;#Rp_w@_@zos`bDN>mc!HPFLK*3X=nv%k2Ij_JrF z8caRun?E<39y%@^VQtG^!J&SDKwgDqYBfSb*5AK>e=IDZ(|^c;h2n3ZoGdnTQhauO z;5>vD`Wam@s%3}_d6cs_YH>VQz+tu8ktS0i7`Zn7Pw^ua7SK?-I5#b%Lj>qj+ z*IO)Y2DEn};Wk?1ZMb3SbSjmr407jStBRg*YPTDa-Hl3WbvmtMeed-Tu=QS}PU()@ z^+CSwCm($K)OR>*@gOqQ_v4SUM?I$i`lqHWSHoMPBuRH;VW_0ScevEaDVUaxjV6b0ui>yW z4^5sNIOW#$8c=TOx)X64sSg%+83b89hy}m`jB!7bq!P@)mYLj~Z55@3nV21M#X8K- z&%eBx&}9i_TEC{LFR>APCc>%boP-V;-G7oWpI^{)HdSZe0~{=D513cx3y=1-R3m&( z>Nzc1X59wy8Ho@rQ&j)CAu7KBwrw+6yEc<+qhjkX8y5Wh4Bn6$2G#xDa&ixE0+Oztkvr89UA&^Z`AqiX=y6zX@v+9vTkcqGz2VkzrXi4*(j9Jrr< zzX`niUXC&GXCiwh<()~q)FaabW!LhNEVFxFUH}bf^PP>w5iyL2wM)~b`*qh&?2A?7 zn+-aKOdH{>lF_5(B@oC&^Np6fp5Y{sx|jbn?YMvNHAfvKPzfL>e>Mg7WF7`{hWzn6 zD8?X%ckMU;BKz3RJO8R1&n;k*@s`PY3zfPrZQSiYe0>P)}-tt z6bu6BP(sDhAmCO=#qjMP7*KkF0q11SY14eXX86Yjz$uk7Z{X)A|_wI%t8@OM8oN1w2( zGDvmeP~CF+X+^(DkY@`J%LC;CM`pF!?6m(_71cN*+7wyY6Vs7CI*ki_AoY9UfkP?TcB!kFE14K zFFjvzj*%Y2^o-H%Bi3-#RA{bVie!P4v$?rH!1??c0ZQ)uNHm?$;~2V_bR|3ejZOpG z>@D2T@ZP)KxCqV~G+R9J5kGkGPao|8VVyapi|hFd+ZCgJj+`w|#qjkNb6v%Xm5ir~ zTwHquVT)p~!H@AorRzp~JR?FL<0K-t09~#_9 z4&pcKQXZJNZ-M3G_7x6AOHkI+0%s{Yr1oQ zxO}xzZMQA~2J0G6Urj(noui!GY&_Sztx4d{CpE`}(?1zC&rg`^ZjW<#7mfKdUHzag zk~s8(smAcFBfqAX<p6Tfr@oqkoX( z)lxz#k3`}yoMhgHoK;9=V2PvC2oXUoJwaAUEiCL-bULVdW|7}fI3zD8XL!G-FCle^ zUhU}a&vGz{a(#QPhL)tS6yoyW0Bi9x)RxI88rZ%pzW;Oi*j_6U{EPaqgg_C!?J!uYVoE?;IA++><6!`wKt9*SQw;s`)6K-h$+qqOCDzq? z{t+S2D(z0(|0APth;r*ToCv&2Y8*xCOOi*`PAvse#1Yv8cx71WoypAE^oUbWOeExQ zjBa-Mkr1K%-f{aiZObZ_#ea!T%yCIsAUaZyJkk|Q3>{!bbj*qazL7f}^aWcbsTuX> zxI3w*#;+-CIxUpB_>Zt!-fY;r=%$Pe+_Id}6`T{ZTG5oyy3gVaQ5mm2XZk~I#!p5QU zu~&KhfGoyl3-Iw2S8Mkv3;ieHWwo01>mAR=P+QDL(zlk9BN%C@MFS7~K`lf`@Sx|I zmo{d6xZz@dv@nve*#S6cJfvW98rrtpVB)|l`;%p8k04THnvDOc1uWuXB3SbHC!9V% z67ghc&)6h#bM(Si#23TK_b8<*;9pqY_NW~_wPu>})<+nS{vYP^@0Lrw-}bc~<}pM~ zl2pPUfv-_NL+2tJ_D*P_1hxBssSUXmq4)~hEeb-RUZV99Euu=$ z2aZA@jr^(rq14Vz^@#&M%C{O#IK)G(vU93-`fd)FZ`oBq>UXuV1A1-cCca&52- zqLGi7dv$dONXWKv%R(BjFa6%ro+1l@bll$G2wT~yr-E{XK#tihkc74)F@)!+v9M?$ z86=p-lz{XM(YuG;dl)HBK}9WzvBufZNX*zg@1geW7x&?h6F<=xRbe7&tj zKRAANkx0^5xgkht^Wy6EJ;Gk80d6KZ+M~zAoxN}{s zRH{%04Y)FpM$kgt~Cmv}SpWYFD<4tD7q_3g^?N}-FV z)W?G-G9I!F8I767aiFty7=1v3?iGY(?CjaE(T`k)RfgX59|$JZ9lI6gmWV}rq*wk_jMMz_@S*s zt)T50^#1C%HnXu9DFs12s{)KgpHq-&Ozv=5{|J?zUyC2FLeC2`8@613dy;PoJ8tJ) z>k10Urb3iEb2)TbtTQceq_)1fJvUX)w=ZjDO1fq)bIq@IJU)CqB1sdG4hHP?_y1D3 zsPhibq#;(mT6M~jI{D0=y0kXtuq%eORcv?T#L*%xr>|Kvmf3Yv&&H~T?+(BG)ohK; zqAwP`e?Ry6orycBe5Cd6ab@6KSQpQy=5ur)IhbW*_w0ZU`ZAj#-eZWe<&{Z_cv;>zr9xO zPp9w*TZ%!OZ)31u)WbHCX-=J>krpHj2YAd6jL7bM5vjagk0f=9b|I#hoTZku*Vm|O6yk`HkDi(3eI?)_Hkd=_7EySHKC~68YN!P*l)l0Ki&Osw2meiskjnA>Y;po`cuF; z!I~Cc0Q{DC<>_#(vU~acJ*>R|qu#fpXfd>Mf?^l+( z+MQ@>q*69dx5$XQ)z{1R(_wMM)$+0(bw({5c;;Ec?_`OCsZ$$*%ug&3p5yd%dQob&+Mgtmz zDg{1)t`88OF!qx*3>ezWwue2z8lEm8ecEpC(0^j}v^K|`-cWXj{qj0)EE8o;Dm|58 zDm@Z#zqu@$vcS9o9-r9zIVa|hHoWl4v^cx(td+y{v^)t!A}y9PzUo!C`ZSx~P;)P! zxMtN9-g5VYZ}3!EUG&L&lkI^c{$0_+y6Ry|<;-m3* zf4vT@^GLf&;)%F$tZx=0`u(Vqa;s``tLFZ-(f+C)O{dQ=3XM_P-)suGNy+FX&Vmnv zy)BdI=_}0WJz`{`x3anS8GkZTQfHGq4d+hGO*ORwjX~W+f_kr6atPz~X+~V5{w!G6 z!*`e1y9+5}%#wqj@pc0p@gj~DnZ?Kfy*z6V+u{!<#^zQ>sxd9G7u`i_uG!b$XaJBEVNlqe_o&D3iH#T4rhVk+o+m2$~$k&hBB8#>%>I63Mj z?n{Yfk~HmgoSl4)VP{aPiG^IM>QhOzneJr6t?6@}Ng9^7r{fUgdMA+GA=)PliS5t* zNch9H{;o`>?l}6>5b+eI(_2cP`$vk;w$+uC6=kK)lYE&z-bAQpT zDlRDJxBCl&s|f_JK0Ltes%nFeKdV%t#LY9GBW;iBEqxlYzMNjs@wFwcxhyoE?;muG zVr9XL_aS@q7);GNG5<=iXh1e)`41H}uyYog=7*%tot4N}RTESdnO}N@Qg>N3wA>|G z`;)EKUbbHR@S*fuUTrPSOx~~dV}q!RR$_Ko$iPOSZElW_Z&=v|C1jyUt)$+AG0|PM z-DJ~UjR@=ZM~u5IQ28uZQN>dJ0Gt;-+|u#Mnv_^LcqeRY z^xV#Z{zMttRhYy3+a-1Nz8`sh98FqPFkNUe+hssSgcR!eF-5c6$D^x~+Z9OF`mnxl zPIIZ$Of_wrWTxTeSPn$|{#-*{x31;vr^#2{SY#8+-hV zje4}C_qNAzv|5V8Bv9hSqcR0rOhy(@Q_Mmo&;C@zH`o@vj4qW2cCQdJ;w|tTf*$Yl zZ1XtkR=4&vHOzTA&XTxiN9A&oI1*~ zJDX}UPix&@6KbxKx3?X5BkiR3^$(O+#ph_`HfQl#ZFe;;s3|_jA}!zxE52c^2T$as zy>rCSra?40(XvU>a%WsS`dL&yR{D@2&l-%XM;7$S9Pt--s?t5~(y!K8MHg9EHVD}A zfqGuiFTCfaYR--ho~Wh37s)wA@n$sFBv#`Esv#oF&(wuu4WZvD62FIheuB-OTC=rTut&(Xs$JQQ&x~Zvw z+zn=qxiK0deIJkMc`mI6(93vNc|1~!hSnI8BHffyY0nzt3nzo0WfYoY&m#*|<>XRq z(zAc=uan;aeB?d#{!0(|B|%DiF}klYF;T`;)!nO0-SgIq3Y*tkMa5D`4-)7 zJ`1**n`6AgKW%>C90bT&{M&p&CrRhC;~hVW$uMf=v2NR z1d#G%2v?>^OnUW9S{z+7?p+|oX*I#pP}`fcG3M>}rCJ)#X|>bX;#JbnA)S*(WDy`X zG^!}yJh6Z8VIRNQZp$KzpgeX)y%!9uM^7eM@pr-b37qS{PU8k0d&+~_)>eDMwad5F z5-gDAGKn%b4uR=NeaTt$^5|&L&zQvyo8XPB^dy! zt?N|78DT+#UNYu|=OGj^_asFfG40v%QsZZ^oxg%1|0)tjMwXLrOg*D%fsPD@sGt5l zN}A{E-dD`oSkm_z?%n2b1V3c>>Ya=X9dxT2ep{ER>R_9=>ye7`mr8nndW^&fm*ZI? zs0=fH)MM(n#q97v#LK@Ps4CG+v58d)P!J({ zIqM}s&NHzsYpW3r<;AzI1{|b92Oh3ndTkgNdfuHw5vqLVF$>sitbQys`6C2c4tc^Cn%KK4m2K7v+{dn zUJ$?^O4nB!ey5ru<)Amy>)4+A7El0_$;3KrPzA64F#jB)c)F|($T7={IZ9r9j&2M= zju0L=AKJMfo`SL?2cA!X?XK}JuAT3&vOGs)!MBNGd%uHlDUYZ5&;t2VV12DGZ4oQK zAWP&Qn@P4jfn&rNJ1N%WD<2Jn*@l+8H3WQ&lEuPVN_a$Pykr8Y?NHbk(5(jawew+j zHyKZLMg?ll;nPb>jZv7>(-cnba*5`dh4CNh-{|#T*J%TT!ILer&G2CV&Yyk>S)cp7 z+H53@Kz~FL%Vr6NXE`-ME_J5)I01{kzEIsCXX>f$ShrZSL@^z0Zm}Aq54^M6fsvc( zNx|Jta~V*I?|8IY#s)vu6W{w5eEgd_8C3=Qu`VZ6-(F-yLj}wyQv0421ldRXNq^|5 z_kN2#76OdV$GG?Z+B=dT>)RGs^8WHaBK3J;LwY4`@?CzL;uthlRq(7-e@^asQS(}R zXJ-w>J#${t<65d$Rc7Dpo{zb0RvF+7(gnM6t;YocfP*H=E%AWo{!4lEKh#bCzVUxo z@xRiT|HkTXto{zQ|9B7P@A&v16CdZn5m<@8*Xw`GdVz`l|8_n9UctXt@L!UV6!ITd zKp;Zeg8vctUr}sgvaD4>(S9C&M6gr-pPp%hZM%~cMI|BUWe-O_OuSKW6g1NQVs|ER vF5m!x!!RG5bKvuTxRG20d{smMpfJQxIVP%;(>MpNKunD-E)`$A^ZWk+sJu}ED%NIPK=lOg-2cdgQON#(}ytK4LMs`bcbMxUNjYp0giGN%l6&2O)g(yM*eC*jU zKhoIPaHt_N+K&$YHZ33oKv!Xd{qW%<4Z)yJZ2=(wx|GJorjHID6f**#i?fY`gNF_^ zH8ty44x$C1i;0eo{=^kmL=q603J?OIE3y`R@)Msp`qDy63n2iysBYb&uZW3ZMgVkm zJ$v?uY!ng#pnvPK3Vh;<7`xD20J^a5-MdE-0-%etNkNaEJqQ8N)%CDhfdF)E(b3%q z0npXi>VOacU0qZZn}tA^$BY0#2q6GK2!P8;b8}0R{U7=fW9Rep0ivU$qND7;XuEJu z@5s+104^7eO-+sApAm53x_682-mP25?@tJTOE2_8#D5x_I(IPI7GTfr-CBPP(V6j& zj(|%qv>fQ{r(`>z9kzpaCIrAGW)bMD6~RgDupR6RApkD1=9Y_6frt)H3NFg42KITC zZfxGQ`S>Sj>y-a@bM#H$k9J%k0Btp2%ze5J9=!863;({S<+*81HE*|b*j}H}{Xg#h z<*%RH3x6BH$6LeUBf6MkdiFT$eL6V=fe-Xx@{xmq)7~K}T92&h9)DBUYC&yn?cTk6 z?ZWlfU*D@&F9B^_4k75|rNQf~n_4UI#s7@*-V|jczqv_!%3I$0UF27>=_d;szmnV| z>at0}^5x5mii+A^6CE8rY}l~O%*-!+=}YPiuYbP!>cbB|Y!}MQ%ZCmfD&R8H$su_2 z=SO!1{_|^5kF7u9;6=|j7c6PA3-7+8x87_SIP`KU!C(IJm(tSGldjykbLZm4i?gz_ ze)OXs@lzXc$vw4fY2ABuOP_kG^_uL5AHJ&JRS!;|q0Z3BuNpLbbo9a(2A@36)9#7x z;eR-Kxw`Cf{4S;;Lxv=U4IDU7;g?}ou3R~G>{tPp+QEZ|e*EKSAD=rn^5&e!9cTVHtL zg&S_TA#(Z8fBv%-;a~sy*MZ`AA{Sc>K_~$?W`PN%+*@Y{vxPrw3&g$`b^FBOqVA;|i#>6-p8=IbfYT2yWkAFQp zGh3bHf@TEu|5CwoNB;Y_&Hi_f4(v1kONZ9c-|qN*`Q?{C``OQa;~U?&|Ni@J6b&Cf z{2%}LN93kOix$Pl$J+?J=bn3P3{Rds`I>94`RZ4{T2oWg`tD_AWmjEwmCNP&@|VAC zx-t%}o zx7>0|WN6u_{?@m?Wux7mkpK9{KiU?(J>Nd^$Rn-qYtP{Y1qFZj!ykVC``_EaZ@lrw zF=NI=ZfbqqEjl$6-{{OM1BvYEoXdGj(dGV1H=ZSdPxg57Gf0E@!= z@4s)SZMWWa*Ij`?z*ZKsXU{(AGqro#N8i7H|0h5B$zT5RmtX(-*9CNpqo+d`#|147 z>i>Q64F7aXf7?6AeWFAr+U4KFdYy>{|;DgJSEgLanMC2NqFn{Oe<;BIt*>wGZ z2Ody(BrjdMv~}ukbBEmA-2VOhZ{EE5_S)Pw?z@pYr=_LYn|71U1uSZjSxM`WZQ*n4t+!gmoKB}z zz+O!h&@r|&6#UYel{uS<#f({{Bd=XPTmi19ZA3Wujap29L+*glivAL%aLPG z?Oz-mb<+0~*^{KLnNw0y+Fogc?VI2HrtRlj2&@z~mh3sd?SCC?4BP7ZH^2Ffh2ZVC z-?jm7&&em8OjOkF-MekAVVCWFBNd3SrHB1#lM6e@>1G7BK@hoMO8~p+*T4R?!ck~F z&&c4nXZ}5V_SnkB_8M)f^4xRJtzEm;p7U+mziQR0PkriBY7>I- z?5CR%*rr3|f>py-@{!ek+gSWZSmZc1&$s*A#AN;Y^?%m29bqe&4#@b|uwYi3Eh5q)pza21OfPfCL3Y>J!(q+rkc{(`+Y4>zH_+dMv%SM9xT3!FG zwr}nbnefQm`4!if>dn2T3=Q$w!8VYuYJw_wCin~ z(0?Wowz(+aQtRXp+;aDMz6|m7(@)!zWO8!yxN+lb;MofYws-jClTWs8r(GR3Y}l~7 z@4nk!qT9N4tA)VYVf%u1tL+r7S+mCW`KM2xZnK7>q9WVIv+c&nPGJYxl%(yOmL%Jr zu-Sg(O&i{}u;0FY`?9del~$#n{NyK=xqt6|_q(={X;YZBVXX(TS1WB!(RLfcW<9UJ z{<^imCODB6oV08K{`Xxj-#lxxYkOtxsi&T@!4`S#@Mk~!nLV$zz0ZmjD{O4rtAGl3 z!CGL$DRPqy+F$(Q7giH{b->=;>R>5pjfg#U4<0O0T{4+SV)WL)a-A05h37`x}bl3xb@}6ciuYtyAK=D?fRq(`i2FY zE;f>V1~`Tzy?s|7$Na z1h!IW{Z=9Xo$f+^FiBIR{`Pju2k*6;UkR~@28bTM@9AQ zarUi1ey;&=iFM-dH91uWf71y+#TdB6xU!a#XsD~+S-5s(?y3!CwGFy# z1?wudZYV0Px%j8pP*eU|;ns?ppm6Rw7J~ZHM~8Xb{ZIVw+Vs^`r~RTgRBe59>f9Mq z7j3Tz)|bsp=R|a%()xI_<-rYYwYrDVA8TeM;Y7c|q^UBWn(d(;M&Ym`7>a?6v zzb-C+*@|gX9-1~iuaf7U^Kec}gFeY))8d@R-s&AU#N{~MU1L0H3l;|}979uM9qP=1 zP`e>BWk#thSV#HyfLE|?Djmmr6?;dMy|xHg*k)XcaQM)7S7qZguv+;m6d%* ztfNDBj&ThcJ$Xp@B8BdBK_j_51KoWu;|hd0ViU(qN!0&>m;oa)2Y(3X=fd|00#$`` zGHxE~>F@CjOPe@r)oWGZxnkYciPwAF!)ES0W%gWGQ7~^xdeUH9TnrtNIcG!FvAJb~ zzbxMl74Nw=e#E4O+iOo*chr~9yD4c#>0xPq{`b7@TUS*zgyyI_^Jdx!#PuKOy(v8_ zXH(VL!c$kVDRcsMdVhn5j+``eb;30nGUTZfEA3bHx>g~RU4xZc5Gpyb) zfAYv-13mWcvo`H*J8N$6y_Pe6#IS+3s`B1E{?VLG6?L|NS~c><`$9_xfAORnJW2D* z>qFE2(v?%v!e5{Gq|65v=byO14t)_ki4X0vWBb;O9b_~1B7c8m?c<%aaPg#+xN8TF z&bQMJR240qo|$5wi)UC$#*9_lB4^X!d-;xmwyay6ziRreLp|3H9XX?*qONvl?&J~I zhd$zn83k46TJB@VTv=E!Z&F(LBc@F+tZrz%sej7iiv4BtQ|xmek#lV7-cY+CyyBP` zUOKh^s5tGB{eRnL3{PFK+x{A=my8@Rc+7^HKvhoY_@nb8e`RO+^xHYruI&Ot5Zs$T zE#skA%7SsDC)}0cRoVJwGiI+k!y~DoYVDM9bMto8_D#5D^ayvL{FNEso4h*ow;rfi zGdW}SD6W z;uqoGjHEuHi*YFv@0&I%?&uuNzhhm#&y{@p_)#8*zc4#v>I;RH8Z`2@RF5h)E}JxN z&bHP)lHl&VB?Yzap~-Q<%8hfg#*Lp<=pT@j;(v7=sNOjD(e;(*Gcba?moIpxLhd1P zy@IuS3KuLY+S@jlz2Zit#M!tl-RcWJJR1CO6qO&4d-&)i_vs%Mr#rG^KD*VAg?XTYhm4$1|0)0lkxG6U^G@$EBW{w)Sd39d-sCkYJg%DilOZ75eR9(Q|g z>DmpIX|vnkL5Q4yypOIe%<|f#x~6FIsDG)&g?YOsdDCO#(&xYEu5k{%d5|q={X2tc znH#H1s{NTR-`UU29@*8KUM{sqcFOXXR^4hRQ&;-v_a@}~j-Gk)^k-J51_mW1#m1;% zue1<4qS9Tigu7iBU)Hif0E0+}e?Sx+}SdbY0TxO+bu75pm z$K>R(kpa^yb#X!ND7R|1PW;Za?LJq|&(~%SaMYK~OdYqmx@?bsf;aY@mmIy4=WWUx z?GAr^(xz;w++OLw!+reb-f?MnB!8@(UtauL^|T?0j@t4~6#=x$6tk51ou-14=lJ|}exJ{tx*Xy4Yr7h1D=I@zl0|nX6}7qAQTu+NR)4kjX!Z=h z*&E(fvWI)B*R`csXB6<2+tY?CX~dA&@Y}uPZcex+w{l-)wZ9?J8FShb7ET0&BCF}f4=s_%|idSSz{)x-zRtcs1ZYxV~1Dezfg4c z*9fl3czVvQ?&HUCI6W>$u`aRT-a=dE6xTZXC664QoSdFq_KZ#5)hX)B7mpjCTY1>! zO-W6NACT(buwrv{yMK2KO_CRt_qMq$tKUFwxWIgypZ; z{)Y8=P~yPzdJ)exAkoI_;gbAC5 z))h@n_m`x^IRh12Ua$1`oA~PRn+ACA7(K9X$)3Eall>#(o&Gn<+W$ToXKZNpy1!(_ z!o{^?CXG&;GPa=X`PXOMeYh;4PoTQk_J=&TPs{Y4?pq9;-igyj?Z_@#GI`S8G>>C{ z*~VhOGk<=5R*Ef&YeTc;{oC`N84wt%l4o;vgq8-Ock11tbBl>f&rB+qRT?^(lF-TQ zsyJ2)htu8Hsp`stBtKAY#d%@f$V ze8ukbnUloWj>DZB#=T-E90;Fq;g8{anyYebu7=1&5C(i8% z_^N9UhgRAdV?wXmpTpLoy{J6gy=C+Ejr)Vnp|)8Y9_pumRGewq?r^z64~6Q&W%HGu zIW|QX?bGiHE^I~+6PGb>;ix!!v0%}Zsn1k<#!vM2Jo9_>PMn+n>e8%{?z+-f^47dw z?SB}W@${-0p&cFPkO^z^7vGlb@)fOFR}vULB`c}DeuqZ#Xpd^kH|OSV^3^*NXRXbD zbhy)3oVVtc!t!9^glF;=r=4M=D<(F5`AbVPyup%<>+;qY``jt>Us^S3h&`g5iCIf$ zjp%24@3Zcnwz@Wc%GhgKKWK-}Eyk1i+<)5T6NbC}#rbQt`r{_g7}@XWEd$c$&%4d* zunp?$DNBl-p#gvVYd9+FPOtMo<(73T^LIOvvU4U3b=4N0^NU^iD^`{GPDpbnfBIcOR7*>X0Ca8q z>q7v9006VG3nl{$y-5650002!NklI!_olgS{fT0BQFy~-eg7qbS+IyO|8oTwMl@O5da^1_5z{J zGy;ObVC(o-n+mjE_#XfO0RR7gy(WbK000I_L_t&o0I0)NrOYIZ>i_@%07*qoM6N<$ Ef>zhV8vp?LYP93I)D4)@wa;(YU}8BiQXG%`w6xQZbSVo)Ge}>g7aTxXqlFjrm1P- zv~;yJyApMUv_!q`P&bup6WkWqHt@GyU~L}T23~EP+AfY|9OcjbKIb5!f}-NBcRU|g zc^%F<9KPWBJinjM=YVu=Y-|*OkC(>A@W^g#XlOWjs_xXOQ-9&>BO)SNy%0eNfR8;J z=BMiFYCozCkM^d)-=YPC0O%}iu%A46sx}bN`7Iy>K&MhySO4LO6JkaHbaJ+FaN?tn z>gyYHItP&g(8)wbMt2|^KKY4HG`+OY*hmO~PO3|n$Qz=fm=OS- zUH9(Y!W)Hz0DtJbtOB37A<8av6@X5xYuBz3gaGK|Y*NszdpAM=bavfrRv-YKTV!Mx zLI8AjwmKjLKxY>b!Dbp@iI}3qpFSs1&=u@&i zpgn9m?@S1QYs@0hQ7eLT*u%E7FN6TN#u^&0N(I8&IVrd*uNv6rRkW?)z>YJYpsiE> z@2JS(KZtbPAOI~jT+MyDc0PE=Zx;UTaO3l{>MP%FsP;d2`_8_AA7YY zPw8rk>VMwtqW9_G5cuEMLrJGj_}jcgM5G?w*fn-|=W0P!Rn?IrN9@Aj!GnAB=pmqm z>mdXkyfoOop}x5SU;598xZx2t@*C=PxTNu|--UnQf_}QB?rTZiBCeYhtX{o3KR>_a zHIb2#Lx&DcPf!2KSH7Z-@Y-vyJ@UvScA=!CWPiwzAp)*59UOu;f6=rn@Sop^cznxQ z51#)*L+*-tyYTKidh5-`{zI;p68!nke=aI2I_Jv$`}Z$jzC0r%)*@%8v!OoIjuN(|}WzrR8+!>(PscEW@S0De`9Zb_|4|;k3N6qAK68p{Xb2YYu;}?1gB1&vh(x7 zg9rcl&wr{7AAb1Zn{U4P_rL%BGIlrYU2LuXr$7Cvp|#gkUVQPz!jOuJ3cJr|Kl@p` za_iQuc1SJn_Q3}q*b%<-&O3E=b>}~b&wu9&-`es+*jnS93;+1XKf)i&?kwP9PMrAY zCqH@aiG>TpZ!UQJaeK4XXW-?zq7c;lOS#XV`u}e<_}*IT6pue|cP z&wcJ&-}=@A4?J+wO*ai2Hq1tQ_@-scmc_=#+6cV&-g^fO7%+YM^qX(K`RiZ*dVghQ zW%Iii7Z-ow3tw=#Twnd_S8crg;0HhW_rL!gzUgaU`&w33*1moF1`Zr(*C!+-*y8^$ zfBB2rX7^mMU_tou_U+rl7k>TgU)zJ*ZeM(Syd6NVUcDACUflfaC6_E&VjsPC@802C zKl7Q-%$zwhd~0(Q!WaJbx4+$T%YQAyhY$bmcfWh`4?OVTs+B)l{?xK3 zmn?ba=~eUQKmN$vOm&be3PIVg>p%LSy>||r(0j+4F8BECxh1geG#mfhwr#Twc01wQ z82;iHzi2}x5c)lBVY6nJBRnfuR*y|Bi0jJYxJCrte5YTRp?haiY9lRgj zJd%FxZnQvw|Cre$J=keZA*F^pJyFRM8u&(hit83m+gJS6$rDXhy7`j z3ww~ZW(3Urb?F&40J246Y3xx9D?S}!uQ{` z|5}+Jbm^vyHRt}Abok7C{&(8gD7A@zLIcSLPxDS&v(JD2^R^bS3&)QixAplsw?6U2 z6E=p!m)&l6%YU1?ckiy#EB3Z#1h(lAzF^g`m3(-$-!c~e5fXkJo9Em8ZDO)z%NFa} zz7U&4*u=q#(7Z=r6$oE&IIdRO@v&t40#*Ur6j%`2FZ|&6aoa4o;fAP=+9v4W5L|e( z(D^w%w61IPm(RVE&A07OJDr|0DX`gl_$FhU0yb-~k$>Ov4tBy1FZFG|ASNcJ<&3~a ztgYU|@b?Q``I5a25+GG!Fa}C=HedCQc`u6QBpdD4W zWvg-fGyLYKKmF;9jEuazye_Bz-4)vcuuZ{m2yAum$Rm%~?qKu6{%1e?+0vy;@4x?k zwcCC7-4~vf*Z{T=gs+@Ccdoq{VHa#K(;nZh&wtF!w3EEeMC`$BE@KU{-9cOS+s2{Y z&0guSBeC593x_?9-L3g$1!KFL-}uHi>`%Miwh3(#VVjErF2*Ww&NVAnty0J7;1Jw) z&t>jDKJ&~oc9Kj=N}4!vq76KI;lTC|pL*)4!Gi~f?{HIy?X=x<&pq}M-L74`ECkjL z+kY3dTWzOssEw>?T*0X!}Zfk)}aKbG(XW0V$ z&%0i}dDdpv_R8GTPd{ygE&STy&wu`NJAbdXyw937Yiw-WtAGl1!CGL$DSVR++F$=;>R>5pj)-5*R8{hwDX*I4kE5nsx$Cu=`@SbozH@V|yl;3GQvpYaU!rM&~3 zJp$l*!7o7t5Y*7n*bw}c=$HR%(IXUL`vRBrYtcmr0OqpN-3nBf z?%JAPRC)Cev9_}0^}JoBl>y<>wJ!uUMUM^jxNkiBzkaD3%G>;+*Ou>kZ05o_GnehH z4Ad0Qjla2WuD;^*wE)|I-v1Gs;H_8ptQc=0gzjs4T|s?^bHE4R=2 z-stp)mu0P3^-7svS69{R(_-B>#%ApGwKxLrhOzcE7`CYRf*-x6V(t7{b7s!UD)Q;- z@)fU{HRIt~v$M-M_gsccS{n39nvfFXJpESBm_aT_TX&7}q%2t;D0K`O8SPL<4uq<$ z>B)a{id<>0?OKuG)W<;ezS-ZqKQEBH;TP*i^=|Jw`O6B5tK>{vkoU}ZkJAyQ>s8N~ zX^WQj_VyYX?@|W~lxL40v*eKHv7PxDF;Q|QWGu}Z@Sb}_Tu}G8pmEa&g)UO)4i_+z%hTW8`#P>bkRyLOVf>5){SS!hJ0iWWaCxqLkHBA^w;*lQ z5YLSs&(M^q^VYv!9-1pw@0vQ;;~qM9|9P|L>eAdrGg1==+Tvo!i1YV>x%lxyPn`YlREUus3G;G|A1snEyYukBjO-ERwP_I@`$1RZB-RFYVu=Q+e@o$0kwYgt@j6)4!(kEw|Ej4m(&EO z{Y7hMq=ddcv5DyqF3&l8fgSuJcoKgeK46dS-8f-@&DisO;k8fPw57|ZCCBvZKQ6}( z+h3l)a&~&MeJ-A%$!T-e?+G7Gt@o8X``fZ^dCvOTV~2S94H-Qrx3s!y|GMcT1_wXl zh&j3Cms;-AkGVE4chR(z&__&}omWxYeAA7|%S(?HFHW}4eMHvjse5hJ*3f^7V`^yW z)cUic%}4g_nKNwUl0){_P_bfk-+|+|R{G1cf{#BgJN#F6kx##sL+#wIFa&`kIkVCp zeziCdGj7VHv^W)SSv6<=`U^ahYRfmxn7A-|Usdn;+s2J>`%7M(bJz3@!M}BX>Bi}4 z^It8jawUvT?i)C;dd-1zmz94G_o%6NC%NrX_n6Ul&r6GSMfvtGnVi04M{&TDbVu?a zr#JtF%yBbwkJMiH05Y?8dYy5%jZ5_Si*_uYI6130_@Qb_*Uz|TSzehV;f_gDM|-OG zy}J0}g?UE;?K(Dvo@6t}&)HZ|;fNbKBGys1K5OT((>GPUK7aCz7Y={;-6>P=pOoY* z%XuMl_PU~gYe@Q(WKVF8)o0{{`=%wi9o0oy_e{y!S>;T;J7Yp}w0HNq`I%Y!s?Po* z+?STvD|j&`dFuVM#>6zu(R}+h=XhO7cT67Rarp8w(`LSySEd1@Zy)JV>9$qVCN9|1 zyhjo^l)WOi$~`10CQyI2ZDGd5$@B7jeG`-8T*oW6EqrWC+2st3z@gPko-LJoP)v_N z)#1D)%kq!3%w><5G08DDZi{w#Lr;%d-@*Kn<8luhm*{T$S<%)bJNk~CoEG21ZnVG0 zsTrvQoGmA+fy#@0`WHOZ&gfE>27Yh;x|K(o7UnkPOC6WwagTpqu+HUm#3c^u?NCjb z!+XcNii4%Tz<}1*@BXs9O~w9RV_w?6Ze(ykR~OD5GjYd;?2<8y99#1&@O`E}pSvW< z6&yJar_R{<-dXP*4ah-hnKKfcwMTNZZLqm-Tl?~JqoQpnmo1-o$GW0T zTgy`Bx4wfAJ^+8kJ+>(?BhDt(mHE@h%q+;uJ}@mVH997B@k{PX=a5kYY(eYWA4o~x zR#8~tOLuuMer)#4uGs!ckv+4MSHHY|tQ|~s(PMW_$?-NFdD84>H;nWTNKA~5Qtc6U z)&E;oTyoGmJM+$|w-@g$@LEY{XNL9&T+^P}nBa3IB*lN&TDNl0@sTqNN{hWUW86K@ zd=WC;j>Gvy2VC)!=FE$?>ubVUvaiEy&kgS#Je0L73Ve3JFXb*t2z@T|Qq#BW&fYgY zX+n6w^ccB3cikAbDtAr&-mJY|SJp2!rT2Bz6wVzvaYse*Vc(Rv=u2L5^hjE?J$sxx z^z})Zv9o_{Z<+5-_nDh}#-!XCziDwv!Rr;X1|>MEO178!#O#8y>{wygx0UEs;w$53!Z-@^Y2N_Z*jGw>OFszs_fb988#{| zw5w!K_mOd~odvoezqiCr8?M9=gQ7!k_ly}8fAhMsqh%Go+5~4*n+MQ;RANl{*66sD zLC%7`6(vXfsnNY%ZdZ@uoTX_?j#m4sD!j*S>S3Qtt?yWSj?M2oYWH%*r6hz8rgz-f z_&$F*-lnNTPnWxQVAJZEFCOvveBPtMwMFY+Cr7l~RbG-iFZ*z%-Lc}Bofe&Ke??nz z#w7L)Z8z5X4i|ev2OJaLqH_%zImEqXN4bsg39;d8?5{2K(Ru_=rLJD?&>7X?blJ7O z_I!y+l7oeN%WC4>#rgJX$Iau$#Pw|ZSrG_$ z+V#k13$)|YZ}kDM@7ykDLhphg^rn5H-eS7AOpSI{>5J- zxH;{a1!LW39>?MIxEuw##sWw3Y?)I~<>-?%dRS6YYEtpDHhEWvs3}=Kaq_ydldgZb zK%iV-y>hX4u8P^rLJs`pr)d* z=%`$`ZTPS4>lQDZF*??9;eiw7^0Rv*N1=I_qwiMKU~uWVmwWz+HODLHCSskj(d>vfNE4~pv}9o|#av_Di? zw#z1{&Y1YVPG9-nk}8`|zPu}E^|JX>lKVQEkGfsQ=5*a0oQ5_ZOm*4oB~5>O1U23R zg=Ht5iA(ceTfbsn=D0YgTK_@?d}X_Azas9j?fDy4EtxmjCKs2Hq9|AY1W$_tmala0 zLF@B?g#MTHBA%;nf{oXcg*my!O(F7FiW=-^MkQ@DS!Z!vbbqU}ptb|8Drra+RjG<#)m1NcHu3|B&0YRtaP z`7={}g~>5af9cNMWxhUBUmG^OZ`_^Z`sb}UoIP{8Z*;8Fcd)qi?~`#x2WPLx3fC-M zUNwH&xRekZ zZ!7RQV;5&6+mg5{I9oopH~ZPX{vj%SE^A+KX%Kv;p6xoesF>9B#N2sB!GkFb9?XH# z)3tCo-MyWvD9c^F#WB+5%Uh9qW;)<>_YHmxOV%z~>PwrP7o-Sjf**uUH0O*TXH=yZinAn zQFSu7(oP#6eAWIEwifM0R%-f$ zWL>pSzcaY989`J`+M=anV(i6&Wiw_zTj7~JHLm-G-=k;3!kmBCR%VQLR~NmSy>WMi zV@TRF>*oY_bew~xY|2@Fdy>nWzj1S+f7pzS#Mb&99LeK6sw&yBZryfojWc21rkuxy zIlTqh8(+;U2_#H;HhX!>1va{(qElDDyfQ5=P`GV#_Lc&#J9+WT>!%H}XOuG`W97UN zeQfW2-aWH6RKcbB|Y`#ngfX>L~0`_Nk zIv_#-bT+mraN@)Xg(?ufhNS_}xzyFwgzkJYYLNgjBLF`3>;*zK zn*{^{f#&hA78Pi|@ZSIc0RR6*L#0Ik000I_L_t&o0BM_U0Qz*o-v9sr07*qoM6N<$ Ef(}8B0ssI2 diff --git a/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-loading-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-loading-linux.png index c2b44b9f6bb62ed54fd00d85d2c567bba2c8ba0b..b6ad990619011bcfdf079d4ccd1b81e596824cf4 100644 GIT binary patch delta 4164 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 Date: Thu, 2 Apr 2026 15:43:13 +0100 Subject: [PATCH 156/211] Update screenshot --- .../banner.spec.ts/univention-menu-linux.png | Bin 25423 -> 25424 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-linux.png b/modules/banner/element-web/e2e/snapshots/banner.spec.ts/univention-menu-linux.png index 0467cd03b27f34d5a873727ff716f87225581bf8..90f47fc89757c92fc68ed1fa144797c9ac80ef6c 100644 GIT binary patch literal 25424 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 literal 25423 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 Date: Mon, 6 Apr 2026 20:43:24 +0000 Subject: [PATCH 157/211] build(deps-dev): bump vite in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 7.3.1 to 7.3.2 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 7.3.2 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- modules/widget-lifecycle/element-web/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 0fa0eecb03..5ed58cf056 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -20,7 +20,7 @@ "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.1.11", + "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-svgr": "^4.3.0" }, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index c0fa58b261..c03f5db255 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -26,7 +26,7 @@ "react-dom": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.1.11", + "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index ab4c4d9469..14f1539528 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -20,7 +20,7 @@ "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", - "vite": "^7.1.11", + "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0" }, "dependencies": { diff --git a/modules/widget-lifecycle/element-web/package.json b/modules/widget-lifecycle/element-web/package.json index 4611eaecda..d45ba8f861 100644 --- a/modules/widget-lifecycle/element-web/package.json +++ b/modules/widget-lifecycle/element-web/package.json @@ -16,7 +16,7 @@ "@types/node": "^22.10.7", "@vitest/coverage-v8": "^4.0.0", "typescript": "^5.7.3", - "vite": "^7.1.11", + "vite": "^7.3.2", "vitest": "^4.0.0", "vitest-sonar-reporter": "^2.0.0" }, From 55c7023ca0dd94ad88cc35c9321da3d7122f361f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 10:17:51 +0100 Subject: [PATCH 158/211] Update screenshots due to element-web changes --- .../login-legacy-linux.png | Bin 108178 -> 108182 bytes .../login-mas-linux.png | Bin 65636 -> 65640 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/login-legacy-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/login-legacy-linux.png index 31d48fab8e7dd359f9adc70a933edce71d190c60..e958aa4ea931c28fc3e715321005e7390b5999c7 100644 GIT binary patch delta 49454 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�>Q-*@jMsndp79-|xwUuA2nHIMc?^gyV9+xP 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

fbtH|bs*Abq+!815b24wF4F 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ⅅ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 JT2aHG{{yehi-rII delta 49430 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

  • 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 diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/login-mas-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/login-mas-linux.png index 4e6a663daaefef7dfadd559becfffe8e80937645..9d5fe7fd60dd222f5902e627a6e1da10e19704f7 100644 GIT binary patch delta 62689 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 delta 62683 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 Date: Fri, 10 Apr 2026 14:56:03 +0100 Subject: [PATCH 159/211] Move playwright-common to element-web monorepo https://github.com/element-hq/element-web/pull/33088 is the other half of the PR --- .../element-web/e2e/restricted-guests.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts b/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts index 7d7bc3e09e..75ee368369 100644 --- a/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/e2e/restricted-guests.spec.ts @@ -12,8 +12,7 @@ import { type SynapseContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; import { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; -import { makePostgres } from "@element-hq/element-web-playwright-common/lib/testcontainers/postgres.js"; -import { makeMas } from "@element-hq/element-web-playwright-common/lib/testcontainers/mas.js"; +import { makePostgres, makeMas } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; import { RestrictedGuestsSynapseContainer, RestrictedGuestsSynapseWithMasContainer } from "./services"; import { test as subBase, expect } from "../../../../playwright/element-web-test"; From 484ea4676e512265b6107c53a2ee7eb87edf3495 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Apr 2026 10:51:27 +0100 Subject: [PATCH 160/211] Iterate --- modules/banner/element-web/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 5ed58cf056..93feb7cae4 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -16,7 +16,10 @@ "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", + "@types/react-dom": "^19", "@vitejs/plugin-react": "^5.0.0", + "matrix-web-i18n": "^3.6.0", + "matrix-widget-api": "^1.17.0", "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", From 319ec4bcccc3bb992b8f90972f6b8ef1d660398a Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 14 Apr 2026 09:51:03 +0100 Subject: [PATCH 161/211] Remove accidentally commited diff file --- .../element-web/external_all.diff | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 modules/widget-toggles/element-web/external_all.diff diff --git a/modules/widget-toggles/element-web/external_all.diff b/modules/widget-toggles/element-web/external_all.diff deleted file mode 100644 index ed1bc76ecb..0000000000 --- a/modules/widget-toggles/element-web/external_all.diff +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts -index b65e3ec..9d1ba49 100644 ---- a/modules/widget-toggles/element-web/vite.config.ts -+++ b/modules/widget-toggles/element-web/vite.config.ts -@@ -28,7 +28,14 @@ export default defineConfig({ - target: "esnext", - sourcemap: true, - rollupOptions: { -- external: ["react"], -+ // make sure to externalize deps that shouldn't be bundled -+ // into your library -+ external: [ -+ "react", -+ "react-dom", -+ "@vector-im/compound-design-tokens", -+ "@vector-im/compound-web" -+ ], - }, - }, - plugins: [ -@@ -41,6 +48,7 @@ export default defineConfig({ - externalGlobals({ - // Reuse React from the host app - react: "window.React", -+ "@vector-im/compound-web": "window.CompoundWeb", - }), - ], - define: { From 8d82407e8bcc84b994f0bc6c53ca166c550f87ea Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 14 Apr 2026 09:53:07 +0100 Subject: [PATCH 162/211] Use proper version number Co-authored-by: Florian Duros --- modules/widget-toggles/element-web/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-toggles/element-web/src/index.tsx b/modules/widget-toggles/element-web/src/index.tsx index 9527917f48..ec6542106a 100644 --- a/modules/widget-toggles/element-web/src/index.tsx +++ b/modules/widget-toggles/element-web/src/index.tsx @@ -13,7 +13,7 @@ import { CONFIG_KEY, WidgetTogglesConfig } from "./config"; import { WidgetToggle } from "./toggle"; class WidgetToggleModule implements Module { - public static readonly moduleApiVersion = "^1.0.0"; + public static readonly moduleApiVersion = "^1.12.0"; private config?: WidgetTogglesConfig; public constructor(private api: Api) {} From ddb873a3707e1f84c698fc36c9623cd5e0e6bac5 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Tue, 14 Apr 2026 14:36:53 +0100 Subject: [PATCH 163/211] In lifecycle tests, dismiss the Verify toast if it appears (#252) Depend on @element-hq/element-web-playwright-common v3.1.0 to provide the `toasts` fixture. --- .../element-web/e2e/widget-lifecycle.spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts index 83ad62ec65..36022edbf5 100644 --- a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts +++ b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts @@ -63,7 +63,9 @@ test.describe("Widget Lifecycle", () => { }, }); - test("auto-approves preload and identity", async ({ page, user, homeserver }, testInfo) => { + test("auto-approves preload and identity", async ({ page, user, homeserver, toasts }, testInfo) => { + toasts.rejectToastIfExists("Verify this device"); + // A bot creates a room with the widget pinned to the top panel, then invites the test user. // Because the widget was added by a different user (the bot), Element would normally show a // preload consent dialog before loading it — this test verifies that dialog is skipped. @@ -119,7 +121,9 @@ test.describe("Widget Lifecycle", () => { ).toBeVisible(); }); - test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver }, testInfo) => { + test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver, toasts }, testInfo) => { + toasts.rejectToastIfExists("Verify this device"); + const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( "POST", @@ -180,7 +184,10 @@ test.describe("Widget Lifecycle", () => { page, user, homeserver, + toasts, }, testInfo) => { + toasts.rejectToastIfExists("Verify this device"); + const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( "POST", From f816c28eeab339907fb9b7fd3d2e65daa28eb430 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 22 Apr 2026 15:57:39 +0100 Subject: [PATCH 164/211] Update tests/screenshots for https://github.com/element-hq/element-web/pull/33211 --- modules/banner/element-web/e2e/banner.spec.ts | 4 ++-- .../opendesk/element-web/e2e/opendesk.spec.ts | 2 +- .../snapshots/opendesk.spec.ts/menu-linux.png | Bin 18149 -> 17900 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/e2e/banner.spec.ts b/modules/banner/element-web/e2e/banner.spec.ts index edaf519492..73b4bde7a8 100644 --- a/modules/banner/element-web/e2e/banner.spec.ts +++ b/modules/banner/element-web/e2e/banner.spec.ts @@ -120,7 +120,7 @@ test.describe("Banner", () => { }); test("should render", { tag: ["@screenshot"] }, async ({ page, axe, navigationJsonResolver }) => { - await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Be in your element" })).toBeVisible(); await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); const nav = page.locator("nav"); @@ -207,7 +207,7 @@ test.describe("Banner", () => { }); test("should render error", { tag: ["@screenshot"] }, async ({ page, axe }) => { - await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Be in your element" })).toBeVisible(); await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); const trigger = page.getByLabel("Show menu"); diff --git a/modules/opendesk/element-web/e2e/opendesk.spec.ts b/modules/opendesk/element-web/e2e/opendesk.spec.ts index 46d4506b32..d4fe5ee1f3 100644 --- a/modules/opendesk/element-web/e2e/opendesk.spec.ts +++ b/modules/opendesk/element-web/e2e/opendesk.spec.ts @@ -113,7 +113,7 @@ test.use({ test.describe("Opendesk", () => { test("should render top-bar", { tag: ["@screenshot"] }, async ({ page }) => { - await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Be in your element" })).toBeVisible(); await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); const nav = page.locator("nav"); diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png index c4a893dd31694c2cc6d774ea531f56e19b69e052..f690835d36c2fbb2a73893973566140e21296c26 100644 GIT binary patch literal 17900 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 literal 18149 zcmeIacU+V2(kC25M5%%z9h4%{6{LoyfFMYb-UA|1LLk)8LJ_6+BGLr~qy(gQ0wSOw z9YPJgSLrom6MpCHyYKVt+5J3wcK3Nd@8|GG?)!6>x#pUgYi6$R%uT4Kx)M1F0|@{C zAb+l`palRB;ZCpgUB7}m$g!#r;0^>XT1rm=#XU@`0Ki?qbA=~59%&nBZy%k5bnH&= z(Qgjn>=%7+G+)0my#~GowV12=SbV>fbB5<#vF$fqXl*UHWi5;*r%=^^&)__CqWGJE zgI=sa`=@J~1p4uPDW62Limy&kyF;Gp2I&cz{N_mB!(g25$3t(tdwB0w&wBpcFA3|W zz4RDQsipMZqwEUQ+bK~{_x7ehTB)a(nIjC(BA~~=e}Wl*0+=zm3n0Xw*arZ}@W*dN zS03Vz&3CRh@VsUX1#p_mU(uk(Tk0PfpF+Z%S zqM~AGXxLz+`dUIl0>!JQrnb4+gue}_3%l2MdGe3hnX9_4m#J-NNKX;0xDdu$OegeO z*%3yJf2*&&CmzrMKRZkk!u0F$S7@(}M{G6zUk;P*sKt7lv^yk3{XnF#EDbjs$W8#^q zt*27mgL|(!_J+>|GA^&5t+W6pCnp=AvrpGOmVPH=&yd)9kDWQ#G`u=1>z=eU{A71= zY;26J%z3fXI>R$JH@AJ$J(@+@YpTIBK<+L|+0t?ziF?a&q6$$^z(XBs0&O9}iN~@% zB3a7Q+0M>RRkbVH@7$8g*4DOk%Wtn+40$|98r-_G@;Q2IvUV>~-;64jfbiP+o-B6n zY#WK4XT=^5>x0roA>&~*ycs^bzYK^x_E-9?F8}n{Pfkvflao<9>m|r0%MOb5V^-`A zt03}xH{&eoa!y`R14bP=SwllZgv1oCQZ6ff_o6#2nPm@0E%%mtquX?-oFvmHsvSX9 z_G5d!veP>+9jfcIv**B|vy_kq?8Vu9M|Aav0?DY6bvWi=WN>g$u;I`5TP627IYq?8 zN({^2j91vQ?Jf%XoxG)z*#=nRK6kU=Ki8x0A&rn90?39Od4hKackZH4taA zrz`B$6PTS2R!T5ZQ-yc|Y4X?t`5@OmGEtR$?R?h{h z;MUmq`1sgZi*#EEq`Rxj`{HDon35$eKHg=_D#>s;H6`caf`ZS1){#E+wXgp4Z-@iZQ#keocg$tS?eH(|D zqh&8VdeS7=oI99d2CGM9T>{4CZ`2x)@_3f~U6z@;37Yl?RHr?cnZfmFZtdLjIa=h+ z*w$c5kRRs2vFRkYYf;kY_z!Mo>uYNYKN!OB){LV%>UFFt*yy@hITH4BrT;U|ZtA=a z?5Iv~x;GI>a&tlYVw!N)08ZGIr<8sz&GVsI_H0AA@Ey=?ZHQE`0Xowhz_t0>-Tjc# z@3@sK!{e#K#k}mrJdPCd;-Cbfx;|2}J&nXloang?hSH#m)BZAQaV3J|`e{sYqOanY zZ{PYzn%Q;(Sr(waW@++E1dW}_-?zvp*Q2!fw&RwE{!MKRejf6S#Pd0#Y- zLZP%}^KDZ~=N*0K89T_$W%4^%yf<*m3Hzs&Qt|b0fiCJ-t;@2}VB+O?aDB>aGg0N? z!IZ2lz%2un#S@nZDSZt~-us4AK3BA7Eyv=Ox}ng)w(5t+V7=nu(|l z&O(b!YInJ0Pkv=cdrM9&xz}q*ryhM_4neH>80nk3={|e*%+0OxVDa6JTWRWN4tMaO z0I(d%f-_CNM$F*^4DFVb=u1pSr>Uc}e>mYdGBVPeIx^fA47|Tq+&cqb%g=kYI>K-p zgIkue)}t`|>vG?pPmV9)R%KBjxVUzy@o1_%m&CZ9H{HX9Z{3}&k(syIbaHd`bF@qf z5a!S!j6fjXzkd&eZMoK?(O#D>-Tz%o4S~mW6CBloTjb2DNVZ*+u_KW zw7ap3OL-;lKpdxPu6jBT8}baj?R(tHDudY{L_3@?nbX+dxe>4Y!F&b2nv-Nij9>l* zx`l@uV>k~hfioVQD_ZyI(EX&XrBxJ|m+33X#l}37FQot)t4lpWsRRPbJjQ%uG1cBWh|!jflM3Bj`)_FsBDVul=tfOcKst zYHM*m&QM`@Z_n>yFVnctT31KM)x@PR9IxTb^93%{&cd(wj%ZdvHq{S&V|8wYmB5`Z z&*R-U#}^O0=1+O%gZ{2tuZI9&dQVy!)4E6BrzfR*+}zxyd(0!b3{oD@jRp%kA|fJA zPR^q#?-G%Mv4;_{>`K0SsncoVPIG~jGB)VLJXy?YCvQoGUa=hn0-t?P`+fFXNCe%Z z?_L82fn3E4=aD!oZVn)1dj-*Kz5Ed8}jw7YXW@m@xw{ zozOmiL3p8jx}Kd~K_b_@D%ez8dvItd0q1pXx-{E%I5Yp>E=$lUSk5>>%XIqtWvuk? z;qW37kLA=MI-dM|1s}CPIX<8KYqo$8_WyAF#)W4Gd;8!2K0Hgiu4p zQvm+c7_u4;-%>pNvQ5hN2zL+K^FtVr`DeCip358lonZW+`{s~7 zmd8@|YvXO1Xiw0-yEUE!W2go|M)b5@remE#?1y@~CPBC=d=+`tKhXwA1PkC+k05D}L6fq3#K6U!Ea5RZ4wEfi~hRN;Fl{G>t_ZP?{FX~3A zN;dMeKRdyOzmA_0cD;3Tn;9WOPlqG0a>JU@KUxjB*+0_RsJAj~RDsG?NQJQOO|U4w z3llmuo0%1(&C8zf%d^Naw=Ns)T{l{_Z7 z`HG<*A%Q?OewAdSYQG`bCT7ODz&g2}5HjGEeNNd`wMc-51lUCZ)UcHUrYcyn}&b;Z0M1X(Em z&{0vGFV-5r-CBU<;XnOI_S*pOOzuY2-NH+*{}Usy#Bx<1%zKnF9x?1~DIp=*|L9H@ z#BAI7urX$Ed|aOp8ohSR&dh&Bsh4M6RU0hNO$DI~qRuHA+eKL>4m|x!ieaYkZ-2}B^!ZbDY5JF1Am9{BHQy&k;t%oz0N`A8@NPJzcBi*<4JMGRiOWy5F z5+XLMemF7vkcpzzf=)fSRZ>qy0!KA8F;KB6xjEu3p5WYPZ~0G^sQ#OL5Fm@`$MNmV z0&M6$Y1;QiKbMo=l-6DXo73YAp%m|;K2i-nR)eG%DZ!|2^&6#*j|uDuXWW`oEbN@;Q;0!i^%t) zWb%f_#KI&DI1(XATe|*=;SP)+>6vNOPKoMl`amGuxC$?XU!}wQ^+^YhTAU*{WWTE}}H|MDU9 zy*`Lbz&x6@`A#+|$QDfRwvMz^0)qL&VE89)YM{xQ`=ggs+E7K2E=d19Bl`n!Fe5}8 zcGL8YsiI%^Rop{&)q5TTl(?`eXRrE7u>>mxHY+rxL9$BAw+$R~R#?YLkPt_K>`VTt z>Wm3c5I>$a^_=G9f&NCn?vMWN3r^N+P93xM?4&OPYULzREkO{@N`EE zb^R+*DpbA!!nu`S4=wsS@=S7W_v2L&QeVz|1!BR-F~g@AEEm2crh>aDM~Mp<*PyxC zF7QO^wMN{PW{D>NyvYDYuMps62rwtTYK}kp^WVg;VgR5@?tl3K4m*GYi5{)sM;Qy9 zb{gO5KQArs%hr^YW#v}`haqYQ#>;9fMHq&1Nz&B z5|BZPGhB$|O(dlNx(2wwEV2spto79xEEXx>@+lHZOG$K5%>{!NQJ|hCh@%Z=m%i1+ z+bl&GqeOGBH)e8&UAiSqF?yRk%mYDT5KVGG8Bv?l=l02KJt!Li#BHZiGp!EtYMl=l zKzH+?<6jwr7NdTyf~rb$D+(;0rf6{J7}p{6IAUZ4*f~)y`YI_P1r84UJp9cPpaeKC z9Hd~vl9^ZjcC~r^ja029?@D)Pw=z|Juhiqi49IYzTb!)Jz?T$Su`LSgh*UT1rtOxd zRewsSrrkwYoRuYHMfGptglV79pu24r^^u@x@{57;_D}kmMpx!b$7e@al{_>Ry)aC*(zd=CkDS) zIwow>^nMMs6q;P#IJ-<7S5n@QHVZ_Lun*j%2m&HGyO+_rl@414=z|t>)6J%s5H2Q) zaHd?L*Q6M$-8^(o)g=0Frrx1T zS{Jcu!skfrGuZh3?jG$*i zgn^3g$d_hdNRAGuJYSL{BVs+sYvHvNy#$eA@zUCW^~eX-n|_C{3kxp6J>`ct^a<|l zyhbByzB<^wJP|(etLJXrj@6Whpzp82Z6e(y~$)8dV7e$z$|h;AV7dVl3QZ7(~|uFh+)J303}06M1HTkK_F zoGw_hQoATDq+y1|TV*+kO9R`SN@fhJ^6I_}o>@6ZEu(dTse;NoFWHR1kd*PE@yBX| zLx@;qFnA%30OMU#=!B|~U16|6yrG%WmJ-1RFlPYu91Rl&Bd<;v9L|ga#ojsJNhq#? zKe*-JPlWV>>BkMmCM>*AS65MYeij{{_P4x+0-wIpsn7>eQ{|9RA#l5@C_Ih}H@HF( zAVd5#QIY6KcfXWwSVL_(yyR-rs-tCl^z-k)s zkogMvK3k9+e(+_prvh&;SG`=rWfJ^Ph0lK*fZqz6ku#H5|0v|fOByg2MS@RL{s%zy z`x^eYG=i|`uiJ-~Nmx<-;=hpEG}*8J2=cbS<1Y`yf-dxtLK_pkLqYRX?$D0jyk*}5 zUVZpx7Nn&{44-%a#8nSTnzPU)x{vo+H^%Bc_qH@m7^hBtN8A|HyBh2RhnDeXkmDr+ znETRubw(AralVzNdjB_5%!VEs!d~G$cu2Sk!_0iRcUFaD>b=S|RqawedAeOvgqf|r z7Vmedk$aqzqRQ#X`+^{bShxj8HcTJBbGGI{ZccIG-dax_^nxDVdY!(^6SI`w@^Z{< z{b`&0G+f+ck5Xw9emJ(t`d*s-8Cq(Q_-Hb2R$?)S!en*&?3YTC>i5bW&r&>Z{x$2k zL|KsBIpSy2A3xap#JJQv$`v#AmUKZRBa@KV5Azc2`e$O|YR3ymaDLq^;e`IVlH!^y zW|`L!kpUFi-<1~*AAhr(F{cHjeGkHuRJMn+11P)dPrJpAP3Kg4dl;$5cXoTCBt#ERS+3Y0Mflxx^~=>EY%Se-sHLkUd+=t_7^cHNT+`sOV$iE#N|i z-Y=2R)~IeE1(W3dd}_&w)7jVs;}qoQnK#J#b5d)%zNd!MB~LLir*eQEq_><_St$fE zKO2}E5^-8jieaDR&VM#fP+q9^?g%n{ZcOjAtbG)v9@&om69Cb;SPx377MnbmMuxnj z+(chY%)l^u3=~^arDAEPjV&w^2bbxJTxRSF!KhmRL+qfVAG~WX8Z#!jw~7_f;NJD! zbMNhv1&+2o`%XR~ww(}5=?il`j4`MclR9n=>fRG|oc1o1)m7lw@9)LBWxb%6HlS!Y z`Tc$5JNt69I_&w^JypC&{hK)Y38BZNn4i^0p{P<}-PGs??9$7@p*)|-N01Hl#kj*r zh+pmOM`&SAUbOEd zXWrrWad71@7J46kxn|PX;g){^kboZQzo+GJ^Xsm?dvCA{$Z3iBzs=*_-#q|z@s)=2 zYTHKirSoa_4Eg(we^kT5yN2Q3#M>pgc!>Z)fIQ8YC;zy*^baXDG5f1&r{->Lnq5*j z1z0=6K55q%_#W++sRgHZ)6$y7`WVW}=M!v&3pL7@u*|5siz{*rFnF%FZT{L?C`!fN zA4{LidBzC7Gj*75<+kbq;!YO%O|#4*hH5$<>dlxoGGjcnicFSVzj#{|u;Ys_KLX@n zoHj%?oReC5yM^+^l`nyZ)8drW~DT`=T>0?9^QUFnU{*_gWq8AZ_6Lz ze9p#3vDkF}ouhSDCn{IDT%O*W@?!Of_$V|4b=ud$9xNlvX3Bh(xqu&W{mrDbcLOr3 zfure0O=<3ziXK_p**R7<2C*z?PaIA3n`_lHBUk57@E8SdQycSD{!?Pz!cs*$asV?a zU_DM0Y?Q*J68b=1r%==n>)EP*jZBn`-L0>UEt_mONAzfCCb~FjT`gu5o4+`_Pqvq~JMhRPpgu{O>mp*sv%1DaAQmb< zHGK)B%87B>5gGg#>-B6I&L9oS6^~EjMr`@s3w97;?xgB}BR*h99zTVdVs+QW2nusXAZFDb|QqnSm_O3_ua8T3$rp!?f&XlKE9&l99Eba zg5xB~Y?gV0^$U}!CrwM|0CQG^Q>|}Ojj(gask|;@0dUE{@oUf} zrEMJHrk2;}N?SeC;bsGLq*NP^zmg%vX3uobC`jTer_W(T>}CsrD6 zrYnz=)L;3H;p_z?B;~u44HTNR#{5-M{-K)PLvV*GmLs^lE#f0BeH3YWEIWd2;26bK zZQ_S%+X{bm^gup;P=kPCL?2Q>#zNA~D(vceW~i|fb^55OqQ}{;rgra8-8a9~>!ip# zgM5+C0OIb9mo`wfwx^FJb$mwm=Y?wNQ9Sqp6YH4es%n{l&6vMhY#1p6@lC9z*Qtx2$!?dM=QjM~_ z*N4&?8#koN)_zR{$NiNSwQ#r+jS4lx```J|VYklijl@+diW-h@lo!%J1m%Bit ziKmX*@`|>RuVsABMos+ql0{D_ZaeyXsc0yMe$`j7kk`V&)qnK@aOl+UE0MnTGD~6d zupE0k05$LbNLKw$66w03J>vPXq>r zSX(Mi3M&pyAl9LC1@4x~L?&v7INHQ0C{*MJlKVwfR8i3TE^@=3l8huejxUnjUObk} z)vJc-_%`e67`=ok1%z}l(uwhcw+HHTho9Ts%`^Yya-_y&*L^14mUEbQT~FHBh>MLT zd@DaB{QzC+)s>x=P~!)~vjDIbctZGX5PG#iz8d~A{k1sKcU513!>x~V-gEsI^x6L zm?U`}&(n%3wHFm1{tPguXp5-#C`65?d@QUKRfFJRQQI|?@|Q{x=U@I8(v!L~=R1Wk z7^ejD^XEMa3ttvEtoOx#t-4C4sRs|vKExxPSqT;os9tv7>^C*s#g{eYtp8!Q_>V%u z|A7vQf4&FfpQoTa1em;CBw+Rs{{3qN?|IYg=k^!AP%A5WZAVzaS<2cJZu{~j_1Q@Y zL$dJB)cc&u$VN0fUM=5J{{xs-R++P9_W-b?+ry_-K>yjrv45V!UYR$^ef|PALVQ$e zu1)YK5Z5^wO3U7rFOD!yYBJM4@@+%mOuV>8q}A=y{!XUe*7oL9dTsl7+;U-xX#nv^ zF=%fnPNZ#}cG)WhELvQ+69Xin{k~0G|>0 z1g|kXWgE}|HMyc{(r}oQF1yS9>3S%{6COWAtI=JzIVKqkJ^5@1qL*+{)l@jtFG&?E zynBg_eSH+q>glBa;FM3TrL!{nMV&1Uyo!~Gr){V?+-jz|Xf9IIyN?E$3OVXM>8Zqn zgY(u>HD$jGq=I4moqMLSW(E}=OYdSY3U@CyBP-*B&fp{J@Jg9+JIjN)NBYNbG5)Vx znp0}OKGxY|NZT}~Wq$Ls%^l{RHn3$a#o)yb=wZgCA~POpE#uNpVk2G}`f3CZPNjyS z(65=d1VOGN6}pue!g-|FAY(DQ&SEUu{jrD29jn{F8CXr~oM1nJb_1|oXG}(}DfkWg z#K1~igm?MFi;<>I1TqaiG1YgvC>&IK*znl;G@9pW#=Y8kC9k(M;VjdPw|9(~zp>D) zvC_GyX06K|pvV=CVW<+2m~7XYhRfobNRyoUY{g44vM%P>L8QHC%q?CU_iOkNn zy)TY4gvsdLdC@~BEWpd<_}y*dL($Zjr?>ol>64%DC_;x8cf748oWyEPex(nmYUH*y za?{@;8Pu0H(>5}jAJ^@_n}70t%$AFH(`dE2;+_chmq_?7T<3DrJHw>pMo^kCP8V;sWecS@bIcFbCw$-LnvnI)l6?}W36p*4D6edJL(SBKJiG;&t@<#&%OQ1x2}2CmDsuzk#H8u zWsN#%u-21yl(p(M_w^5pRvu4>8O27f5^I1m;q!gXKfdqJb-?bRJ#c&%$rXSraE7`R0v+@W!t-#KeLg*SS`cYB1Uq_L`2$YYT| z{yg*CuH<#Z*_44_CudU5FRVQ`_D^6+LErGaL*wE^waK;%)5&ol2zfG%yl7ocia4EW zXme>dTfH;#3B21Rvm)41H{DQmfJTz>d<0_RjxM}QOfax`#}iy%V0*Q*kKf4*VxW2o zagy1X#k0Y+2jd$#B_<|85?opNMp;QG;cn>AYSWdyk$fPb{ zBz`C6C)uF$*{>n*4&j>RdyUf@KHFlz>9gT&X-miYSb)90-z2T;-)fz~tu{c>S31OO z13MacNufVipiCQd!MK|KDEL1B>%}{t250o?j0IY5Pu}b_)U2CxN4Y=KKVL(eK37-L ziW8QQ;FkDBFRkt@nNE>6IO{5ot3pbQJ}g=RhuB>d+$FR%9z{wE|nEn^eR;#27$^u%U|%u#<#&EaL-hF*NSY<&6eG@X1i1UM8Kdc@=E zQ#CpD-*+4v^0y+bj_yo}56DW@Ls4^LhwtXFxA8Oe-zw9`J&e{t^-VA5dskLeJ?c># z;cF~6M~xHG$FtyqbAhY@t}1Y5HaST6=(0f9$A6=m#MN2=Q?}PWAq8R*1%c3`lV9qG zpM9;WVEgEFG#R0$xX4*4OlZjMv~FhEUAGK|myeX-cJ!QVx11ZY9A&kPhGS%hwxcHE z@k{{fN_uttxiDQx4jG~|6XgO;LhQAvY8xeaq z4l;2+`UoC`w<<+umd*^DW(}H3K2zDcrOWD2Hc%3xZ1CM8>*CS`LO(lR{qF#L|9%&X z*`)o)yV%h7as4Dr#dxZibcfQ(<<6N}VfuP=Yneb_?o`9v3^9$8(!yc=MyEr>B!%9G zt?O11T?h3)(qn-r)`D?O^rH^1a%m3J@vjFxInG-O2ecu)=qu;gaW{DvSB%ji2T zGH(spzulASqMfq-OjZIYkgwq=%1KF||?_ciB8`47ANENIvvu$OfsIvD>pk@!<7BQQd2l@0X(KC6Sn- zJP#!d9mvSKHKDaTy?SJfQ^V+uy3v~+wUc=v>*&-*yTRhL`rt=o`1Sz+wH)hL!1A>8 zUG6at7t@u^gWwBq)0Ox}{tK$DgdyQoPi=Wa+sLcAeDJOJa|bA7VUoIPQc7IeHE>P= z^lNdpy1@QtKw%04j|!M$ zZ+4<%smf7hXaT6%uoFA5TlYN+*AuL{Il#&vv+8N%mPYPr~^(R-2|?BNdX*$ro<(d2Tg@$|F3H zc{M`? zrepxPKdy$|mv5?z^!6OfZS7eKCoNHKD$2|n0vstOPi5&-{^10su~FMbHXO_yzJ@uY zy8Yre)VH)gM~aX-p7oT~?bnth{AO5M%gPYb6U=a(5Ei-M&RJ*eezBUxbt#mzA}$=?R|QRJm<%XDa@Ut z-hVWZvNakC6{uhe7K!~6s@1i-E1D2n#fS4-WAhmDMNa8>|JDOWzp63;%YoyTj0_tV zJk37SPzL`?z>uIC4v(mL4 z+)kfa&)*rooXOVnApqap#&W}qT`!TmcG~lv1U(osM!q_QVll)==rwqHYE=B zrL_%p&1u1ia3(&%j`}-(@*7fXGB>pCzBmk@H9rwn?bNbHjOv_BeooGc8-q#jwqFdI zNveN!9=l~#{B~?!SnBCz!-@ZoxEccS`}@nSZph|Jv-i|Dkd@{=-gM;rO>T4 zHc&}AX&-h!U7%e|Bq0%{Y~DGrZ1%v+5mWEATH%_{%?7(8^ zwiYF(X*hA;uK=s6$=e)dI}-cg_TBogBQU+_ta0nT5_*ZoiYJ$Y-?*E_+x!8C@ZUdg zT>)KVuP>0Hjy2U~-SohSjcC-UD+tsbe=i>LOZ|WR@u?-z`2FdCxya~*4Ce$ZS_lH7fRMmBh8+3!vN$*7FJGyM8C|9M`KzgW*S z1%7zBY7QHK9?H*1Ayl6J`L7;is*o25%|#p-X|xXamZhSCteLlFvLg`kK_Ji0^qB8| zbI}q_QTfB0%!#Z}nnb-J^vk<*yVu&gJICwyi*4QF7=Ci&s^9Qi!A5(&8czA9oqcks zzC-K%1WyyUz+j>)^4fD4#HH_WRegKSJa6{8N3g?Lw&X9UF9x56tbI~#@2y!)TuePX z*iLd;pAHLoAmRGwkG0<> zjYF3jkpso`g_~b;cT-RWjBYcoM4^6|AS!L`a66pGYTDJ$*bsc*rw1PsmI^!JitVqfe{crCV`?pcx zziCPP1bdK^pp{)|!z_(&SL=(Pqw=qOFk241Nvx8vGPQ+|UC<24LO@Z%K#InCGrhJ- zMF{mvl~CQY%|ChrM%p*=ugw*w5**j;q@-liK<^g~fg2%`O-YhXiP<~&xyYrtasgmu z^#y87XIIm(2@k+a&7~5@Kg4b$jq&^5xy6ko05}w`Cq_?7?y9D$3a)4ZKal5s*?*6Q zCnZ3U`^^fhW*{C9egd#@GZP-8fP1soM;zRRIrJmyHf&|>>P!}Z$T1LDtgS5sq zbMH_cBn1YanZ`_Fvwfw$bQG;n1Mm}Lmm3Lm{n!pv z(*RqL$b-6S5MGmg)#+Xck937`Kzef!gePS40zZZ$|Z85zU^s3jWY?smQ!k z0)x;2BVJBDZUY6zAfXBiIwLTeA=GEeqoz`10N3Wq_6E%H>e=rQTO5FH_ec%gYYxG* zUbKi~4IZJ4?wN9zUK8h%V5Vq&$P|S8P{yDV6x=#%!$<(%c(5{h#bpTJXyNa%`V8PO zkvn8mpjV;Q`7k2HmO({=OkM$uXtPjQ|Dh5ZlAmC~Ks7jIl$vXigF}xwug<51S#@Td zv;ZBjW;sP`#bMotZj!iRppxWytx&p7lGaT|w)_=$W#()_$D24w-KD70C(u)|)vk_w zi-Y}ZzLHznSlQuB2&FfulB~jQ_3VXq>N5pfgc@Yn_j60qez*(Y2VUY~U|$e2neG&7gT~eTm7O7iggRH2jWLm<3(7+9L%pCq?VM@rMav zAA_`V^T`Gu;(CaT!1C)h7c`uDHw=C@iwBSUg$~}5b%V(E_9MPaMu4lHJ*npR$-#GN zTZxl>ZlzmYftT7g<3<3?<#`i~GGK~yU7@)`2_KDk<>_-nT4O@;nJ5M;u9W2`Xa%8i z%dSZ&?KyI>XVOzevW(zTWnkC9+Gt&Iq7x#yIcoG;Git_Kp4)bsFdym5DG2*B@Ohk>_7oHl`-!P`+I+{yc?U!gS zkgtnk;RWlnTXi4MgkCe}-Dgm#WTksnWEmuBn2P7O=k40FU3+ik3Q-!3QX1oK1#rKLXBSoG6rQ8f$mjyGl>p8S_J*2 zKU(Ws{-0UF#!tkJRw5BFDuXru;S0YXf!bEh=#`{oDS35%-d|mBNX*!X(xykvs;l6D77=Ty-(;zD7 zQ|Ec9F=g_{!$#xxsWZD;zBWrhC!;I0`dK|}1+Vj3{orXubsF^3dnu@Fn~2Fir|Y8Gd8Ga%_Yt&_e{Se4b2tw`U0rPR*v>MGu|t#Dh@ z_-mw5PP%JV*nrbq_z0C$ksUH&8{vDpd<{ktddBsFs}!sHZHG6uj_ko0i|GNBNWfB`t6Ab`d3&#InfPcy7 sU&isT1@kX&@h?~TuQ>6a6C?> Date: Wed, 29 Apr 2026 20:10:19 +0000 Subject: [PATCH 165/211] Update dependency typescript to v6 --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- modules/widget-lifecycle/element-web/package.json | 2 +- modules/widget-toggles/element-web/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 93feb7cae4..1a3cbc269a 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -22,7 +22,7 @@ "matrix-widget-api": "^1.17.0", "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", - "typescript": "^5.7.3", + "typescript": "^6.0.0", "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-svgr": "^4.3.0" diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index c03f5db255..9855fca27c 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -25,7 +25,7 @@ "react": "19.2.4", "react-dom": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", - "typescript": "^5.7.3", + "typescript": "^6.0.0", "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0" } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 14f1539528..c1d2133668 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -19,7 +19,7 @@ "@vitejs/plugin-react": "^5.0.0", "react": "19.2.4", "rollup-plugin-external-globals": "^0.13.0", - "typescript": "^5.7.3", + "typescript": "^6.0.0", "vite": "^7.3.2", "vite-plugin-node-polyfills": "^0.25.0" }, diff --git a/modules/widget-lifecycle/element-web/package.json b/modules/widget-lifecycle/element-web/package.json index d45ba8f861..d1531de023 100644 --- a/modules/widget-lifecycle/element-web/package.json +++ b/modules/widget-lifecycle/element-web/package.json @@ -15,7 +15,7 @@ "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@vitest/coverage-v8": "^4.0.0", - "typescript": "^5.7.3", + "typescript": "^6.0.0", "vite": "^7.3.2", "vitest": "^4.0.0", "vitest-sonar-reporter": "^2.0.0" diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index b4f1a3e1bf..740d79b83b 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -24,7 +24,7 @@ "@vitest/coverage-v8": "^4.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", - "typescript": "^5.7.3", + "typescript": "^6.0.0", "vite": "^7.1.11", "vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-svgr": "^4.3.0", From 746a022a95bbc9ebba6743e608bb9a09d52867cc Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 15 May 2026 13:08:10 +0100 Subject: [PATCH 166/211] Use the rejectToast function from utils instead of a fixture --- .../element-web/e2e/widget-lifecycle.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts index 36022edbf5..9fa2bb6eca 100644 --- a/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts +++ b/modules/widget-lifecycle/element-web/e2e/widget-lifecycle.spec.ts @@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; +import { rejectToastIfExists } from "@element-hq/element-web-playwright-common"; import { test, expect } from "../../../../playwright/element-web-test.ts"; @@ -63,8 +64,8 @@ test.describe("Widget Lifecycle", () => { }, }); - test("auto-approves preload and identity", async ({ page, user, homeserver, toasts }, testInfo) => { - toasts.rejectToastIfExists("Verify this device"); + test("auto-approves preload and identity", async ({ page, user, homeserver }, testInfo) => { + rejectToastIfExists(page, "Verify this device"); // A bot creates a room with the widget pinned to the top panel, then invites the test user. // Because the widget was added by a different user (the bot), Element would normally show a @@ -121,8 +122,8 @@ test.describe("Widget Lifecycle", () => { ).toBeVisible(); }); - test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver, toasts }, testInfo) => { - toasts.rejectToastIfExists("Verify this device"); + test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver }, testInfo) => { + rejectToastIfExists(page, "Verify this device"); const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( @@ -184,9 +185,8 @@ test.describe("Widget Lifecycle", () => { page, user, homeserver, - toasts, }, testInfo) => { - toasts.rejectToastIfExists("Verify this device"); + rejectToastIfExists(page, "Verify this device"); const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( From aee4cd9cdb9ce05c8fdf03a47f5118365fd61edb Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 18 May 2026 18:59:39 +0200 Subject: [PATCH 167/211] Close toasts in playwright test So the test can srtill see the room list tile after https://github.com/element-hq/element-web/pull/33444 --- modules/opendesk/element-web/e2e/opendesk.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/opendesk/element-web/e2e/opendesk.spec.ts b/modules/opendesk/element-web/e2e/opendesk.spec.ts index d4fe5ee1f3..227f59a3b7 100644 --- a/modules/opendesk/element-web/e2e/opendesk.spec.ts +++ b/modules/opendesk/element-web/e2e/opendesk.spec.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import { rejectToast } from "@element-hq/element-web-playwright-common"; import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; import { test, expect } from "../../../../playwright/element-web-test.ts"; @@ -203,6 +204,9 @@ test.describe("Opendesk", () => { user_id: user.userId, }); + await rejectToast(page, "Verify this device"); + await rejectToast(page, "Notifications"); + await page.getByText("Trusted Widget").click(); await page.getByText("Accept").click(); From 41008452d862a9a6720087a7367aca4bac32745f Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 21 May 2026 16:27:48 +0530 Subject: [PATCH 168/211] Update screenshot --- .../preview-bar-linux.png | Bin 5165 -> 5177 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index d08ce9721c34f7f0b309b1fb26bb969b9dccb7db..2fd705ebe43d7f06068d6d0eae6dca451409d39d 100644 GIT binary patch delta 5171 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 Date: Fri, 22 May 2026 15:55:09 +0100 Subject: [PATCH 169/211] Update screenshot I don't know why this is now failing, it had already been updated in https://github.com/element-hq/element-modules/pull/269 . I guess I'll try updating it again. --- .../preview-bar-linux.png | Bin 5177 -> 5164 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index 2fd705ebe43d7f06068d6d0eae6dca451409d39d..e41a90c749c91b1df4c343db9de5572f38b6f62d 100644 GIT binary patch delta 5157 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 TrcM0k00000NkvXXu0mjfCmoDOj$3#UUI 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 From 9d33c0a72986c14df3c3cf741068ca6cee29a41e Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 26 May 2026 15:43:22 +0530 Subject: [PATCH 170/211] Update screenshot --- .../preview-bar-linux.png | Bin 5164 -> 5173 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/preview-bar-linux.png index e41a90c749c91b1df4c343db9de5572f38b6f62d..55ae944415ffccf7863b85a50c351ec6eafe0931 100644 GIT binary patch literal 5173 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 aR09C1g;>3&P5kEo0000 Date: Tue, 26 May 2026 16:18:35 +0530 Subject: [PATCH 171/211] Retrigger sonarcloud From 7242d1727dd418c5ab8800cea4839e11a8364f49 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 28 May 2026 14:33:53 +0100 Subject: [PATCH 172/211] Allow the banner title to differ from `brand` --- modules/banner/README.md | 11 ++++++----- modules/banner/element-web/src/Banner.tsx | 5 +++-- modules/banner/element-web/src/config.ts | 7 +++++++ modules/banner/element-web/src/index.tsx | 1 + 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/modules/banner/README.md b/modules/banner/README.md index bd18b28b25..21955c4e63 100644 --- a/modules/banner/README.md +++ b/modules/banner/README.md @@ -5,11 +5,12 @@ Allows rendering a top bar with slide out left panel menu. Supports the following configuration options: -| Key | Type | Description | -| ------------- | ------ | ------------------------------------------------------------ | -| logo_url | string | URL to the logo to render in the banner | -| logo_link_url | string | URL to send the user to when clicking the logo in the banner | -| menu | `Menu` | Data to render in the banner menu | +| Key | Type | Description | +| ------------- | ------ | ---------------------------------------------------------------------------------------------- | +| logo_url | string | URL to the logo to render in the banner | +| logo_link_url | string | URL to send the user to when clicking the logo in the banner | +| title | string | The title to render next to the logo, falls back to top level `brand` variable if unspecified. | +| menu | `Menu` | Data to render in the banner menu | The `Menu` type is fulfilled by the following discriminated union: diff --git a/modules/banner/element-web/src/Banner.tsx b/modules/banner/element-web/src/Banner.tsx index 92087743b6..112470d4e8 100644 --- a/modules/banner/element-web/src/Banner.tsx +++ b/modules/banner/element-web/src/Banner.tsx @@ -37,9 +37,10 @@ interface Props { logoUrl: string; href: string; menu: ModuleConfig["menu"]; + title: string; } -const Banner: FC = ({ api, logoUrl, href, menu }) => { +const Banner: FC = ({ api, logoUrl, href, menu, title }) => { let menuJsx; switch (menu.type) { case "static": { @@ -59,7 +60,7 @@ const Banner: FC = ({ api, logoUrl, href, menu }) => { - {api.config.get("brand")} + {title} ); diff --git a/modules/banner/element-web/src/config.ts b/modules/banner/element-web/src/config.ts index 3a01e3e8ef..678ed0360a 100644 --- a/modules/banner/element-web/src/config.ts +++ b/modules/banner/element-web/src/config.ts @@ -89,6 +89,13 @@ export const ModuleConfig = z.object({ */ logo_link_url: z.url(), + /** + * The title to show to the right of the Logo + * + * Will fall back to `brand` variable if undefined, can be hidden by setting to the empty string. + */ + title: z.optional(z.string()), + /** * Configuration for the menu. */ diff --git a/modules/banner/element-web/src/index.tsx b/modules/banner/element-web/src/index.tsx index 538fb8b3af..66859118a1 100644 --- a/modules/banner/element-web/src/index.tsx +++ b/modules/banner/element-web/src/index.tsx @@ -47,6 +47,7 @@ class BannerModule implements Module { logoUrl={this.config.logo_url} href={this.config.logo_link_url} menu={this.config.menu} + title={this.config.title ?? this.api.config.get("brand")} /> , ); From e8d6e4cdc9aa16d12ef37d2b76ca90e68bba08aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:22:19 +0000 Subject: [PATCH 173/211] Update react --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 4 ++-- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 1a3cbc269a..1c328f390f 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -20,7 +20,7 @@ "@vitejs/plugin-react": "^5.0.0", "matrix-web-i18n": "^3.6.0", "matrix-widget-api": "^1.17.0", - "react": "19.2.4", + "react": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 9855fca27c..ff92b3028d 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -22,8 +22,8 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "react": "19.2.4", - "react-dom": "19.2.4", + "react": "19.2.6", + "react-dom": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index c1d2133668..40cc632b24 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -17,7 +17,7 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "react": "19.2.4", + "react": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", From d9e0bd9bf77e755d668fcdf992be0ef83f5a84dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:23:47 +0000 Subject: [PATCH 174/211] Update vector-im --- modules/banner/element-web/package.json | 4 ++-- modules/restricted-guests/element-web/package.json | 4 ++-- modules/widget-toggles/element-web/package.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 1a3cbc269a..3d86c55a8d 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -29,8 +29,8 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", - "@vector-im/compound-design-tokens": "^8.0.0", - "@vector-im/compound-web": "^8.0.0", + "@vector-im/compound-design-tokens": "^10.0.0", + "@vector-im/compound-web": "^9.0.0", "framer-motion": "^12.4.10", "styled-components": "^6.1.18", "zod": "^4.0.0" diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index c1d2133668..3a6eefe0f6 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -24,8 +24,8 @@ "vite-plugin-node-polyfills": "^0.25.0" }, "dependencies": { - "@vector-im/compound-design-tokens": "^8.0.0", - "@vector-im/compound-web": "^8.0.0", + "@vector-im/compound-design-tokens": "^10.0.0", + "@vector-im/compound-web": "^9.0.0", "styled-components": "^6.1.18", "zod": "^4.0.0" } diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 740d79b83b..d0cd33d225 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -32,8 +32,8 @@ "vitest-sonar-reporter": "^2.0.0" }, "dependencies": { - "@vector-im/compound-design-tokens": "^6.0.0", - "@vector-im/compound-web": "^8.0.0", + "@vector-im/compound-design-tokens": "^10.0.0", + "@vector-im/compound-web": "^9.0.0", "matrix-widget-api": "^1.17.0", "styled-components": "^6.3.11", "zod": "^4.3.6" From cff89430f6b2f2870821686d93f8e730c21f7841 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 3 Jun 2026 16:43:23 +0100 Subject: [PATCH 175/211] Remove unused export --- modules/widget-lifecycle/element-web/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-lifecycle/element-web/src/config.ts b/modules/widget-lifecycle/element-web/src/config.ts index ac22f816b2..c2e5b9166c 100644 --- a/modules/widget-lifecycle/element-web/src/config.ts +++ b/modules/widget-lifecycle/element-web/src/config.ts @@ -12,7 +12,7 @@ z.config(z.locales.en()); /** The config.json key under which widget lifecycle module configuration is stored. */ export const CONFIG_KEY = "io.element.element-web-modules.widget-lifecycle"; -export const WidgetConfigurationSchema = z.partial( +const WidgetConfigurationSchema = z.partial( z.looseObject({ preload_approved: z.boolean(), identity_approved: z.boolean(), From 3bf5baadaa1811f0f2e615d7e60cfea791aeb494 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 3 Jun 2026 16:54:42 +0100 Subject: [PATCH 176/211] Handle breaking change --- modules/restricted-guests/element-web/src/AuthFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/element-web/src/AuthFooter.tsx b/modules/restricted-guests/element-web/src/AuthFooter.tsx index 4062c57645..5ff6b11851 100644 --- a/modules/restricted-guests/element-web/src/AuthFooter.tsx +++ b/modules/restricted-guests/element-web/src/AuthFooter.tsx @@ -49,7 +49,7 @@ const AuthFooter: FC = ({ api, config, onLoggedIn }) => { return ( - From 46b6254996727047d37d7e65685da7fcb7e6a7d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:15:38 +0000 Subject: [PATCH 177/211] Update vite --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- modules/widget-toggles/element-web/package.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 3d86c55a8d..216dbb5d4a 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -24,7 +24,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", - "vite-plugin-node-polyfills": "^0.25.0", + "vite-plugin-node-polyfills": "^0.28.0", "vite-plugin-svgr": "^4.3.0" }, "dependencies": { diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 9855fca27c..ab7122a9e4 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -27,6 +27,6 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", - "vite-plugin-node-polyfills": "^0.25.0" + "vite-plugin-node-polyfills": "^0.28.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 3a6eefe0f6..f899b5089d 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -21,7 +21,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.3.2", - "vite-plugin-node-polyfills": "^0.25.0" + "vite-plugin-node-polyfills": "^0.28.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^10.0.0", diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index d0cd33d225..21eaa56d6e 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -20,13 +20,13 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-playwright": "4.1.7", "@vitest/coverage-v8": "^4.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.25.0", + "vite-plugin-node-polyfills": "^0.28.0", "vite-plugin-svgr": "^4.3.0", "vitest": "^4.0.0", "vitest-sonar-reporter": "^2.0.0" From fb1ddda7c85328e6ea261b49697e214aa76ef8fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 3 Jun 2026 17:41:36 +0100 Subject: [PATCH 178/211] Avoid mixing vitest versions --- modules/widget-toggles/element-web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 21eaa56d6e..bf5b8de940 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -20,7 +20,7 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", - "@vitest/browser-playwright": "4.1.7", + "@vitest/browser-playwright": "^4.0.0", "@vitest/coverage-v8": "^4.0.0", "react": "^19", "rollup-plugin-external-globals": "^0.13.0", From 2a6e185be959d8721312fd654777d986ecca9fb9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 08:13:41 +0000 Subject: [PATCH 179/211] Update vite --- modules/banner/element-web/package.json | 4 ++-- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- modules/widget-lifecycle/element-web/package.json | 4 ++-- modules/widget-toggles/element-web/package.json | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 6de5f92bce..c9a36d3b84 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -23,9 +23,9 @@ "react": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", - "vite": "^7.3.2", + "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", - "vite-plugin-svgr": "^4.3.0" + "vite-plugin-svgr": "^5.0.0" }, "dependencies": { "@radix-ui/react-dialog": "^1.1.6", diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index a4bc223a15..c69700b632 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -26,7 +26,7 @@ "react-dom": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", - "vite": "^7.3.2", + "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index db6e4e99f2..70d9b16709 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -20,7 +20,7 @@ "react": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", - "vite": "^7.3.2", + "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" }, "dependencies": { diff --git a/modules/widget-lifecycle/element-web/package.json b/modules/widget-lifecycle/element-web/package.json index d1531de023..44f50e50b3 100644 --- a/modules/widget-lifecycle/element-web/package.json +++ b/modules/widget-lifecycle/element-web/package.json @@ -16,9 +16,9 @@ "@types/node": "^22.10.7", "@vitest/coverage-v8": "^4.0.0", "typescript": "^6.0.0", - "vite": "^7.3.2", + "vite": "^8.0.0", "vitest": "^4.0.0", - "vitest-sonar-reporter": "^2.0.0" + "vitest-sonar-reporter": "^3.0.0" }, "dependencies": { "zod": "^4.0.0" diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index bf5b8de940..1fb7b28dd3 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -25,11 +25,11 @@ "react": "^19", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", - "vite": "^7.1.11", + "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", - "vite-plugin-svgr": "^4.3.0", + "vite-plugin-svgr": "^5.0.0", "vitest": "^4.0.0", - "vitest-sonar-reporter": "^2.0.0" + "vitest-sonar-reporter": "^3.0.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^10.0.0", From 2321dc8f63859701da453309deede53b488367ed Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 11:11:29 +0100 Subject: [PATCH 180/211] Use esmExternalRequirePlugin --- modules/banner/element-web/vite.config.ts | 10 +++++++--- modules/opendesk/element-web/vite.config.ts | 10 +++++++--- modules/restricted-guests/element-web/vite.config.ts | 10 +++++++--- modules/widget-toggles/element-web/vite.config.ts | 10 +++++++--- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index d7ae139b11..f91b5b95e4 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vite"; +import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; @@ -27,8 +27,12 @@ export default defineConfig({ outDir: "lib", target: "esnext", sourcemap: true, - rollupOptions: { - external: ["react"], + rolldownOptions: { + plugins: [ + esmExternalRequirePlugin({ + external: ["react"], + }), + ], }, }, plugins: [ diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 8f1d83e06f..b18db2791f 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vite"; +import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; @@ -25,8 +25,12 @@ export default defineConfig({ outDir: "lib", target: "esnext", sourcemap: true, - rollupOptions: { - external: ["react"], + rolldownOptions: { + plugins: [ + esmExternalRequirePlugin({ + external: ["react"], + }), + ], }, minify: false, }, diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 24798ac03f..7c90bde5f7 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vite"; +import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; @@ -26,8 +26,12 @@ export default defineConfig({ outDir: "lib", target: "esnext", sourcemap: true, - rollupOptions: { - external: ["react"], + rolldownOptions: { + plugins: [ + esmExternalRequirePlugin({ + external: ["react"], + }), + ], }, }, plugins: [ diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts index b65e3ec8ea..a0c42c6ee0 100644 --- a/modules/widget-toggles/element-web/vite.config.ts +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vite"; +import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import externalGlobals from "rollup-plugin-external-globals"; @@ -27,8 +27,12 @@ export default defineConfig({ outDir: "lib", target: "esnext", sourcemap: true, - rollupOptions: { - external: ["react"], + rolldownOptions: { + plugins: [ + esmExternalRequirePlugin({ + external: ["react"], + }), + ], }, }, plugins: [ From 82b5397c7daa7a261c42475657874bec9bdde546 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 11:36:15 +0100 Subject: [PATCH 181/211] Use rolldownOptions.output.globals --- modules/banner/element-web/package.json | 1 - modules/banner/element-web/vite.config.ts | 11 ++++++----- modules/opendesk/element-web/package.json | 1 - modules/opendesk/element-web/vite.config.ts | 11 ++++++----- modules/restricted-guests/element-web/package.json | 1 - modules/restricted-guests/element-web/vite.config.ts | 11 ++++++----- modules/widget-toggles/element-web/package.json | 1 - modules/widget-toggles/element-web/vite.config.ts | 11 ++++++----- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index c9a36d3b84..a2af841d74 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -21,7 +21,6 @@ "matrix-web-i18n": "^3.6.0", "matrix-widget-api": "^1.17.0", "react": "19.2.6", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index f91b5b95e4..6aa89587be 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -10,7 +10,6 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; @@ -33,6 +32,12 @@ export default defineConfig({ external: ["react"], }), ], + output: { + globals: { + // Reuse React from the host app + react: "window.React", + }, + }, }, }, plugins: [ @@ -42,10 +47,6 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], define: { // Use production mode for the build as it is tested against production builds of Element Web, diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index c69700b632..70814d9062 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -24,7 +24,6 @@ "@vitejs/plugin-react": "^5.0.0", "react": "19.2.6", "react-dom": "19.2.6", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index b18db2791f..4a9353fb03 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -10,7 +10,6 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -31,6 +30,12 @@ export default defineConfig({ external: ["react"], }), ], + output: { + globals: { + // Reuse React from the host app + react: "window.React", + }, + }, }, minify: false, }, @@ -39,10 +44,6 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], define: { // Use production mode for the build as it is tested against production builds of Element Web, diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 70d9b16709..b014f82147 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -18,7 +18,6 @@ "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", "react": "19.2.6", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 7c90bde5f7..4517c4584c 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -10,7 +10,6 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -32,6 +31,12 @@ export default defineConfig({ external: ["react"], }), ], + output: { + globals: { + // Reuse React from the host app + react: "window.React", + }, + }, }, }, plugins: [ @@ -40,10 +45,6 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], define: { // Use production mode for the build as it is tested against production builds of Element Web, diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 1fb7b28dd3..38e6b87177 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -23,7 +23,6 @@ "@vitest/browser-playwright": "^4.0.0", "@vitest/coverage-v8": "^4.0.0", "react": "^19", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts index a0c42c6ee0..f50e35e54d 100644 --- a/modules/widget-toggles/element-web/vite.config.ts +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -10,7 +10,6 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; @@ -33,6 +32,12 @@ export default defineConfig({ external: ["react"], }), ], + output: { + globals: { + // Reuse React from the host app + react: "window.React", + }, + }, }, }, plugins: [ @@ -42,10 +47,6 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], define: { // Use production mode for the build as it is tested against production builds of Element Web, From 9d9991d838e158f05223b228b20c3fbe20358ef8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 12:18:22 +0100 Subject: [PATCH 182/211] Use rolldownOptions.transform.define --- modules/banner/element-web/vite.config.ts | 12 +++++++----- modules/opendesk/element-web/vite.config.ts | 12 +++++++----- modules/restricted-guests/element-web/vite.config.ts | 12 +++++++----- modules/widget-toggles/element-web/vite.config.ts | 12 +++++++----- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index 6aa89587be..5556992672 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -38,6 +38,13 @@ export default defineConfig({ react: "window.React", }, }, + transform: { + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": JSON.stringify("production"), + }, + }, }, }, plugins: [ @@ -48,9 +55,4 @@ export default defineConfig({ include: ["events"], }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - process: { env: { NODE_ENV: "production" } }, - }, }); diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 4a9353fb03..d55959680e 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -36,6 +36,13 @@ export default defineConfig({ react: "window.React", }, }, + transform: { + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": JSON.stringify("production"), + }, + }, }, minify: false, }, @@ -45,9 +52,4 @@ export default defineConfig({ include: ["events"], }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - process: { env: { NODE_ENV: "production" } }, - }, }); diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 4517c4584c..95a5175ae3 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -37,6 +37,13 @@ export default defineConfig({ react: "window.React", }, }, + transform: { + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": JSON.stringify("production"), + }, + }, }, }, plugins: [ @@ -46,9 +53,4 @@ export default defineConfig({ include: ["events"], }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - process: { env: { NODE_ENV: "production" } }, - }, }); diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts index f50e35e54d..0e28cdffd8 100644 --- a/modules/widget-toggles/element-web/vite.config.ts +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -38,6 +38,13 @@ export default defineConfig({ react: "window.React", }, }, + transform: { + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": JSON.stringify("production"), + }, + }, }, }, plugins: [ @@ -48,9 +55,4 @@ export default defineConfig({ include: ["events"], }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - process: { env: { NODE_ENV: "production" } }, - }, }); From f5d93099e370e08ef1267008bdb2301b96bc1f18 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 12:47:25 +0100 Subject: [PATCH 183/211] Re-add externalGlobals --- modules/banner/element-web/package.json | 1 + modules/banner/element-web/vite.config.ts | 5 +++++ modules/opendesk/element-web/package.json | 1 + modules/opendesk/element-web/vite.config.ts | 5 +++++ modules/restricted-guests/element-web/package.json | 1 + modules/restricted-guests/element-web/vite.config.ts | 5 +++++ modules/widget-toggles/element-web/package.json | 1 + modules/widget-toggles/element-web/vite.config.ts | 5 +++++ 8 files changed, 24 insertions(+) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index a2af841d74..c9a36d3b84 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -21,6 +21,7 @@ "matrix-web-i18n": "^3.6.0", "matrix-widget-api": "^1.17.0", "react": "19.2.6", + "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index 5556992672..ab331f6419 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; @@ -54,5 +55,9 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), ], }); diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 70814d9062..c69700b632 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -24,6 +24,7 @@ "@vitejs/plugin-react": "^5.0.0", "react": "19.2.6", "react-dom": "19.2.6", + "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index d55959680e..23a0515ef8 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -51,5 +52,9 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), ], }); diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index b014f82147..70d9b16709 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -18,6 +18,7 @@ "@types/react": "^19", "@vitejs/plugin-react": "^5.0.0", "react": "19.2.6", + "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0" diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 95a5175ae3..048b48335f 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -52,5 +53,9 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), ], }); diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 38e6b87177..9e4f5d8a1d 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -24,6 +24,7 @@ "@vitest/coverage-v8": "^4.0.0", "react": "^19", "typescript": "^6.0.0", + "rollup-plugin-external-globals": "^0.13.0", "vite": "^8.0.0", "vite-plugin-node-polyfills": "^0.28.0", "vite-plugin-svgr": "^5.0.0", diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts index 0e28cdffd8..51770551f9 100644 --- a/modules/widget-toggles/element-web/vite.config.ts +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig, esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; +import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; @@ -54,5 +55,9 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), ], }); From cfab5cc6595a55ff071e4163f6ab32a40eeb4e09 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 13:42:01 +0100 Subject: [PATCH 184/211] Iterate NODE_ENV --- modules/banner/element-web/vite.config.ts | 13 ++++++------- modules/opendesk/element-web/vite.config.ts | 13 ++++++------- .../restricted-guests/element-web/vite.config.ts | 13 ++++++------- modules/widget-toggles/element-web/vite.config.ts | 13 ++++++------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/modules/banner/element-web/vite.config.ts b/modules/banner/element-web/vite.config.ts index ab331f6419..e2ee02eaa2 100644 --- a/modules/banner/element-web/vite.config.ts +++ b/modules/banner/element-web/vite.config.ts @@ -39,13 +39,6 @@ export default defineConfig({ react: "window.React", }, }, - transform: { - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": JSON.stringify("production"), - }, - }, }, }, plugins: [ @@ -60,4 +53,10 @@ export default defineConfig({ react: "window.React", }), ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": "'production'", + "process": { env: { NODE_ENV: "production" } }, + }, }); diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts index 23a0515ef8..17dc4d055e 100644 --- a/modules/opendesk/element-web/vite.config.ts +++ b/modules/opendesk/element-web/vite.config.ts @@ -37,13 +37,6 @@ export default defineConfig({ react: "window.React", }, }, - transform: { - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": JSON.stringify("production"), - }, - }, }, minify: false, }, @@ -57,4 +50,10 @@ export default defineConfig({ react: "window.React", }), ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": "'production'", + "process": { env: { NODE_ENV: "production" } }, + }, }); diff --git a/modules/restricted-guests/element-web/vite.config.ts b/modules/restricted-guests/element-web/vite.config.ts index 048b48335f..9dfdb5dbed 100644 --- a/modules/restricted-guests/element-web/vite.config.ts +++ b/modules/restricted-guests/element-web/vite.config.ts @@ -38,13 +38,6 @@ export default defineConfig({ react: "window.React", }, }, - transform: { - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": JSON.stringify("production"), - }, - }, }, }, plugins: [ @@ -58,4 +51,10 @@ export default defineConfig({ react: "window.React", }), ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": "'production'", + "process": { env: { NODE_ENV: "production" } }, + }, }); diff --git a/modules/widget-toggles/element-web/vite.config.ts b/modules/widget-toggles/element-web/vite.config.ts index 51770551f9..dc8cc37107 100644 --- a/modules/widget-toggles/element-web/vite.config.ts +++ b/modules/widget-toggles/element-web/vite.config.ts @@ -39,13 +39,6 @@ export default defineConfig({ react: "window.React", }, }, - transform: { - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": JSON.stringify("production"), - }, - }, }, }, plugins: [ @@ -60,4 +53,10 @@ export default defineConfig({ react: "window.React", }), ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": "'production'", + "process": { env: { NODE_ENV: "production" } }, + }, }); From ee50bb87df0a03d04a27dea99592198bc1357dfe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:11:34 +0000 Subject: [PATCH 185/211] Update react to v6 --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- modules/widget-toggles/element-web/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index c9a36d3b84..87be4a2ade 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -17,7 +17,7 @@ "@types/node": "^22.10.7", "@types/react": "^19", "@types/react-dom": "^19", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^6.0.0", "matrix-web-i18n": "^3.6.0", "matrix-widget-api": "^1.17.0", "react": "19.2.6", diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index c69700b632..d7b37bb152 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -21,7 +21,7 @@ "@nordeck/element-web-widget-toggles-module": "^0.1.0", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^6.0.0", "react": "19.2.6", "react-dom": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 70d9b16709..d5d6a10828 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -16,7 +16,7 @@ "@element-hq/element-web-module-api": "^1.0.0", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^6.0.0", "react": "19.2.6", "rollup-plugin-external-globals": "^0.13.0", "typescript": "^6.0.0", diff --git a/modules/widget-toggles/element-web/package.json b/modules/widget-toggles/element-web/package.json index 9e4f5d8a1d..7904f8e27d 100644 --- a/modules/widget-toggles/element-web/package.json +++ b/modules/widget-toggles/element-web/package.json @@ -19,7 +19,7 @@ "@testing-library/user-event": "^14.6.1", "@types/node": "^22.10.7", "@types/react": "^19", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^6.0.0", "@vitest/browser-playwright": "^4.0.0", "@vitest/coverage-v8": "^4.0.0", "react": "^19", From 8f846d95a2c089ebee61756541718565c6d9869c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 4 Jun 2026 14:50:03 +0100 Subject: [PATCH 186/211] Update restricted guests screenshots --- .../dialog-linux.png | Bin 12352 -> 11953 bytes .../dialog-login-linux.png | Bin 9971 -> 9591 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png index a2b5cb2be86016be1d0a70ba7169ed55c348870d..2f8c5f8ca3a4de4b6a086e47a68092aefbf9a6da 100644 GIT binary patch literal 11953 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 literal 12352 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 Date: Fri, 5 Jun 2026 14:28:05 +0100 Subject: [PATCH 187/211] Prepare for move to element-web monorepo --- modules/.dockerignore | 1 + modules/.eslintrc.cjs | 79 +++++++++ modules/.gitignore | 158 ++++++++++++++++++ modules/.lintstagedrc | 3 + modules/.prettierignore | 1 + modules/.prettierrc.mjs | 3 + modules/Dockerfile | 24 +++ modules/docker-bake.hcl | 55 ++++++ modules/knip.ts | 30 ++++ modules/package.json | 61 +++++++ ...+vite-plugin-import-css-sheet+1.0.12.patch | 10 ++ ...+element-web-playwright-common+4.0.0.patch | 22 +++ modules/playwright.config.ts | 105 ++++++++++++ modules/playwright/element-web-test.ts | 35 ++++ modules/playwright/tsconfig.json | 13 ++ modules/tsconfig.json | 14 ++ 16 files changed, 614 insertions(+) create mode 100644 modules/.dockerignore create mode 100644 modules/.eslintrc.cjs create mode 100644 modules/.gitignore create mode 100644 modules/.lintstagedrc create mode 100644 modules/.prettierignore create mode 100644 modules/.prettierrc.mjs create mode 100644 modules/Dockerfile create mode 100644 modules/docker-bake.hcl create mode 100644 modules/knip.ts create mode 100644 modules/package.json create mode 100644 modules/patches/@arcmantle+vite-plugin-import-css-sheet+1.0.12.patch create mode 100644 modules/patches/@element-hq+element-web-playwright-common+4.0.0.patch create mode 100644 modules/playwright.config.ts create mode 100644 modules/playwright/element-web-test.ts create mode 100644 modules/playwright/tsconfig.json create mode 100644 modules/tsconfig.json diff --git a/modules/.dockerignore b/modules/.dockerignore new file mode 100644 index 0000000000..f83526d133 --- /dev/null +++ b/modules/.dockerignore @@ -0,0 +1 @@ +**/node_modules/ \ No newline at end of file diff --git a/modules/.eslintrc.cjs b/modules/.eslintrc.cjs new file mode 100644 index 0000000000..67a4bd314d --- /dev/null +++ b/modules/.eslintrc.cjs @@ -0,0 +1,79 @@ +module.exports = { + plugins: ["matrix-org", "eslint-plugin-react-compiler"], + extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react", "plugin:matrix-org/a11y"], + parserOptions: { + project: ["./tsconfig.json"], + }, + env: { + browser: true, + node: true, + }, + rules: { + "react/jsx-key": ["error"], + + "no-restricted-properties": [ + "error", + ...buildRestrictedPropertiesOptions( + ["window.innerHeight", "window.innerWidth", "window.visualViewport"], + "Use UIStore to access window dimensions instead.", + ), + ...buildRestrictedPropertiesOptions( + ["*.mxcUrlToHttp", "*.getHttpUriForMxc"], + "Use Media helper instead to centralise access for customisation.", + ), + ...buildRestrictedPropertiesOptions(["window.setImmediate"], "Use setTimeout instead."), + ], + "no-restricted-globals": [ + "error", + { + name: "setImmediate", + message: "Use setTimeout instead.", + }, + { + name: "Buffer", + message: "Buffer is not available in the web.", + }, + ], + + "import/no-duplicates": ["error"], + "matrix-org/require-copyright-header": "error", + + "react-compiler/react-compiler": "error", + }, + overrides: [ + { + files: [ + "packages/element-web-playwright-common/**/*.ts", + "playwright/**/*.ts", + "modules/*/element-web/e2e/**/*.{ts,tsx}", + ], + rules: { + // This is necessary for Playwright fixtures + "no-empty-pattern": "off", + // This is necessary for Playwright fixtures + "react-hooks/rules-of-hooks": "off", + // This just gets annoying in test code + "@typescript-eslint/explicit-function-return-type": "off", + }, + }, + ], + settings: { + react: { + version: "detect", + }, + }, +}; + +function buildRestrictedPropertiesOptions(properties, message) { + return properties.map((prop) => { + let [object, property] = prop.split("."); + if (object === "*") { + object = undefined; + } + return { + object, + property, + message, + }; + }); +} diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 0000000000..bcaacaaac3 --- /dev/null +++ b/modules/.gitignore @@ -0,0 +1,158 @@ +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Python +.venv +__pycache__/ +.tox/ +.coverage* +coverage.* +*.egg-info/ +_trial_temp/ +.mypy_cache/ + +# Custom rules +# Ignore all built libraries +lib/ +temp/ +coverage/ + +/playwright-html-report/ +**/test-results +**/_results +# Only commit snapshots from Linux +**/e2e/snapshots/**/*.png +**/e2e/snapshots/**/*.yml +!**/e2e/snapshots/**/*-linux.png +!**/e2e/snapshots/**/*-linux.aria.yml +/playwright-report/ +.DS_Store diff --git a/modules/.lintstagedrc b/modules/.lintstagedrc new file mode 100644 index 0000000000..16e652a838 --- /dev/null +++ b/modules/.lintstagedrc @@ -0,0 +1,3 @@ +{ + "*": "prettier -u --write" +} diff --git a/modules/.prettierignore b/modules/.prettierignore new file mode 100644 index 0000000000..4ce36e1e79 --- /dev/null +++ b/modules/.prettierignore @@ -0,0 +1 @@ +*.api.md \ No newline at end of file diff --git a/modules/.prettierrc.mjs b/modules/.prettierrc.mjs new file mode 100644 index 0000000000..eac1f269d1 --- /dev/null +++ b/modules/.prettierrc.mjs @@ -0,0 +1,3 @@ +import config from "eslint-plugin-matrix-org/.prettierrc.js"; + +export default config; diff --git a/modules/Dockerfile b/modules/Dockerfile new file mode 100644 index 0000000000..16ba8480e1 --- /dev/null +++ b/modules/Dockerfile @@ -0,0 +1,24 @@ +ARG ELEMENT_VERSION=latest@sha256:a84f294ce46e4327ebacecb78bfc94cf6a45c7ffa5104a28f06b5ac69d0b2548 + +FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14 AS builder + +ARG BUILD_CONTEXT + +RUN apk add --no-cache jq + +WORKDIR /app +COPY package.json yarn.lock ./ +# Copy the package.json files of all modules & packages to ensure the frozen workspace lockfile holds up +RUN --mount=type=bind,target=/docker-context \ + cd /docker-context/; \ + find . -path ./node_modules -prune -o -name "package.json" -mindepth 0 -maxdepth 4 -exec cp --parents "{}" /app/ \; +RUN yarn install --frozen-lockfile --ignore-scripts +COPY tsconfig.json ./ +COPY ./$BUILD_CONTEXT ./$BUILD_CONTEXT +RUN cd $BUILD_CONTEXT && yarn vite build +RUN mkdir /modules +RUN cp -r ./$BUILD_CONTEXT/lib/ /modules/$(jq -r '"\(.name)-v\(.version)"' ./$BUILD_CONTEXT/package.json) + +FROM ghcr.io/element-hq/element-web:${ELEMENT_VERSION} + +COPY --from=builder /modules /modules/ \ No newline at end of file diff --git a/modules/docker-bake.hcl b/modules/docker-bake.hcl new file mode 100644 index 0000000000..d8af5480cf --- /dev/null +++ b/modules/docker-bake.hcl @@ -0,0 +1,55 @@ +// https://github.com/docker/metadata-action#bake-definition +target "docker-metadata-action" {} + +variable "ELEMENT_VERSION" { + default = "latest" +} + +variable "DATE" { + validation { + condition = DATE != "" + error_message = "DATE must be set" + } + + validation { + condition = DATE == regex("\\d{6}", DATE) + error_message = "The variable 'DATE' must be of format YYMMNN where NN is the build number of the month." + } +} + +variable "BUILD_TAG" { + default = "${ELEMENT_VERSION}-${DATE}" +} + +group "default" { + targets = [ + "element-web-modules-opendesk-plugin" + ] +} + +target "_common" { + inherits = ["docker-metadata-action"] + platforms = [ + "linux/amd64", + "linux/arm64", + ] + context = "." +} + +target "_element_web_module_base" { + inherits = ["_common"] + dockerfile = "./Dockerfile" +} + +target "element-web-modules-opendesk-plugin" { + inherits = ["_element_web_module_base"] + tags = [ + "ghcr.io/element-hq/element-web-modules/opendesk-plugin:latest", + "ghcr.io/element-hq/element-web-modules/opendesk-plugin:${BUILD_TAG}", + "registry.opencode.de/bmi/opendesk/components/supplier/element/images/opendesk-element-web:${BUILD_TAG}" + ] + args = { + ELEMENT_VERSION = "${ELEMENT_VERSION}" + BUILD_CONTEXT = "modules/opendesk/element-web" + } +} \ No newline at end of file diff --git a/modules/knip.ts b/modules/knip.ts new file mode 100644 index 0000000000..2c332cd741 --- /dev/null +++ b/modules/knip.ts @@ -0,0 +1,30 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { KnipConfig } from "knip"; + +// Specify this as knip loads config files which may conditionally add reporters, e.g. `vitest-sonar-reporter` +process.env.GITHUB_ACTIONS = "1"; + +export default { + ignoreDependencies: [ + // Needed for lint:workflows + "@action-validator/cli", + "@action-validator/core", + // Unlisted peer dependency for @element-hq/element-web-module-api + "matrix-widget-api", + ], + ignoreBinaries: [ + // False positive for `yarn list` + "list", + ], + workspaces: { + "modules/*/element-web": { + entry: "src/index.ts{x,}", + }, + }, +} satisfies KnipConfig; diff --git a/modules/package.json b/modules/package.json new file mode 100644 index 0000000000..7353a6543d --- /dev/null +++ b/modules/package.json @@ -0,0 +1,61 @@ +{ + "private": true, + "type": "module", + "workspaces": [ + "packages/*", + "modules/*/element-web", + "modules/*/synapse" + ], + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'", + "lint:prettier": "prettier --check .", + "lint:prettier:fix": "prettier --write .", + "lint:eslint": "eslint --max-warnings 0 modules/*/element-web/src", + "lint:types": "yarn workspaces run lint:types", + "lint:codestyle": "yarn workspaces run lint:codestyle", + "lint:knip": "knip", + "test": "yarn workspaces run test", + "test:playwright": "playwright test", + "test:playwright:open": "yarn test:playwright --ui", + "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot", + "prepare": "patch-package && husky && yarn workspaces run prepare" + }, + "devDependencies": { + "@action-validator/cli": "^0.6.0", + "@action-validator/core": "^0.6.0", + "@element-hq/element-web-module-api": "1.14.0", + "@element-hq/element-web-playwright-common": "^6.0.0", + "@playwright/test": "^1.52.0", + "@stylistic/eslint-plugin": "^5.0.0", + "@types/node": "^22.12.0", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", + "eslint": "^8.26.0", + "eslint-config-google": "^0.14.0", + "eslint-config-prettier": "^10.0.0", + "eslint-plugin-deprecate": "^0.9.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-matrix-org": "^3.0.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-compiler": "^19.0.0-beta-27714ef-20250124", + "eslint-plugin-react-hooks": "^7.0.0", + "eslint-plugin-unicorn": "^56.0.0", + "husky": "^9.1.7", + "knip": "^6.0.0", + "lint-staged": "^17.0.0", + "patch-package": "^8.0.1", + "prettier": "^3.4.2", + "typescript": "^6.0.0" + }, + "resolutions": { + "vite": "8.0.15", + "@vitest/browser-playwright": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "vitest": "4.1.8" + }, + "packageManager": "yarn@1.22.22" +} diff --git a/modules/patches/@arcmantle+vite-plugin-import-css-sheet+1.0.12.patch b/modules/patches/@arcmantle+vite-plugin-import-css-sheet+1.0.12.patch new file mode 100644 index 0000000000..6606c16079 --- /dev/null +++ b/modules/patches/@arcmantle+vite-plugin-import-css-sheet+1.0.12.patch @@ -0,0 +1,10 @@ +diff --git a/node_modules/@arcmantle/vite-plugin-import-css-sheet/client.d.ts b/node_modules/@arcmantle/vite-plugin-import-css-sheet/client.d.ts +index d884a7e..533850e 100644 +--- a/node_modules/@arcmantle/vite-plugin-import-css-sheet/client.d.ts ++++ b/node_modules/@arcmantle/vite-plugin-import-css-sheet/client.d.ts +@@ -1,3 +1,4 @@ + declare module '*.css' { +- export default styles as CSSStyleSheet; ++ const styles: CSSStyleSheet; ++ export default styles; + } diff --git a/modules/patches/@element-hq+element-web-playwright-common+4.0.0.patch b/modules/patches/@element-hq+element-web-playwright-common+4.0.0.patch new file mode 100644 index 0000000000..048ffb753e --- /dev/null +++ b/modules/patches/@element-hq+element-web-playwright-common+4.0.0.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/@element-hq/element-web-playwright-common/playwright-screenshots.sh b/node_modules/@element-hq/element-web-playwright-common/playwright-screenshots.sh +index d860e34..d252f3a 100755 +--- a/node_modules/@element-hq/element-web-playwright-common/playwright-screenshots.sh ++++ b/node_modules/@element-hq/element-web-playwright-common/playwright-screenshots.sh +@@ -16,7 +16,7 @@ function build_image() { + WS_PORT=3000 + + # Check the playwright version +-PW_VERSION=$(pnpm --silent -- playwright --version | awk '{print $2}') ++PW_VERSION=$(yarn --silent run playwright --version | awk '{print $2}') + IMAGE_NAME="ghcr.io/element-hq/element-web/playwright-server:$PW_VERSION" + + # If the image exists in the repository, pull it; otherwise, build it. +@@ -42,7 +42,7 @@ trap clean_up EXIT + + # Wait for playwright-server to be ready + echo "playwright-screenshots: Waiting for playwright-server" +-pnpm --dir "$SCRIPT_DIR" exec wait-on "tcp:$WS_PORT" ++npx -y wait-on "tcp:$WS_PORT" + + # Playwright seems to overwrite the last line from the console, so add an + # extra newline to make sure this doesn't get lost. diff --git a/modules/playwright.config.ts b/modules/playwright.config.ts new file mode 100644 index 0000000000..8dca7c75d8 --- /dev/null +++ b/modules/playwright.config.ts @@ -0,0 +1,105 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig, devices, type Project } from "@playwright/test"; +import fs, { globSync } from "node:fs"; +import path from "node:path"; + +import type { Options } from "./playwright/element-web-test"; + +const chromeProject = { + ...devices["Desktop Chrome"], + channel: "chromium", + permissions: ["clipboard-write", "clipboard-read", "microphone"], + launchOptions: { + args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--mute-audio"], + }, + connectOptions: process.env.PW_TEST_CONNECT_WS_ENDPOINT + ? { + wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT, + exposeNetwork: "", + } + : undefined, +}; + +/** +We assume that all modules will have the following directory structure: + +└── modules/ + └── my-module/ + └── element-web/ + ├── e2e/ + │ ├── test-1.spec.ts + │ └── test-2.spec.ts + └── package.json + +The following code maps each such module (my-module in the example above) to a separate +playwright project. + */ +const projects: Project[] = []; + +// Get all the directories that hold playwright tests +const moduleTestDirectories = globSync("modules/*/element-web/e2e", {}); + +// Process each directory +for (const testDirectory of moduleTestDirectories) { + // Based on the directory structure, the parent directory of the test directory holds package.json. + const moduleDirectory = path.join(testDirectory, ".."); + + // Get module name from package.json + const packageJson = JSON.parse(fs.readFileSync(path.join(moduleDirectory, "package.json"), "utf-8")); + const MODULE_PREFIX = "@element-hq/element-web-module-"; + const name = packageJson.name.startsWith(MODULE_PREFIX) + ? packageJson.name.slice(MODULE_PREFIX.length) + : packageJson.name; + + // Create playwright project + projects.push({ + name, + use: { + ...chromeProject, + moduleDir: moduleDirectory, + }, + testDir: testDirectory, + snapshotDir: `${testDirectory}/snapshots`, + outputDir: `${testDirectory}/_results`, + }); +} + +const baseURL = process.env["BASE_URL"] ?? "http://localhost:8080"; + +export default defineConfig({ + projects, + use: { + viewport: { width: 1280, height: 720 }, + ignoreHTTPSErrors: true, + video: "retain-on-failure", + baseURL, + trace: "on-first-retry", + }, + webServer: { + command: process.env.WEBAPP_PATH + ? `npx serve -p 8080 -L ${process.env.WEBAPP_PATH}` + : "docker run --rm -p 8080:80 ghcr.io/element-hq/element-web:develop", + url: `${baseURL}/config.json`, + reuseExistingServer: true, + timeout: (process.env.CI ? 30 : 120) * 1000, + }, + workers: 1, + retries: process.env.CI ? 2 : 0, + reporter: process.env.CI + ? [ + ["list"], + ["html"], + ["github"], + ["@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js"], + ] + : [["list"], ["html", { outputFolder: "playwright-html-report" }]], + // When running the browser in docker, set the platform to `linux` as that is the platform where the browser is running + snapshotPathTemplate: `{snapshotDir}/{testFilePath}/{arg}-${process.env.PW_TEST_CONNECT_WS_ENDPOINT ? "linux" : "{platform}"}{ext}`, + forbidOnly: !!process.env.CI, +}); diff --git a/modules/playwright/element-web-test.ts b/modules/playwright/element-web-test.ts new file mode 100644 index 0000000000..ca8ca83f7e --- /dev/null +++ b/modules/playwright/element-web-test.ts @@ -0,0 +1,35 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { test as base, expect, type TestFixtures } from "@element-hq/element-web-playwright-common"; + +export interface Options { + moduleDir: string; + modules: string[]; +} + +export const test = base.extend({ + moduleDir: ["", { option: true }], + modules: async ({ moduleDir }, use) => { + await use([`${moduleDir}/lib/index.js`]); + }, + + page: async ({ page, modules, config }, use) => { + config.modules = []; + for (let i = 0; i < modules.length; i++) { + const module = `/modules/module-${i}/index.js`; + await page.route(module, async (route) => { + await route.fulfill({ path: modules[i] }); + }); + config.modules.push(module); + } + + await use(page); + }, +}); + +export { expect }; diff --git a/modules/playwright/tsconfig.json b/modules/playwright/tsconfig.json new file mode 100644 index 0000000000..e8183fcccf --- /dev/null +++ b/modules/playwright/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es2022", + "jsx": "react", + "lib": ["ESNext", "es2022", "dom", "dom.iterable"], + "resolveJsonModule": true, + "esModuleInterop": true, + "moduleResolution": "node", + "module": "es2022", + "allowImportingTsExtensions": true + }, + "include": ["**/*.ts", "../modules/*/element-web/test/**/*.ts"] +} diff --git a/modules/tsconfig.json b/modules/tsconfig.json new file mode 100644 index 0000000000..00a3ce25e3 --- /dev/null +++ b/modules/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "lib": ["dom", "es2022", "esnext"], + "moduleResolution": "bundler", + "esModuleInterop": true, + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "declaration": true + } +} From cb8c90c2953af4e901a6a864e1a7614f9aa8dad7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 5 Jun 2026 14:30:11 +0100 Subject: [PATCH 188/211] Remove opendesk module --- modules/opendesk/README.md | 12 - .../opendesk/element-web/e2e/fixture/app1.png | Bin 1588 -> 0 bytes .../opendesk/element-web/e2e/fixture/app2.png | Bin 13818 -> 0 bytes .../opendesk/element-web/e2e/fixture/logo.svg | 50 ---- .../element-web/e2e/fixture/navigation.json | 44 ---- .../element-web/e2e/fixture/silent-login.html | 14 -- .../element-web/e2e/fixture/widget.html | 52 ----- .../opendesk/element-web/e2e/opendesk.spec.ts | 217 ------------------ ...Opendesk-should-render-top-bar-1-linux.yml | 5 - ...Opendesk-should-render-top-bar-2-linux.yml | 15 -- .../snapshots/opendesk.spec.ts/menu-linux.png | Bin 17900 -> 0 bytes .../opendesk.spec.ts/nav-hover-linux.png | Bin 2288 -> 0 bytes .../snapshots/opendesk.spec.ts/nav-linux.png | Bin 2288 -> 0 bytes .../opendesk.spec.ts/nav-loading-linux.png | Bin 2226 -> 0 bytes modules/opendesk/element-web/package.json | 32 --- modules/opendesk/element-web/src/index.ts | 79 ------- modules/opendesk/element-web/tsconfig.json | 7 - modules/opendesk/element-web/vite.config.ts | 59 ----- 18 files changed, 586 deletions(-) delete mode 100644 modules/opendesk/README.md delete mode 100644 modules/opendesk/element-web/e2e/fixture/app1.png delete mode 100644 modules/opendesk/element-web/e2e/fixture/app2.png delete mode 100644 modules/opendesk/element-web/e2e/fixture/logo.svg delete mode 100644 modules/opendesk/element-web/e2e/fixture/navigation.json delete mode 100644 modules/opendesk/element-web/e2e/fixture/silent-login.html delete mode 100644 modules/opendesk/element-web/e2e/fixture/widget.html delete mode 100644 modules/opendesk/element-web/e2e/opendesk.spec.ts delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png delete mode 100644 modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png delete mode 100644 modules/opendesk/element-web/package.json delete mode 100644 modules/opendesk/element-web/src/index.ts delete mode 100644 modules/opendesk/element-web/tsconfig.json delete mode 100644 modules/opendesk/element-web/vite.config.ts diff --git a/modules/opendesk/README.md b/modules/opendesk/README.md deleted file mode 100644 index d313c219b9..0000000000 --- a/modules/opendesk/README.md +++ /dev/null @@ -1,12 +0,0 @@ -Example module to load all the nordeck opendesk legacy modules & deprecated customisations as a single modern module. - -## Copyright & License - -Copyright (c) 2025 New Vector Ltd - -This software is multi licensed by New Vector Ltd (Element). It can be used either: - -(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR - -(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). -Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/modules/opendesk/element-web/e2e/fixture/app1.png b/modules/opendesk/element-web/e2e/fixture/app1.png deleted file mode 100644 index 2c34416b885b9728da50b3d275652cec6e9d6004..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 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 T^B6||yQ} - - - - - - - - - - - - \ No newline at end of file diff --git a/modules/opendesk/element-web/e2e/fixture/navigation.json b/modules/opendesk/element-web/e2e/fixture/navigation.json deleted file mode 100644 index 79bac98d25..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/navigation.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "categories": [ - { - "identifier": "category1", - "display_name": "Category 1", - "entries": [ - { - "identifier": "app1", - "display_name": "App 1", - "icon_url": "http://localhost:8080/app1.png", - "link": "https://example.com/app1", - "target": "_blank" - }, - { - "identifier": "app2", - "display_name": "App 2", - "icon_url": "http://localhost:8080/app2.png", - "link": "https://example.com/app2", - "target": "_blank" - } - ] - }, - { - "identifier": "category2", - "display_name": "Category 2", - "entries": [ - { - "identifier": "app3", - "display_name": "App 3", - "icon_url": "http://localhost:8080/app1.png", - "link": "https://example.com/app3", - "target": "_blank" - }, - { - "identifier": "app4", - "display_name": "App 4", - "icon_url": "http://localhost:8080/app2.png", - "link": "https://example.com/app4", - "target": "_blank" - } - ] - } - ] -} diff --git a/modules/opendesk/element-web/e2e/fixture/silent-login.html b/modules/opendesk/element-web/e2e/fixture/silent-login.html deleted file mode 100644 index ae80633f60..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/silent-login.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Univention Silent Login - - - - diff --git a/modules/opendesk/element-web/e2e/fixture/widget.html b/modules/opendesk/element-web/e2e/fixture/widget.html deleted file mode 100644 index 3e6a834a73..0000000000 --- a/modules/opendesk/element-web/e2e/fixture/widget.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - Demo Widget - - - -

    Hello unknown!

    - - diff --git a/modules/opendesk/element-web/e2e/opendesk.spec.ts b/modules/opendesk/element-web/e2e/opendesk.spec.ts deleted file mode 100644 index 227f59a3b7..0000000000 --- a/modules/opendesk/element-web/e2e/opendesk.spec.ts +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { rejectToast } from "@element-hq/element-web-playwright-common"; -import { type SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; - -import { test, expect } from "../../../../playwright/element-web-test.ts"; - -declare module "@element-hq/element-web-module-api" { - interface Config { - "net.nordeck.element_web.module.opendesk": { - config: { - banner: { - ics_navigation_json_url: string; - ics_silent_url: string; - portal_logo_svg_url: string; - portal_url: string; - }; - custom_css_variables: { - [key: `--cpd-${string}`]: string; - }; - }; - }; - "net.nordeck.element_web.module.widget_toggles": { - config: { - types: string[]; - }; - }; - "net.nordeck.element_web.module.widget_lifecycle": { - widget_permissions: { - [url: string]: { - preload_approved: boolean; - identity_approved: boolean; - capabilities_approved: string[]; - }; - }; - }; - } -} - -test.use({ - displayName: "Timmy", - synapseConfig: async ({ synapseConfig, _homeserver: homeserver }, use) => { - // Required for widget lifecycle test - (homeserver as SynapseContainer).withConfigField("listeners[0].resources[0].names", ["client", "openid"]); - await use(synapseConfig); - }, - page: async ({ context, page, moduleDir }, use) => { - for (const path of ["logo.svg", "app1.png", "app2.png"]) { - await context.route(`/${path}*`, async (route) => { - await route.fulfill({ path: `${moduleDir}/e2e/fixture/${path}` }); - }); - } - await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { - await route.fulfill({ - path: `${moduleDir}/e2e/fixture/navigation.json`, - contentType: "application/json", - }); - }); - await context.route("http://localhost:8080/ics/silent", async (route) => { - await route.fulfill({ path: `${moduleDir}/e2e/fixture/silent-login.html`, contentType: "text/html" }); - }); - await context.route("http://localhost:8080/widget.html*", async (route) => { - await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" }); - }); - - await page.goto("/"); - await use(page); - }, - config: { - "net.nordeck.element_web.module.opendesk": { - config: { - banner: { - ics_navigation_json_url: "http://localhost:8080/ics/navigation.json", - ics_silent_url: "http://localhost:8080/ics/silent", - portal_logo_svg_url: "http://localhost:8080/logo.svg", - portal_url: "https://example.com/portal", - }, - - custom_css_variables: { - "--cpd-color-text-action-accent": "purple", - }, - }, - }, - "net.nordeck.element_web.module.widget_toggles": { - config: { - types: ["jitsi"], - }, - }, - "net.nordeck.element_web.module.widget_lifecycle": { - widget_permissions: { - "http://localhost:8080/widget.html": { - preload_approved: true, - identity_approved: true, - capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"], - }, - }, - }, - "net.nordeck.element_web.module.guest": { - config: { - guest_user_homeserver_url: "https://matrix.local/", - }, - }, - }, - bypassCSP: true, - launchOptions: { - args: ["--disable-web-security"], - }, -}); - -test.describe("Opendesk", () => { - test("should render top-bar", { tag: ["@screenshot"] }, async ({ page }) => { - await expect(page.getByRole("heading", { name: "Be in your element" })).toBeVisible(); - await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); - - const nav = page.locator("nav"); - await expect(nav).toMatchScreenshot(`nav_loading.png`); - // The stub silent html doesn't seem to work in Playwright so send the postMessage manually - await page.evaluate(() => { - window.postMessage({ - loggedIn: true, - }); - }); - - const trigger = page.getByLabel("Show menu"); - await expect(trigger).toBeVisible(); - - // Assert the banner looks as we expect - await expect(nav).toMatchAriaSnapshot(); - await expect(nav).toMatchScreenshot(`nav.png`); - - // Check hover styles - await trigger.hover(); - await expect(nav).toMatchScreenshot(`nav_hover.png`); - - await test.step("open menu", async () => { - await trigger.click(); - const app1 = page.getByRole("link", { name: "App 1" }); - await expect(app1).toHaveAttribute("href", "https://example.com/app1"); - await app1.hover(); - - // Assert the sidebar looks as we expect - await expect(page.getByTestId("menu-list")).toMatchAriaSnapshot(); - await expect(page).toMatchScreenshot(`menu.png`, { - // We exclude this as we don't want to assert Element's styling, only our own - css: ` - .mx_AuthPage { - opacity: 0; - } - `, - }); - }); - - await test.step("close menu", async () => { - // Assert it closes by clicking the overlay - await page.getByTestId("menu-backdrop").click(); - await expect(page.getByTestId("menu-list")).not.toBeVisible(); - }); - }); - - test("should apply custom_css_variables", async ({ page, user }) => { - await expect(page.getByRole("button", { name: "Send a Direct Message" })).toHaveCSS( - "background-color", - "rgb(128, 0, 128)", - ); // -> purple - }); - - test("should show the widget without any permission requests", async ({ page, user, homeserver }) => { - const bot = await homeserver.registerUser("bot", "password", "Bot"); - const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( - "POST", - "/v3/createRoom", - bot.accessToken, - { - name: "Trusted Widget", - }, - ); - await homeserver.csApi.request<{ - event_id: string; - }>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, bot.accessToken, { - id: "1", - creatorUserId: bot.userId, - type: "custom", - name: "Trusted Widget", - url: `http://localhost:8080/widget.html?hsUrl=${encodeURIComponent(homeserver.baseUrl)}`, - }); - await homeserver.csApi.request( - "PUT", - `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, - bot.accessToken, - { - widgets: { - "1": { - container: "top", - }, - }, - }, - ); - await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { - user_id: user.userId, - }); - - await rejectToast(page, "Verify this device"); - await rejectToast(page, "Notifications"); - - await page.getByText("Trusted Widget").click(); - await page.getByText("Accept").click(); - - await expect( - page.frameLocator(`iframe[title="Trusted Widget"]`).getByRole("heading", { name: `Hello ${user.userId}!` }), - ).toBeVisible(); - }); -}); diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml deleted file mode 100644 index f6b58ee014..0000000000 --- a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml +++ /dev/null @@ -1,5 +0,0 @@ -- navigation: - - link "Show portal": - - img "Portal logo" - - button "Show menu": - - img diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml deleted file mode 100644 index e979e85507..0000000000 --- a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml +++ /dev/null @@ -1,15 +0,0 @@ -- list: - - listitem: - - text: Category 1 - - list: - - listitem: - - link "App 1" - - listitem: - - link "App 2" - - listitem: - - text: Category 2 - - list: - - listitem: - - link "App 3" - - listitem: - - link "App 4" diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/menu-linux.png deleted file mode 100644 index f690835d36c2fbb2a73893973566140e21296c26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17900 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 diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-hover-linux.png deleted file mode 100644 index c1adf808ab74f4723b83c3f4711bb49cb4c57cea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2288 zcmb_eYgm%m7XHjp8BL`cwWm~K8gs0XtSL#&LpeESmRaOzqiH8C^Ac)gc&7}rGNBpM zN>j$lTZ-m2#Y8hNsio!(0hP=ILBKl*h@M@~{5a=%&cAc^kMG;-dG=m=?X}+bUHi71 ztCOzwCT$3Ube#_$Itf7Fx-vnF^xBb%*W+RNVFx&%6`+CQ}~D?w&5KG%7djZ*dE(qQqy|3gxK;|M!J*d!+x#|p>*@|nEZJmfvTi;#dWD9oi@zc z%ex;P1z}vh7}NJ&XiAK=ea5~fG@ z^))p$<(TS#6IE%c?f#n2o<4cg$O(4AdN27&01bH(;u4_WjmUZ8p1*_H{g#OJy z5(~7f{H4=eqEy6!HyuNd>{$~Oq`wH)>}|khJf=}p%8M8;u;EWXxO!MX06)02-f+r1JMvRL zH9mSg1-1|QAIq{`te1^u+Q(dl*`w3(a|Mz;qxoDantp(-`w2|3=AS7B50mv-;;U<1`JBMRl&?hKL^as z&G|$Na^EEB6Q!&GBx#pl>wcxELQCJ&yX;(xRLn85{Jj!4LVr0Ly`yf8w~r4apgY?> zM%wJk>!xD-TJL%?nM~qri&0s;sI0A(g1|(s0ti4i%*|8V*B*9qbabTAXmMU_HoK%= z%J$sp%UDkH@M&-Dhdac_$NTw>*G)6(qUL!FLhi}JIHhzXf_Vl>x}l}tm5JVI>K;KZ z^J^PqGLL83M)Vh);1j*v+4KkSwcQ}83!U++#Cw_T} zk#try+lDYRGi#$DC`>dOoqw`0Jw2U+s}qBC&Gr=2yk7c5cKq^I+X{jj6Lmi!J#B1k zSc(=S-*=szF8O_6YgukxU5sS7HWMA1ZS_wF+bk3cMHZWwm=t-G>LC{v7X0;tviF65 z0(l}xhUK4AkGFQluc~AL*-_i1k6iQd_*J_qtQ1^qG>V^vB!R3*rBdC^)+BgF zW~Ppg&UnjKC-Qzg@91!@Hx|2Lx?|hS26NYDvtgl7c;e}=XNamrqRm@YMZiv}l>i;^N}>fx}Vqsl--#U7fNC!{%@@ zX#ri2qq;PC4VAI-IfX(o%}}C9FXB~-p&I}8KbLBD16VyMJ-D_pj*4Qaq@0QfZf?H? zHqU9*YZ>)oO1OPtEU zzH()5{&DJxu&7izZRQA{n(t|+IwyN^pcpMWq+Hr|MY4XzgE~$qT-CaY@dnK0uvecbh7<30E+$>!KHhA&QjDCqP~}tKlNBd9B?W!X^9& zJ)*2;oNUKX2#b~q!eZ-s)UcL4%x^=^4td3><4pq+nJemus$y*(P>Zmx&vIXrBF1muv8` zqod>aKjc4TuTj0-O7Z77k&RiSqoxk~;uKea3f@|`)d4oo?_T|QL;sWDv{EXbisAi+ z*}Hcy&AWE@pu4;KJny{@vKWNlMmCuhH|t}TXJlL==Bj8&EoTWq{G9*8ajp{oNBQkpGvOFz~k{QE-urx{A?}L$d8~* zlm{xsR4L%cU2~5J0)aU&kYyiZO|rVgu3UP`((A^wM*-0X>V_mOC42$`2b9b%dv`s! zZRD}-Y0ry_iV6#FBhVp3_hXyotQC?H5kGt2Tz?SDvs)`G{z_prwQ ni{Y>`yTSZsG;j%7O|Gb$(0k5nocnPWn2@uh>!A{dvkCtK7QI_^ diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-linux.png deleted file mode 100644 index c1adf808ab74f4723b83c3f4711bb49cb4c57cea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2288 zcmb_eYgm%m7XHjp8BL`cwWm~K8gs0XtSL#&LpeESmRaOzqiH8C^Ac)gc&7}rGNBpM zN>j$lTZ-m2#Y8hNsio!(0hP=ILBKl*h@M@~{5a=%&cAc^kMG;-dG=m=?X}+bUHi71 ztCOzwCT$3Ube#_$Itf7Fx-vnF^xBb%*W+RNVFx&%6`+CQ}~D?w&5KG%7djZ*dE(qQqy|3gxK;|M!J*d!+x#|p>*@|nEZJmfvTi;#dWD9oi@zc z%ex;P1z}vh7}NJ&XiAK=ea5~fG@ z^))p$<(TS#6IE%c?f#n2o<4cg$O(4AdN27&01bH(;u4_WjmUZ8p1*_H{g#OJy z5(~7f{H4=eqEy6!HyuNd>{$~Oq`wH)>}|khJf=}p%8M8;u;EWXxO!MX06)02-f+r1JMvRL zH9mSg1-1|QAIq{`te1^u+Q(dl*`w3(a|Mz;qxoDantp(-`w2|3=AS7B50mv-;;U<1`JBMRl&?hKL^as z&G|$Na^EEB6Q!&GBx#pl>wcxELQCJ&yX;(xRLn85{Jj!4LVr0Ly`yf8w~r4apgY?> zM%wJk>!xD-TJL%?nM~qri&0s;sI0A(g1|(s0ti4i%*|8V*B*9qbabTAXmMU_HoK%= z%J$sp%UDkH@M&-Dhdac_$NTw>*G)6(qUL!FLhi}JIHhzXf_Vl>x}l}tm5JVI>K;KZ z^J^PqGLL83M)Vh);1j*v+4KkSwcQ}83!U++#Cw_T} zk#try+lDYRGi#$DC`>dOoqw`0Jw2U+s}qBC&Gr=2yk7c5cKq^I+X{jj6Lmi!J#B1k zSc(=S-*=szF8O_6YgukxU5sS7HWMA1ZS_wF+bk3cMHZWwm=t-G>LC{v7X0;tviF65 z0(l}xhUK4AkGFQluc~AL*-_i1k6iQd_*J_qtQ1^qG>V^vB!R3*rBdC^)+BgF zW~Ppg&UnjKC-Qzg@91!@Hx|2Lx?|hS26NYDvtgl7c;e}=XNamrqRm@YMZiv}l>i;^N}>fx}Vqsl--#U7fNC!{%@@ zX#ri2qq;PC4VAI-IfX(o%}}C9FXB~-p&I}8KbLBD16VyMJ-D_pj*4Qaq@0QfZf?H? zHqU9*YZ>)oO1OPtEU zzH()5{&DJxu&7izZRQA{n(t|+IwyN^pcpMWq+Hr|MY4XzgE~$qT-CaY@dnK0uvecbh7<30E+$>!KHhA&QjDCqP~}tKlNBd9B?W!X^9& zJ)*2;oNUKX2#b~q!eZ-s)UcL4%x^=^4td3><4pq+nJemus$y*(P>Zmx&vIXrBF1muv8` zqod>aKjc4TuTj0-O7Z77k&RiSqoxk~;uKea3f@|`)d4oo?_T|QL;sWDv{EXbisAi+ z*}Hcy&AWE@pu4;KJny{@vKWNlMmCuhH|t}TXJlL==Bj8&EoTWq{G9*8ajp{oNBQkpGvOFz~k{QE-urx{A?}L$d8~* zlm{xsR4L%cU2~5J0)aU&kYyiZO|rVgu3UP`((A^wM*-0X>V_mOC42$`2b9b%dv`s! zZRD}-Y0ry_iV6#FBhVp3_hXyotQC?H5kGt2Tz?SDvs)`G{z_prwQ ni{Y>`yTSZsG;j%7O|Gb$(0k5nocnPWn2@uh>!A{dvkCtK7QI_^ diff --git a/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png b/modules/opendesk/element-web/e2e/snapshots/opendesk.spec.ts/nav-loading-linux.png deleted file mode 100644 index f79c7ff28bcf87167166f1b0d7c1c3b7d440737f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2226 zcmb_deK^x=AOAU2I~dN@i(Z`8(g`_~bT09PBE399$tsOh5*cQ#OkTE>nn zaQ)5t5Cp-GA3N*|L90Lr9fW-czFiYu7%=JJeUG|96>PI92!cf%KYYk9HgATH4g2^J zu|`%+nkoK$wRN`d{k>hN{b~7zv+NwjvG{G6LvX)cmdZ8$?ti@ev-7E8mu23Q&h742 z2)J(^eqZ^XqPQ2G_xfDh4h=5bFGw|8^AOpEYX2Hh6fuZ-lol^p%8zF)@N%V&LUhLa zD=Xaui1j<-R)T0GtQkx@?m&j1)9wHB;=y^y*2=2n?{N)_MU>mko<^h5uQ~nw{gwF! zpp-cZ^l&7kpsK1$B9WZhG~Kj4sTLHMjf8x^5A5IH2I)dj2JE{%|FLKbc22UZ#*VP; zL)pnD6^)XHYnvDfXceXI>3ZzC83GZ%`{mOk{FU(6*WBbrD zAwcLL`zI$TU9k?Fsf)h8@wQ5-ybw24bk0@YY3{l#Yqe&+mM;lu+BuGo{negWA(b$} zrfzWy3lEQ5;1i6u9?1D;Q8)&3w(Oi8hFd1y){%}Nji))pNIun*w3?Qd7SG*p_EEh@ zZV9PyI6NdIM6Fgc&N7XVa|;U{n#PzJr$ptAQ>Xgyz0QQmKMT`cHT#qI;6A^zpe4wg zlXSsRL1l19f05?^Ge|pxVTRD40WMdYGBIFcm4<0#>R%0IPYJEh?^{Y zpJ^eKAOOhQ2>N>;_xxe#cjgS2&v$-ZM5hZ=tt;*+N>Pjpv17kRoPZAi^akUSuD+6W0#x*x8ySS7ev)eZC6*9`BBO{4V6kQ@hfF8 zM8&yLPPgP&4(!7W*F=&My8-%N%FVXkL7hCAQ4TJ`d;0Y04W~eV|4r!Ct5;9-(waiL zA6cC)e$JVa%jJWD;bDN>kjdN8(Tnp16D6nVlEKPgA)A`+q6`zqhzBbz@?*DC9(&Ub z474P3PO%r+rZu#Gco=>5tSLuItCx3K*GBO-3qO7QsOYipO*WAE}Uc1(``{O-OZenLF-it+O0+`Id0Q7F_1Nt*~m z+ImD`vC`@3)NJf<%?uZfg(Lssa`T;ThL5+WA(Po`HW10+txt70x(|)Q$l9`H%Q%Xs zN1U7NTZ9qbKm2LDBfUvE&Co_a#0%MhH4!`pleO6`cJ_mV57kLAMW?khYohpZOJB2t zGMa!5-Oj5!sIw@Dm!?fQB%cIV(~X9WRd#H(wy}XD17dg~fN|DmD-4nEZgx5dVuLXl zjQ9)+wVqs9SO{8n=IZL|lGM-K-Q9T&4Gqe&RXwDzkEJNUrICbLFW9Bl6nG_WN&Xer zxEJ7|4RkbYZY7w4y$`&&#b%TnowB@l9RX};wqN30bPZXcYyD2hL9UyVEsU?)1m32iVCi1Hd zdl{f34-XG7FE1b|$Am?hEULtBnn{?|jOkU;owVhkGN9D%rsciF*PsNMoT;j^1bC2- z7#X)Hv(KZ^hy+JgZ@P#q#iD5BHcm%sHLMMMt17?X{)s^(|VRaf3?)T&^W>~w*} zi5wGhtsu^t>fK9o)gB;zTXCgn2J`Y8;B|nUD`xd=yy2$EW$8;MiQo>>J}6p-H!Bp1 z%8h1b38P7CcJ0~)#5WQzeS;fsZ=27w@M)*NLM8(bX}=s67A6(E0Sea637wprxMlup zMJXvMs|+?In_M8N6;m8~N4rYeqy<8Ih6`QEo@6G`v9-1JrgJJ;6HhQzGi!IA=&Uhz z%?;wAM$z-9`c2&fFi+n&h)Du0rj z+neXO)-A^oe|5P}d|R0RB_J@6&*%T>c5k<3RCss;FsX4Xj)+@MPV_DD1BMeE9K2iy z?h*lqpk|V13(V&?B^r3Foe!nr#7?|yv_&8g0IL)Z+xa;*gn*{6BS}6<6n%aDBO3i6 zuwJxZYFx7s1V{e$U%imv9dscs#{!Ci5g)G#OM6Tk8XEdcou8X~pa=j7H1I&5+qu>F{x4k-A4SXjDTpPM|_3sOSYC8P^Cla>qUuU0Mz9ry4)T;X={ G&c6YO=sRox diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json deleted file mode 100644 index d7b37bb152..0000000000 --- a/modules/opendesk/element-web/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "nordeck-plugin", - "private": true, - "version": "0.0.0", - "type": "module", - "sideEffects": [ - "**/*.css" - ], - "license": "SEE LICENSE IN README.md", - "scripts": { - "prepare": "vite build", - "lint:types": "tsc --noEmit", - "lint:codestyle": "echo 'handled by lint:eslint'", - "test": "echo no tests yet" - }, - "devDependencies": { - "@element-hq/element-web-module-api": "^1.0.0", - "@nordeck/element-web-guest-module": "^2.0.0", - "@nordeck/element-web-opendesk-module": "^0.5.0", - "@nordeck/element-web-widget-lifecycle-module": "^1.0.1", - "@nordeck/element-web-widget-toggles-module": "^0.1.0", - "@types/node": "^22.10.7", - "@types/react": "^19", - "@vitejs/plugin-react": "^6.0.0", - "react": "19.2.6", - "react-dom": "19.2.6", - "rollup-plugin-external-globals": "^0.13.0", - "typescript": "^6.0.0", - "vite": "^8.0.0", - "vite-plugin-node-polyfills": "^0.28.0" - } -} diff --git a/modules/opendesk/element-web/src/index.ts b/modules/opendesk/element-web/src/index.ts deleted file mode 100644 index e50aac9ce4..0000000000 --- a/modules/opendesk/element-web/src/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import NordeckOpendeskModule from "@nordeck/element-web-opendesk-module"; -import GuestModule, { - assertValidGuestModuleConfig, - GUEST_MODULE_CONFIG_KEY, - GUEST_MODULE_CONFIG_NAMESPACE, - shouldShowComponent as shouldShowComponentShared, -} from "@nordeck/element-web-guest-module"; -import WidgetLifecycleModule from "@nordeck/element-web-widget-lifecycle-module"; -import WidgetTogglesModule from "@nordeck/element-web-widget-toggles-module"; - -import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; - -declare module "@element-hq/element-web-module-api" { - interface Config { - [GUEST_MODULE_CONFIG_NAMESPACE]: { - [GUEST_MODULE_CONFIG_KEY]: { - guest_user_homeserver_url: string; - skip_single_sign_on?: boolean; - guest_user_prefix?: string; - }; - }; - } -} - -declare global { - interface Window { - // XXX: temporary hack until we rewrite everything in modern modules - mxMatrixClientPeg: { - safeGet(): { - getSafeUserId(): string; - }; - }; - } -} - -class OpendeskModule implements Module { - public static readonly moduleApiVersion = "^1.0.0"; - - public constructor(private api: Api) {} - - public async load(): Promise { - const { api } = this; - - api._registerLegacyModule(NordeckOpendeskModule); - api._registerLegacyModule(GuestModule); - api._registerLegacyModule(WidgetLifecycleModule); - api._registerLegacyModule(WidgetTogglesModule); - api._registerLegacyComponentVisibilityCustomisations({ - // XXX: the following is cribbed out of `@nordeck/element-web-guest-module` due to - // it making imports incompatible with workspaces - /** - * Determines whether or not the active MatrixClient user should be able to use - * the given UI component. If shown, the user might still not be able to use the - * component depending on their contextual permissions. For example, invite options - * might be shown to the user but they won't have permission to invite users to - * the current room: the button will appear disabled. - * @param {UIComponent} component The component to check visibility for. - * @returns {boolean} True (default) if the user is able to see the component, false - * otherwise. - */ - shouldShowComponent(component): boolean { - const config = api.config.get(GUEST_MODULE_CONFIG_NAMESPACE)?.[GUEST_MODULE_CONFIG_KEY] ?? {}; - assertValidGuestModuleConfig(config); - - const myUserId = window.mxMatrixClientPeg.safeGet().getSafeUserId(); - return shouldShowComponentShared(config, myUserId, component); - }, - }); - } -} - -export default OpendeskModule satisfies ModuleFactory; diff --git a/modules/opendesk/element-web/tsconfig.json b/modules/opendesk/element-web/tsconfig.json deleted file mode 100644 index 8f4f6b8ace..0000000000 --- a/modules/opendesk/element-web/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "lib" - }, - "include": ["src"] -} diff --git a/modules/opendesk/element-web/vite.config.ts b/modules/opendesk/element-web/vite.config.ts deleted file mode 100644 index 17dc4d055e..0000000000 --- a/modules/opendesk/element-web/vite.config.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { defineConfig, esmExternalRequirePlugin } from "vite"; -import react from "@vitejs/plugin-react"; -import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default defineConfig({ - build: { - lib: { - entry: resolve(__dirname, "src/index.ts"), - name: "element-web-plugin-opendesk-nordeck", - fileName: "index", - formats: ["es"], - }, - outDir: "lib", - target: "esnext", - sourcemap: true, - rolldownOptions: { - plugins: [ - esmExternalRequirePlugin({ - external: ["react"], - }), - ], - output: { - globals: { - // Reuse React from the host app - react: "window.React", - }, - }, - }, - minify: false, - }, - plugins: [ - react(), - nodePolyfills({ - include: ["events"], - }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), - ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": "'production'", - "process": { env: { NODE_ENV: "production" } }, - }, -}); From 5d5c59473bfb42846c06ab7f0b1dfa5832a790ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 10:32:55 +0100 Subject: [PATCH 189/211] Update docs --- apps/web/README.md | 2 +- docs/config.md | 2 +- docs/deprecated-modules.md | 2 +- docs/install.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/README.md b/apps/web/README.md index c0683d7017..c4b84a1268 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -126,5 +126,5 @@ Element Web supports a module system that allows you to extend or modify functio Modules are extensions that can add or modify Element Web's functionality. They are: -- Built using the [`@element-hq/element-web-module-api`](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api) +- Built using the [`@element-hq/element-web-module-api`](https://github.com/element-hq/element-web/tree/develop/packages/module-api) - Loaded in EW via [config.json](../../docs/config.md#modules) diff --git a/docs/config.md b/docs/config.md index 046a737447..98209c1f8d 100644 --- a/docs/config.md +++ b/docs/config.md @@ -596,7 +596,7 @@ Currently, the following UI feature flags are supported: } ``` -Each module URL is loaded using dynamic import (`import()`). The modules are loaded in order after Element Web initializes but before the application fully starts. Modules must be accessible from the browser and should export a compatible module format that works with the [Module API](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api). +Each module URL is loaded using dynamic import (`import()`). The modules are loaded in order after Element Web initializes but before the application fully starts. Modules must be accessible from the browser and should export a compatible module format that works with the [Module API](https://github.com/element-hq/element-web/tree/develop/packages/module-api). ## Undocumented / developer options diff --git a/docs/deprecated-modules.md b/docs/deprecated-modules.md index 1391d8883e..9b95ebd5fd 100644 --- a/docs/deprecated-modules.md +++ b/docs/deprecated-modules.md @@ -1,7 +1,7 @@ # Deprecated Module system > [!CAUTION] -> DEPRECATED. Use [Element web module api](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api) instead. +> DEPRECATED. Use [Element web module api](https://github.com/element-hq/element-web/tree/develop/packages/module-api) instead. The module system in Element Web is a way to add or modify functionality of Element Web itself, bundled at compile time for the app. This means that modules are loaded as part of the `pnpm build` process but have an effect on user experience diff --git a/docs/install.md b/docs/install.md index 1829bf0e4a..dcf275f329 100644 --- a/docs/install.md +++ b/docs/install.md @@ -66,7 +66,7 @@ on other runtimes may require root privileges. To resolve this, either run the image as root (`docker run --user 0`) or, better, change the port that nginx listens on via the `ELEMENT_WEB_PORT` environment variable. -[Element Web Modules](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api) can be dynamically loaded +[Element Web Modules](https://github.com/element-hq/element-web/tree/develop/packages/module-api) can be dynamically loaded by being made available (e.g. via bind mount) in a directory within `/modules/`. The default entrypoint will be index.js in that directory but can be overridden if a package.json file is found with a `main` directive. These modules will be presented in a `/modules` subdirectory within the webroot, and automatically added to the config.json `modules` field. From 4825a5a4a4fc15860d3f76d8ae5252284eb61890 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 10:33:09 +0100 Subject: [PATCH 190/211] Tidy deps --- apps/desktop/package.json | 6 +++--- packages/module-api/package.json | 8 ++++---- packages/playwright-common/package.json | 2 +- packages/shared-components/package.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 6cad429e36..89637224b2 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -31,7 +31,7 @@ "start": "nx start", "lint": "pnpm lint:types && pnpm lint:js", "lint:js": "eslint --max-warnings 0 src hak playwright scripts", - "lint:js-fix": "eslint --fix --max-warnings 0 src hak playwright scripts && prettier --log-level=warn --write .", + "lint:js-fix": "eslint --fix --max-warnings 0 src hak playwright scripts", "lint:types": "pnpm lint:types:src && pnpm lint:types:node && pnpm lint:types:test && pnpm lint:types:scripts && pnpm lint:types:hak", "lint:types:src": "tsc --noEmit", "lint:types:node": "tsc --noEmit -p tsconfig.node.json", @@ -80,7 +80,7 @@ "@types/auto-launch": "^5.0.1", "@types/counterpart": "^0.18.1", "@types/minimist": "^1.2.1", - "@types/node": "18.19.130", + "@types/node": "22", "@types/pacote": "^11.1.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", @@ -107,7 +107,7 @@ "prettier": "^3.0.0", "rimraf": "^6.0.0", "tar": "^7.5.8", - "typescript": "6.0.3", + "typescript": "catalog:", "vitest": "catalog:" }, "hakDependencies": { diff --git a/packages/module-api/package.json b/packages/module-api/package.json index 102512cb7d..925f03e2b7 100644 --- a/packages/module-api/package.json +++ b/packages/module-api/package.json @@ -36,15 +36,15 @@ "@matrix-org/react-sdk-module-api": "^2.5.0", "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", - "@types/react": "^19", - "@types/react-dom": "^19.0.4", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "catalog:", "matrix-widget-api": "^1.17.0", "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", - "typescript": "^6.0.0", - "unplugin-dts": "1.0.1", + "typescript": "catalog:", + "unplugin-dts": "catalog:", "vite": "catalog:", "vitest": "catalog:" }, diff --git a/packages/playwright-common/package.json b/packages/playwright-common/package.json index e81a39bb31..f452ab0ca9 100644 --- a/packages/playwright-common/package.json +++ b/packages/playwright-common/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@element-hq/element-web-module-api": "workspace:*", "@types/lodash-es": "^4.17.12", - "typescript": "^6.0.0" + "typescript": "catalog:" }, "dependencies": { "@axe-core/playwright": "^4.10.1", diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index a67845feae..89430eeba6 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -131,7 +131,7 @@ "typedoc-plugin-markdown": "^4.9.0", "typedoc-plugin-missing-exports": "^4.1.2", "typescript": "catalog:", - "unplugin-dts": "1.0.1", + "unplugin-dts": "catalog:", "vite": "catalog:", "vite-plugin-node-polyfills": "^0.28.0", "vitest": "catalog:" From 2fc6eb717d9489f7e9f3e9475ab62339cafcace1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 10:33:19 +0100 Subject: [PATCH 191/211] Generate lockfile --- pnpm-lock.yaml | 738 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 541 insertions(+), 197 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deed59212a..6be5199799 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,12 @@ catalogs: '@vector-im/compound-web': specifier: 9.4.1 version: 9.4.1 + '@vitejs/plugin-react': + specifier: 6.0.2 + version: 6.0.2 + '@vitest/browser-playwright': + specifier: 4.1.7 + version: 4.1.7 '@vitest/coverage-v8': specifier: 4.1.7 version: 4.1.7 @@ -235,9 +241,18 @@ catalogs: typescript: specifier: 6.0.3 version: 6.0.3 + unplugin-dts: + specifier: 1.0.1 + version: 1.0.1 vite: specifier: 8.0.15 version: 8.0.15 + vite-plugin-node-polyfills: + specifier: 0.28.0 + version: 0.28.0 + vite-plugin-svgr: + specifier: 5.2.0 + version: 5.2.0 vitest: specifier: 4.1.7 version: 4.1.7 @@ -251,7 +266,7 @@ overrides: caniuse-lite: 1.0.30001793 markdown-it: 14.1.1 matrix-widget-api: ^1.17.0 - '@types/node': 18.19.130 + '@types/node': 22.13.14 config-file-ts: 0.2.8-rc1 node-abi: 4.31.0 '@types/pg-pool': 2.0.7 @@ -270,6 +285,7 @@ overrides: packageExtensionsChecksum: sha256-EMEi1vcyzQthk7O/0AcntvnHgJaKCoFBlzp6iX/qNYk= patchedDependencies: + '@arcmantle/vite-plugin-import-css-sheet': 8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747 '@dnd-kit/abstract': a4ddfb7b2d2d0b52c6709cead2e0feef065f2a17a516496679813344f326f9c1 '@matrix-org/react-sdk-module-api': 016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96 '@types/auto-launch': b60dc9846a11a1684ce52c55493f29ea398d3dc44b88f66ab44e9843d6c01538 @@ -293,6 +309,9 @@ importers: '@action-validator/core': specifier: ^0.6.0 version: 0.6.0 + '@element-hq/element-web-playwright-common': + specifier: workspace:* + version: link:packages/playwright-common '@nx-tools/nx-container': specifier: ^7.2.1 version: 7.2.3(@nx/devkit@22.6.5(nx@22.7.5))(@nx/js@22.6.5(@babel/traverse@7.29.7)(nx@22.7.5))(dotenv@17.4.2)(nx@22.7.5)(tslib@2.8.1) @@ -300,14 +319,14 @@ importers: specifier: 'catalog:' version: 1.60.0 '@types/node': - specifier: 18.19.130 - version: 18.19.130 + specifier: 22.13.14 + version: 22.13.14 cronstrue: specifier: ^3.0.0 version: 3.14.0 eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(24da5b5c4d393c3b1f692cbfbfe73aed) + version: 3.0.0(dd8c44dac078085b4cf5561606e117de) husky: specifier: ^9.0.0 version: 9.1.7 @@ -337,10 +356,10 @@ importers: version: 6.0.3 vitepress: specifier: ^1.6.4 - version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3) + version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@22.13.14)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3) vitepress-plugin-mermaid: specifier: ^2.0.17 - version: 2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3)) + version: 2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@22.13.14)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3)) yaml: specifier: 2.8.4 version: 2.8.4 @@ -406,8 +425,8 @@ importers: specifier: ^1.2.1 version: 1.2.5 '@types/node': - specifier: 18.19.130 - version: 18.19.130 + specifier: 22.13.14 + version: 22.13.14 '@types/pacote': specifier: ^11.1.1 version: 11.1.8 @@ -455,7 +474,7 @@ importers: version: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(24da5b5c4d393c3b1f692cbfbfe73aed) + version: 3.0.0(dd8c44dac078085b4cf5561606e117de) eslint-plugin-n: specifier: ^17.12.0 version: 17.24.0(eslint@8.57.1)(typescript@6.0.3) @@ -487,11 +506,11 @@ importers: specifier: ^7.5.8 version: 7.5.15 typescript: - specifier: 6.0.3 + specifier: 'catalog:' version: 6.0.3 vitest: specifier: 'catalog:' - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) apps/web: dependencies: @@ -632,7 +651,7 @@ importers: version: 1.0.3 matrix-js-sdk: specifier: github:matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/76aee8e682514931bc620b2b289eabe7a8e67118 + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f5eb1e5b74b36d0f43cddc048f7867819e2dfd70 matrix-widget-api: specifier: ^1.17.0 version: 1.17.0 @@ -762,7 +781,7 @@ importers: version: link:../../packages/playwright-common '@fetch-mock/jest': specifier: ^0.2.20 - version: 0.2.20(@jest/globals@30.4.1)(jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0)) + version: 0.2.20(@jest/globals@30.4.1)(jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0)) '@jest/globals': specifier: ^30.2.0 version: 30.4.1 @@ -833,8 +852,8 @@ importers: specifier: ^3.5.3 version: 3.5.6 '@types/node': - specifier: 18.19.130 - version: 18.19.130 + specifier: 22.13.14 + version: 22.13.14 '@types/pako': specifier: ^2.0.0 version: 2.0.4 @@ -918,13 +937,13 @@ importers: version: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) eslint-plugin-jest: specifier: ^29.0.0 - version: 29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) + version: 29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0))(typescript@6.0.3) eslint-plugin-jsx-a11y: specifier: ^6.5.1 version: 6.10.2(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(24da5b5c4d393c3b1f692cbfbfe73aed) + version: 3.0.0(dd8c44dac078085b4cf5561606e117de) eslint-plugin-react: specifier: ^7.28.0 version: 7.37.5(eslint@8.57.1) @@ -954,7 +973,7 @@ importers: version: 3.0.0 jest: specifier: ^30.0.0 - version: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + version: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) jest-canvas-mock: specifier: ^2.5.2 version: 2.5.2 @@ -1079,6 +1098,211 @@ importers: specifier: 2.8.4 version: 2.8.4 + modules/banner: + dependencies: + '@radix-ui/react-dialog': + specifier: ^1.1.6 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@vector-im/compound-design-tokens': + specifier: ^10.0.0 + version: 10.2.1(@types/react@19.2.14)(react@19.2.6) + '@vector-im/compound-web': + specifier: ^9.0.0 + version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + framer-motion: + specifier: ^12.4.10 + version: 12.40.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + styled-components: + specifier: ^6.1.18 + version: 6.4.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + zod: + specifier: ^4.0.0 + version: 4.3.6 + devDependencies: + '@arcmantle/vite-plugin-import-css-sheet': + specifier: ^1.0.12 + version: 1.0.12(patch_hash=8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747) + '@element-hq/element-web-module-api': + specifier: workspace:* + version: link:../../packages/module-api + '@types/node': + specifier: 22.13.14 + version: 22.13.14 + '@types/react': + specifier: ^19.2.10 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 6.0.2(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + matrix-web-i18n: + specifier: ^3.6.0 + version: 3.6.0 + matrix-widget-api: + specifier: ^1.17.0 + version: 1.17.0 + react: + specifier: 'catalog:' + version: 19.2.6 + rollup-plugin-external-globals: + specifier: ^0.13.0 + version: 0.13.0(rollup@4.60.1) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite-plugin-node-polyfills: + specifier: 'catalog:' + version: 0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vite-plugin-svgr: + specifier: 'catalog:' + version: 5.2.0(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + + modules/restricted-guests: + dependencies: + '@vector-im/compound-design-tokens': + specifier: ^10.0.0 + version: 10.2.1(@types/react@19.2.14)(react@19.2.6) + '@vector-im/compound-web': + specifier: ^9.0.0 + version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + styled-components: + specifier: ^6.1.18 + version: 6.4.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + zod: + specifier: ^4.0.0 + version: 4.3.6 + devDependencies: + '@arcmantle/vite-plugin-import-css-sheet': + specifier: ^1.0.12 + version: 1.0.12(patch_hash=8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747) + '@element-hq/element-web-module-api': + specifier: workspace:* + version: link:../../packages/module-api + '@types/node': + specifier: 22.13.14 + version: 22.13.14 + '@types/react': + specifier: ^19.2.10 + version: 19.2.14 + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 6.0.2(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + react: + specifier: 'catalog:' + version: 19.2.6 + rollup-plugin-external-globals: + specifier: ^0.13.0 + version: 0.13.0(rollup@4.60.1) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite-plugin-node-polyfills: + specifier: 'catalog:' + version: 0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + + modules/widget-lifecycle: + dependencies: + zod: + specifier: ^4.0.0 + version: 4.3.6 + devDependencies: + '@element-hq/element-web-module-api': + specifier: workspace:* + version: link:../../packages/module-api + '@types/node': + specifier: 22.13.14 + version: 22.13.14 + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vitest: + specifier: 'catalog:' + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + + modules/widget-toggles: + dependencies: + '@vector-im/compound-design-tokens': + specifier: ^10.0.0 + version: 10.2.1(@types/react@19.2.14)(react@19.2.6) + '@vector-im/compound-web': + specifier: ^9.0.0 + version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + matrix-widget-api: + specifier: ^1.17.0 + version: 1.17.0 + styled-components: + specifier: ^6.3.11 + version: 6.4.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + zod: + specifier: ^4.3.6 + version: 4.3.6 + devDependencies: + '@arcmantle/vite-plugin-import-css-sheet': + specifier: ^1.0.12 + version: 1.0.12(patch_hash=8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747) + '@element-hq/element-web-module-api': + specifier: workspace:* + version: link:../../packages/module-api + '@testing-library/dom': + specifier: ^10.4.1 + version: 10.4.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) + '@types/node': + specifier: 22.13.14 + version: 22.13.14 + '@types/react': + specifier: ^19.2.10 + version: 19.2.14 + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 6.0.2(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + '@vitest/browser-playwright': + specifier: 'catalog:' + version: 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) + react: + specifier: 'catalog:' + version: 19.2.6 + rollup-plugin-external-globals: + specifier: ^0.13.0 + version: 0.13.0(rollup@4.60.1) + typescript: + specifier: 'catalog:' + version: 6.0.3 + vite: + specifier: 'catalog:' + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite-plugin-node-polyfills: + specifier: 'catalog:' + version: 0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vite-plugin-svgr: + specifier: 'catalog:' + version: 5.2.0(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: + specifier: 'catalog:' + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + packages/module-api: dependencies: matrix-web-i18n: @@ -1096,10 +1320,10 @@ importers: version: 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.6) '@microsoft/api-extractor': specifier: ^7.49.1 - version: 7.58.7(@types/node@18.19.130) + version: 7.58.7(@types/node@22.13.14) '@types/node': - specifier: 18.19.130 - version: 18.19.130 + specifier: 22.13.14 + version: 22.13.14 '@types/react': specifier: ^19.2.10 version: 19.2.14 @@ -1122,17 +1346,17 @@ importers: specifier: ^7.6.3 version: 7.8.1 typescript: - specifier: ^6.0.0 + specifier: 'catalog:' version: 6.0.3 unplugin-dts: - specifier: 1.0.1 - version: 1.0.1(@microsoft/api-extractor@7.58.7(@types/node@18.19.130))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + specifier: 'catalog:' + version: 1.0.1(@microsoft/api-extractor@7.58.7(@types/node@22.13.14))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) vite: specifier: 'catalog:' - version: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) vitest: specifier: 'catalog:' - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) packages/playwright-common: dependencies: @@ -1177,7 +1401,7 @@ importers: specifier: ^4.17.12 version: 4.17.12 typescript: - specifier: ^6.0.0 + specifier: 'catalog:' version: 6.0.3 packages/shared-components: @@ -1266,7 +1490,7 @@ importers: version: 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.6) '@microsoft/api-extractor': specifier: ^7.56.0 - version: 7.58.7(@types/node@18.19.130) + version: 7.58.7(@types/node@22.13.14) '@playwright/test': specifier: 'catalog:' version: 1.60.0 @@ -1275,10 +1499,10 @@ importers: version: 10.4.0(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@storybook/addon-designs': specifier: ^11.0.1 - version: 11.1.3(@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + version: 11.1.3(@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@storybook/addon-docs': specifier: ^10.0.7 - version: 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + version: 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) '@storybook/addon-vitest': specifier: ^10.1.11 version: 10.4.0(@vitest/browser-playwright@4.1.7)(@vitest/browser@4.1.7)(@vitest/runner@4.1.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vitest@4.1.7) @@ -1287,7 +1511,7 @@ importers: version: 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@storybook/react-vite': specifier: ^10.0.7 - version: 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + version: 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) '@stylistic/eslint-plugin': specifier: ^5.7.0 version: 5.10.0(eslint@8.57.1) @@ -1323,7 +1547,7 @@ importers: version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@vitest/browser-playwright': specifier: ^4.0.17 - version: 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + version: 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) @@ -1347,7 +1571,7 @@ importers: version: 6.10.2(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(24da5b5c4d393c3b1f692cbfbfe73aed) + version: 3.0.0(dd8c44dac078085b4cf5561606e117de) eslint-plugin-react: specifier: ^7.37.5 version: 7.37.5(eslint@8.57.1) @@ -1385,17 +1609,17 @@ importers: specifier: 'catalog:' version: 6.0.3 unplugin-dts: - specifier: 1.0.1 - version: 1.0.1(@microsoft/api-extractor@7.58.7(@types/node@18.19.130))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + specifier: 'catalog:' + version: 1.0.1(@microsoft/api-extractor@7.58.7(@types/node@22.13.14))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) vite: specifier: 'catalog:' - version: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + version: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) vite-plugin-node-polyfills: specifier: ^0.28.0 - version: 0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + version: 0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) vitest: specifier: 'catalog:' - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) packages/vite-common: dependencies: @@ -1404,7 +1628,7 @@ importers: version: 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) vitest: specifier: 'catalog:' - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) devDependencies: typescript: specifier: 'catalog:' @@ -1515,6 +1739,10 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@arcmantle/vite-plugin-import-css-sheet@1.0.12': + resolution: {integrity: sha512-hLE/4NSBowlWl4soIxjVTtEdAxGWL4wmFHgBq9Y8M4588plq8J23Kpk3QVsxV4Vl3peuZIIq641euSZpNZIXLw==} + engines: {node: '>=22'} + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -2728,6 +2956,12 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + '@esbuild/aix-ppc64@0.27.4': resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} @@ -5036,7 +5270,7 @@ packages: '@rushstack/node-core-library@5.23.1': resolution: {integrity: sha512-wlKmIKIYCKuCASbITvOxLZXepPbwXvrv7S6ig6XNWFchSyhL/E2txmVXspHY49Wu2dzf7nI27a2k/yV5BA3EiA==} peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 peerDependenciesMeta: '@types/node': optional: true @@ -5044,7 +5278,7 @@ packages: '@rushstack/problem-matcher@0.2.1': resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 peerDependenciesMeta: '@types/node': optional: true @@ -5055,7 +5289,7 @@ packages: '@rushstack/terminal@0.24.0': resolution: {integrity: sha512-8ZQS4MMaGsv27EXCBiH7WMPkRZrffeDoIevs6z9TM5dzqiY6+Hn4evfK/G+gvgBTjfvfkHIZPQQmalmI2sM4TQ==} peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 peerDependenciesMeta: '@types/node': optional: true @@ -5839,8 +6073,8 @@ packages: '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - '@types/node@18.19.130': - resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + '@types/node@22.13.14': + resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -6217,6 +6451,19 @@ packages: bundledDependencies: - '@vector-im/matrix-wysiwyg-wasm' + '@vitejs/plugin-react@6.0.2': + resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + '@vitejs/plugin-vue@5.2.4': resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -8798,6 +9045,20 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + framer-motion@12.40.0: + resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -9672,7 +9933,7 @@ packages: resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 esbuild-register: '>=3.4.0' ts-node: '>=9.0.0' peerDependenciesMeta: @@ -10258,8 +10519,8 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/76aee8e682514931bc620b2b289eabe7a8e67118: - resolution: {gitHosted: true, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/76aee8e682514931bc620b2b289eabe7a8e67118} + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f5eb1e5b74b36d0f43cddc048f7867819e2dfd70: + resolution: {gitHosted: true, integrity: sha512-WLU8ayawaMa409vix6jPMaM7UVKqZzFEFQrm/UYUuboMEfA1UZVyFVF71qkOuBN4FPnsL76UAXaSnJmw958I3w==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f5eb1e5b74b36d0f43cddc048f7867819e2dfd70} version: 41.6.0 engines: {node: '>=22.0.0'} @@ -10529,6 +10790,12 @@ packages: moo-color@1.0.3: resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} + motion-dom@12.40.0: + resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} + + motion-utils@12.39.0: + resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -12567,6 +12834,22 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + styled-components@6.4.2: + resolution: {integrity: sha512-xZBhBJsMtGqb+aKcwKgaT+BtuFums9VynX2JRvXJGTx5UfZzN12rk5r4nVdhXYvRw+hE7yiYxVrOqJZaK2+Txg==} + engines: {node: '>= 16'} + peerDependencies: + css-to-react-native: '>= 3.2.0' + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-native: '>= 0.68.0' + peerDependenciesMeta: + css-to-react-native: + optional: true + react-dom: + optional: true + react-native: + optional: true + stylehacks@7.0.7: resolution: {integrity: sha512-bJkD0JkEtbRrMFtwgpJyBbFIwfDDONQ1Ov3sDLZQP8HuJ73kBOyx66H4bOcAbVWmnfLdvQ0AJwXxOMkpujcO6g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} @@ -12602,6 +12885,9 @@ packages: engines: {node: '>=20.19.0'} hasBin: true + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + stylis@4.4.0: resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} @@ -13066,8 +13352,8 @@ packages: underscore@1.13.8: resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} @@ -13297,12 +13583,17 @@ packages: peerDependencies: vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vite-plugin-svgr@5.2.0: + resolution: {integrity: sha512-qj2eAKF8C6PZWemVTvQA0xgQIcP1hHU6Buh7fl6BhvayWwnuxE+z417miKxeDvRWbDrupQ1oK99hfxElopJ3sQ==} + peerDependencies: + vite: '>=3.0.0' + vite@5.4.21: resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 less: '*' lightningcss: ^1.21.0 sass: '*' @@ -13333,7 +13624,7 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@vitejs/devtools': ^0.1.18 esbuild: 0.27.4 jiti: '>=1.21.0' @@ -13409,7 +13700,7 @@ packages: peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@vitest/browser-playwright': 4.1.7 '@vitest/browser-preview': 4.1.7 '@vitest/browser-webdriverio': 4.1.7 @@ -13956,6 +14247,10 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.2.2 + '@arcmantle/vite-plugin-import-css-sheet@1.0.12(patch_hash=8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747)': + dependencies: + lightningcss: 1.32.0 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -15442,6 +15737,12 @@ snapshots: dependencies: tslib: 2.8.1 + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + '@esbuild/aix-ppc64@0.27.4': optional: true @@ -15553,16 +15854,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@fetch-mock/jest@0.2.20(@jest/globals@30.4.1)(jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0))': + '@fetch-mock/jest@0.2.20(@jest/globals@30.4.1)(jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0))': dependencies: '@jest/globals': 30.4.1 fetch-mock: 12.6.0 - jest: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + jest: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) '@fetch-mock/vitest@0.2.18(vitest@4.1.7)': dependencies: fetch-mock: 12.6.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) '@figspec/components@2.1.0': {} @@ -15710,7 +16011,7 @@ snapshots: '@jest/console@30.4.1': dependencies: '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 jest-message-util: 30.4.1 jest-util: 30.4.1 @@ -15724,7 +16025,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.4.0 @@ -15732,7 +16033,7 @@ snapshots: fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 jest-changed-files: 30.4.1 - jest-config: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + jest-config: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) jest-haste-map: 30.4.1 jest-message-util: 30.4.1 jest-regex-util: 30.4.0 @@ -15762,7 +16063,7 @@ snapshots: '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 '@types/jsdom': 21.1.7 - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-mock: 30.4.1 jest-util: 30.4.1 jsdom: 26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f) @@ -15771,7 +16072,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-mock: 30.4.1 '@jest/expect-utils@30.4.1': @@ -15789,7 +16090,7 @@ snapshots: dependencies: '@jest/types': 30.4.1 '@sinonjs/fake-timers': 15.4.0 - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-message-util: 30.4.1 jest-mock: 30.4.1 jest-util: 30.4.1 @@ -15807,7 +16108,7 @@ snapshots: '@jest/pattern@30.4.0': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-regex-util: 30.4.0 '@jest/reporters@30.4.1': @@ -15818,7 +16119,7 @@ snapshots: '@jest/transform': 30.4.1 '@jest/types': 30.4.1 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit-x: 0.2.2 @@ -15894,15 +16195,15 @@ snapshots: '@jest/schemas': 30.4.1 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/yargs': 17.0.35 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(typescript@6.0.3) - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) optionalDependencies: typescript: 6.0.3 @@ -16180,23 +16481,23 @@ snapshots: dependencies: '@chevrotain/types': 11.1.2 - '@microsoft/api-extractor-model@7.33.8(@types/node@18.19.130)': + '@microsoft/api-extractor-model@7.33.8(@types/node@22.13.14)': dependencies: '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.1 - '@rushstack/node-core-library': 5.23.1(@types/node@18.19.130) + '@rushstack/node-core-library': 5.23.1(@types/node@22.13.14) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.58.7(@types/node@18.19.130)': + '@microsoft/api-extractor@7.58.7(@types/node@22.13.14)': dependencies: - '@microsoft/api-extractor-model': 7.33.8(@types/node@18.19.130) + '@microsoft/api-extractor-model': 7.33.8(@types/node@22.13.14) '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.1 - '@rushstack/node-core-library': 5.23.1(@types/node@18.19.130) + '@rushstack/node-core-library': 5.23.1(@types/node@22.13.14) '@rushstack/rig-package': 0.7.3 - '@rushstack/terminal': 0.24.0(@types/node@18.19.130) - '@rushstack/ts-command-line': 5.3.9(@types/node@18.19.130) + '@rushstack/terminal': 0.24.0(@types/node@22.13.14) + '@rushstack/ts-command-line': 5.3.9(@types/node@22.13.14) diff: 8.0.4 minimatch: 10.2.3 resolve: 1.22.12 @@ -17708,7 +18009,7 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/node-core-library@5.23.1(@types/node@18.19.130)': + '@rushstack/node-core-library@5.23.1(@types/node@22.13.14)': dependencies: ajv: 8.18.0 ajv-draft-04: 1.0.0(ajv@8.18.0) @@ -17719,28 +18020,28 @@ snapshots: resolve: 1.22.12 semver: 7.7.4 optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 - '@rushstack/problem-matcher@0.2.1(@types/node@18.19.130)': + '@rushstack/problem-matcher@0.2.1(@types/node@22.13.14)': optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@rushstack/rig-package@0.7.3': dependencies: jju: 1.4.0 resolve: 1.22.12 - '@rushstack/terminal@0.24.0(@types/node@18.19.130)': + '@rushstack/terminal@0.24.0(@types/node@22.13.14)': dependencies: - '@rushstack/node-core-library': 5.23.1(@types/node@18.19.130) - '@rushstack/problem-matcher': 0.2.1(@types/node@18.19.130) + '@rushstack/node-core-library': 5.23.1(@types/node@22.13.14) + '@rushstack/problem-matcher': 0.2.1(@types/node@22.13.14) supports-color: 8.1.1 optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 - '@rushstack/ts-command-line@5.3.9(@types/node@18.19.130)': + '@rushstack/ts-command-line@5.3.9(@types/node@22.13.14)': dependencies: - '@rushstack/terminal': 0.24.0(@types/node@18.19.130) + '@rushstack/terminal': 0.24.0(@types/node@22.13.14) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -18048,21 +18349,21 @@ snapshots: axe-core: 4.11.4 storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@storybook/addon-designs@11.1.3(@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@storybook/addon-designs@11.1.3(@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: '@figspec/react': 2.0.1(@types/react@19.2.14)(react@19.2.6) storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) optionalDependencies: - '@storybook/addon-docs': 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + '@storybook/addon-docs': 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) react: 19.2.6 react-dom: 19.2.6(react@19.2.6) transitivePeerDependencies: - '@types/react' - '@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': + '@storybook/addon-docs@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.6) - '@storybook/csf-plugin': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + '@storybook/csf-plugin': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@storybook/react-dom-shim': 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) react: 19.2.6 @@ -18084,33 +18385,33 @@ snapshots: '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) optionalDependencies: - '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) - '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) '@vitest/runner': 4.1.7 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) transitivePeerDependencies: - react - react-dom - '@storybook/builder-vite@10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': + '@storybook/builder-vite@10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': dependencies: - '@storybook/csf-plugin': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + '@storybook/csf-plugin': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ts-dedent: 2.2.0 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) transitivePeerDependencies: - esbuild - rollup - webpack - '@storybook/csf-plugin@10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': + '@storybook/csf-plugin@10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': dependencies: storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.4 rollup: 4.60.1 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) webpack: 5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15) '@storybook/global@5.0.0': {} @@ -18129,11 +18430,11 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@storybook/react-vite@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': + '@storybook/react-vite@10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(esbuild@0.27.4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) '@rollup/pluginutils': 5.3.0(rollup@4.60.1) - '@storybook/builder-vite': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) + '@storybook/builder-vite': 10.4.0(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)) '@storybook/react': 10.4.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3) empathic: 2.0.1 magic-string: 0.30.21 @@ -18143,7 +18444,7 @@ snapshots: resolve: 1.22.12 storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tsconfig-paths: 4.2.0 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -18365,17 +18666,17 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/bonjour@3.5.13': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -18388,11 +18689,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 5.1.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/connect@3.4.38': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/counterpart@0.18.4': {} @@ -18525,13 +18826,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssh2': 1.15.5 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssh2': 1.15.5 '@types/doctrine@0.0.9': {} @@ -18546,14 +18847,14 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -18575,7 +18876,7 @@ snapshots: '@types/fs-extra@9.0.13': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/geojson@7946.0.16': {} @@ -18598,7 +18899,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/istanbul-lib-coverage@2.0.6': {} @@ -18619,7 +18920,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -18631,7 +18932,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/linkify-it@5.0.0': {} @@ -18664,16 +18965,16 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/node-fetch@2.6.13': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 form-data: 4.0.5 - '@types/node@18.19.130': + '@types/node@22.13.14': dependencies: - undici-types: 5.26.5 + undici-types: 6.20.0 '@types/normalize-package-data@2.4.4': {} @@ -18681,7 +18982,7 @@ snapshots: '@types/npm-registry-fetch@8.0.9': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/node-fetch': 2.6.13 '@types/npm-package-arg': 6.1.4 '@types/npmlog': 7.0.0 @@ -18689,11 +18990,11 @@ snapshots: '@types/npmlog@7.0.0': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/pacote@11.1.8': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/npm-registry-fetch': 8.0.9 '@types/npmlog': 7.0.0 '@types/ssri': 7.1.5 @@ -18708,7 +19009,7 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -18716,7 +19017,7 @@ snapshots: '@types/plist@3.0.5': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 xmlbuilder: 15.1.1 optional: true @@ -18728,7 +19029,7 @@ snapshots: '@types/qrcode@1.5.6': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/qs@6.15.1': {} @@ -18761,7 +19062,7 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/retry@0.12.2': {} @@ -18778,11 +19079,11 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/send@1.2.1': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/serve-index@1.9.4': dependencies: @@ -18791,34 +19092,34 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/send': 0.17.6 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/sockjs@0.3.36': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssh2-streams@0.1.13': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssh2@0.5.52': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssh2-streams': 0.1.13 '@types/ssh2@1.15.5': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/ssri@7.1.5': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/stack-utils@2.0.3': {} @@ -18830,7 +19131,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/tough-cookie@4.0.5': {} @@ -18848,7 +19149,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@types/yargs-parser@21.0.3': {} @@ -18858,7 +19159,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 optional: true '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3)': @@ -19059,34 +19360,39 @@ snapshots: dependencies: react: 19.2.6 - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0))(vue@3.5.31(typescript@6.0.3))': + '@vitejs/plugin-react@6.0.2(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))': dependencies: - vite: 5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0) + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@22.13.14)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0))(vue@3.5.31(typescript@6.0.3))': + dependencies: + vite: 5.4.21(@types/node@22.13.14)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0) vue: 3.5.31(typescript@6.0.3) - '@vitest/browser-playwright@4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7)': + '@vitest/browser-playwright@4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7)': + '@vitest/browser@4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -19106,9 +19412,9 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) optionalDependencies: - '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) '@vitest/expect@3.2.4': dependencies: @@ -19127,13 +19433,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))': + '@vitest/mocker@4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))': dependencies: '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) '@vitest/pretty-format@3.2.4': dependencies: @@ -21378,7 +21684,7 @@ snapshots: electron@42.3.0: dependencies: '@electron/get': 5.0.0 - '@types/node': 18.19.130 + '@types/node': 22.13.14 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -21681,13 +21987,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3): + eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0))(typescript@6.0.3): dependencies: '@typescript-eslint/utils': 8.59.4(eslint@8.57.1)(typescript@6.0.3) eslint: 8.57.1 optionalDependencies: '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) - jest: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + jest: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -21711,7 +22017,7 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-matrix-org@3.0.0(24da5b5c4d393c3b1f692cbfbfe73aed): + eslint-plugin-matrix-org@3.0.0(dd8c44dac078085b4cf5561606e117de): dependencies: '@babel/core': 7.29.0 '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) @@ -21724,7 +22030,7 @@ snapshots: eslint-config-prettier: 10.1.8(eslint@8.57.1) eslint-plugin-deprecate: 0.9.0(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) - eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) + eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0))(typescript@6.0.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 7.1.1(eslint@8.57.1) @@ -22243,6 +22549,16 @@ snapshots: fraction.js@5.3.4: {} + framer-motion@12.40.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + motion-dom: 12.40.0 + motion-utils: 12.39.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.4.0 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + fresh@0.5.2: {} fresh@2.0.0: {} @@ -23125,7 +23441,7 @@ snapshots: '@jest/expect': 30.4.1 '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.2(babel-plugin-macros@3.1.0) @@ -23145,7 +23461,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0): + jest-cli@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 30.4.2(babel-plugin-macros@3.1.0) '@jest/test-result': 30.4.1 @@ -23153,7 +23469,7 @@ snapshots: chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + jest-config: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) jest-util: 30.4.1 jest-validate: 30.4.1 yargs: 17.7.2 @@ -23164,7 +23480,7 @@ snapshots: - supports-color - ts-node - jest-config@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0): + jest-config@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0): dependencies: '@babel/core': 7.29.0 '@jest/get-type': 30.1.0 @@ -23190,7 +23506,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -23229,7 +23545,7 @@ snapshots: '@jest/environment': 30.4.1 '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-mock: 30.4.1 jest-util: 30.4.1 jest-validate: 30.4.1 @@ -23241,7 +23557,7 @@ snapshots: jest-haste-map@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -23281,7 +23597,7 @@ snapshots: jest-mock@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 jest-util: 30.4.1 jest-pnp-resolver@1.2.3(jest-resolve@30.4.1): @@ -23317,7 +23633,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -23346,7 +23662,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 cjs-module-lexer: 2.2.0 collect-v8-coverage: 1.0.3 @@ -23393,7 +23709,7 @@ snapshots: jest-util@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 @@ -23412,7 +23728,7 @@ snapshots: dependencies: '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -23421,24 +23737,24 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@30.4.1: dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 '@ungap/structured-clone': 1.3.1 jest-util: 30.4.1 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0): + jest@30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 30.4.2(babel-plugin-macros@3.1.0) '@jest/types': 30.4.1 import-local: 3.2.0 - jest-cli: 30.4.2(@types/node@18.19.130)(babel-plugin-macros@3.1.0) + jest-cli: 30.4.2(@types/node@22.13.14)(babel-plugin-macros@3.1.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -23921,7 +24237,7 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/76aee8e682514931bc620b2b289eabe7a8e67118: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f5eb1e5b74b36d0f43cddc048f7867819e2dfd70: dependencies: '@babel/runtime': 7.29.7 '@matrix-org/matrix-sdk-crypto-wasm': 18.3.1 @@ -24223,6 +24539,12 @@ snapshots: dependencies: color-name: 1.1.4 + motion-dom@12.40.0: + dependencies: + motion-utils: 12.39.0 + + motion-utils@12.39.0: {} + mrmime@2.0.1: {} ms@2.0.0: {} @@ -25017,7 +25339,7 @@ snapshots: png-to-ico@3.0.1: dependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 minimist: 1.2.8 pngjs: 7.0.0 @@ -25612,7 +25934,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 18.19.130 + '@types/node': 22.13.14 long: 5.3.2 protocol-buffers-schema@3.6.1: {} @@ -26648,14 +26970,14 @@ snapshots: '@repobuddy/test': 1.0.1 '@storybook/addon-vitest': 10.4.0(@vitest/browser-playwright@4.1.7)(@vitest/browser@4.1.7)(@vitest/runner@4.1.7)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(vitest@4.1.7) '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) glob: 13.0.6 is-ci: 4.1.0 memoize: 11.0.0 pathe: 2.0.3 storybook: 10.4.0(@testing-library/dom@10.4.1)(@types/react@19.2.14)(prettier@3.8.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) type-plus: 8.0.0-beta.8(typescript@6.0.3) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) vitest-plugin-vis: 5.1.1(@vitest/browser-playwright@4.1.7)(@vitest/browser@4.1.7)(babel-plugin-macros@3.1.0)(typescript@6.0.3)(vitest@4.1.7) transitivePeerDependencies: - '@vitest/browser-playwright' @@ -26844,6 +27166,15 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + styled-components@6.4.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + '@emotion/is-prop-valid': 1.4.0 + csstype: 3.2.3 + react: 19.2.6 + stylis: 4.3.6 + optionalDependencies: + react-dom: 19.2.6(react@19.2.6) + stylehacks@7.0.7(postcss@8.5.15): dependencies: browserslist: 4.28.2 @@ -26921,6 +27252,8 @@ snapshots: - supports-color - typescript + stylis@4.3.6: {} + stylis@4.4.0: {} sugarss@5.0.1(postcss@8.5.15): @@ -27404,7 +27737,7 @@ snapshots: underscore@1.13.8: {} - undici-types@5.26.5: {} + undici-types@6.20.0: {} undici@6.25.0: {} @@ -27466,7 +27799,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-dts@1.0.1(@microsoft/api-extractor@7.58.7(@types/node@18.19.130))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)): + unplugin-dts@1.0.1(@microsoft/api-extractor@7.58.7(@types/node@22.13.14))(esbuild@0.27.4)(rolldown@1.0.3)(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(webpack@5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.1) '@volar/typescript': 2.4.28 @@ -27478,11 +27811,11 @@ snapshots: typescript: 6.0.3 unplugin: 2.3.11 optionalDependencies: - '@microsoft/api-extractor': 7.58.7(@types/node@18.19.130) + '@microsoft/api-extractor': 7.58.7(@types/node@22.13.14) esbuild: 0.27.4 rolldown: 1.0.3 rollup: 4.60.1 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) webpack: 5.107.1(esbuild@0.27.4)(lightningcss@1.32.0)(postcss@8.5.15) transitivePeerDependencies: - supports-color @@ -27638,27 +27971,38 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-node-polyfills@0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)): + vite-plugin-node-polyfills@0.28.0(rollup@4.60.1)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.60.1) node-stdlib-browser: 1.3.1 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) transitivePeerDependencies: - rollup - vite@5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0): + vite-plugin-svgr@5.2.0(rollup@4.60.1)(typescript@6.0.3)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + '@svgr/core': 8.1.0(typescript@6.0.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + vite@5.4.21(@types/node@22.13.14)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0): dependencies: esbuild: 0.27.4 postcss: 8.5.15 rollup: 4.60.1 optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 fsevents: 2.3.3 lightningcss: 1.32.0 sugarss: 5.0.1(postcss@8.5.15) terser: 5.48.0 - vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4): + vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -27666,7 +28010,7 @@ snapshots: rolldown: 1.0.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 18.19.130 + '@types/node': 22.13.14 esbuild: 0.27.4 fsevents: 2.3.3 jiti: 2.7.0 @@ -27674,14 +28018,14 @@ snapshots: terser: 5.48.0 yaml: 2.8.4 - vitepress-plugin-mermaid@2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3)): + vitepress-plugin-mermaid@2.0.17(mermaid@11.15.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@22.13.14)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3)): dependencies: mermaid: 11.15.0 - vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3) + vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@22.13.14)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3) optionalDependencies: '@mermaid-js/mermaid-mindmap': 9.3.0 - vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3): + vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@22.13.14)(@types/react@19.2.14)(axios@1.16.1)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.15)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(typescript@6.0.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.14)(search-insights@2.17.3) @@ -27690,7 +28034,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0))(vue@3.5.31(typescript@6.0.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@22.13.14)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0))(vue@3.5.31(typescript@6.0.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.31 '@vueuse/core': 12.8.2(typescript@6.0.3) @@ -27699,7 +28043,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 2.5.0 - vite: 5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0) + vite: 5.4.21(@types/node@22.13.14)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0) vue: 3.5.31(typescript@6.0.3) optionalDependencies: postcss: 8.5.15 @@ -27732,7 +28076,7 @@ snapshots: vitest-plugin-vis@5.1.1(@vitest/browser-playwright@4.1.7)(@vitest/browser@4.1.7)(babel-plugin-macros@3.1.0)(typescript@6.0.3)(vitest@4.1.7): dependencies: - '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) dedent: 1.7.2(babel-plugin-macros@3.1.0) glob: 13.0.6 is-ci: 4.1.0 @@ -27743,17 +28087,17 @@ snapshots: rimraf: 6.1.3 ssim.js: 3.5.0 type-plus: 8.0.0-beta.8(typescript@6.0.3) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) optionalDependencies: - '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) transitivePeerDependencies: - babel-plugin-macros - typescript - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.13.14)(@vitest/browser-playwright@4.1.7)(@vitest/coverage-v8@4.1.7)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)): dependencies: '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.7(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) '@vitest/pretty-format': 4.1.7 '@vitest/runner': 4.1.7 '@vitest/snapshot': 4.1.7 @@ -27770,12 +28114,12 @@ snapshots: tinyexec: 1.2.2 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) + vite: 8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 18.19.130 - '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) + '@types/node': 22.13.14 + '@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.15(@types/node@22.13.14)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.7) '@vitest/coverage-v8': 4.1.7(@vitest/browser@4.1.7)(vitest@4.1.7) jsdom: 26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f) transitivePeerDependencies: From cf9ee41d061b534ba093b3c23bf5f0a8b0ef5344 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:19:13 +0100 Subject: [PATCH 192/211] DRY --- .github/workflows/build-and-test.yaml | 3 ++ modules/.eslintrc.cjs | 2 +- modules/banner/package.json | 8 +---- modules/banner/vite.config.ts | 32 ++--------------- modules/package.json | 24 +++++++++++++ modules/restricted-guests/package.json | 8 +---- modules/restricted-guests/vite.config.ts | 32 ++--------------- modules/widget-lifecycle/package.json | 7 +--- modules/widget-lifecycle/vite.config.ts | 11 +++--- modules/widget-toggles/package.json | 8 +---- modules/widget-toggles/vite.config.ts | 35 +++--------------- package.json | 1 - packages/module-api/package.json | 7 +++- packages/module-api/vite.base.ts | 46 ++++++++++++++++++++++++ pnpm-lock.yaml | 42 +++++++++++++++------- pnpm-workspace.yaml | 1 + 16 files changed, 129 insertions(+), 138 deletions(-) create mode 100644 modules/package.json create mode 100644 packages/module-api/vite.base.ts diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index c5db83fbc9..63c516074f 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -208,6 +208,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Build modules + run: pnpm -r --filter "./modules/**" run build + - name: Setup playwright uses: ./.github/actions/setup-playwright with: diff --git a/modules/.eslintrc.cjs b/modules/.eslintrc.cjs index b8b9794a3c..70d522d844 100644 --- a/modules/.eslintrc.cjs +++ b/modules/.eslintrc.cjs @@ -2,7 +2,7 @@ module.exports = { plugins: ["matrix-org", "eslint-plugin-react-compiler"], extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react", "plugin:matrix-org/a11y"], parserOptions: { - project: ["./tsconfig.json"], + project: true, }, env: { browser: true, diff --git a/modules/banner/package.json b/modules/banner/package.json index ae038ca28c..caf25854d9 100644 --- a/modules/banner/package.json +++ b/modules/banner/package.json @@ -7,12 +7,7 @@ "license": "SEE LICENSE IN README.md", "scripts": { "build": "vite build", - "lint": "pnpm lint:types && pnpm lint:js", - "lint:types": "tsc --noEmit", - "lint:js": "eslint --max-warnings 0 src -c ../.eslintrc.cjs", - "test:playwright": "playwright test -c ../playwright.config.ts", - "test:playwright:open": "yarn test:playwright -c ../playwright.config.ts --ui", - "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + "lint:types": "tsc --noEmit" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", @@ -24,7 +19,6 @@ "matrix-web-i18n": "^3.6.0", "matrix-widget-api": "^1.17.0", "react": "catalog:", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "catalog:", "vite": "catalog:", "vite-plugin-node-polyfills": "catalog:", diff --git a/modules/banner/vite.config.ts b/modules/banner/vite.config.ts index e2ee02eaa2..a5f7d1c139 100644 --- a/modules/banner/vite.config.ts +++ b/modules/banner/vite.config.ts @@ -7,16 +7,16 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig, esmExternalRequirePlugin } from "vite"; +import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; +import baseConfig from "@element-hq/element-web-module-api/vite.base.ts"; const __dirname = dirname(fileURLToPath(import.meta.url)); -export default defineConfig({ +export default mergeConfig(baseConfig, { build: { lib: { entry: resolve(__dirname, "src/index.tsx"), @@ -24,22 +24,6 @@ export default defineConfig({ fileName: "index", formats: ["es"], }, - outDir: "lib", - target: "esnext", - sourcemap: true, - rolldownOptions: { - plugins: [ - esmExternalRequirePlugin({ - external: ["react"], - }), - ], - output: { - globals: { - // Reuse React from the host app - react: "window.React", - }, - }, - }, }, plugins: [ importCSSSheet(), @@ -48,15 +32,5 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": "'production'", - "process": { env: { NODE_ENV: "production" } }, - }, }); diff --git a/modules/package.json b/modules/package.json new file mode 100644 index 0000000000..6e5ec59c17 --- /dev/null +++ b/modules/package.json @@ -0,0 +1,24 @@ +{ + "name": "element-web-modules", + "version": "0.0.0", + "type": "module", + "scripts": { + "lint:js": "eslint --max-warnings 0 */src -c ./.eslintrc.cjs", + "lint:ts": "tsc --noEmit", + "test:playwright": "playwright test -c ./playwright.config.ts", + "test:playwright:open": "yarn test:playwright -c ./playwright.config.ts --ui", + "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + }, + "devDependencies": { + "@element-hq/element-web-playwright-common": "workspace:*", + "eslint": "8", + "eslint-config-google": "^0.14.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-matrix-org": "^3.0.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-compiler": "^19.1.0-rc.2", + "eslint-plugin-unicorn": "^56.0.0" + }, + "private": true +} diff --git a/modules/restricted-guests/package.json b/modules/restricted-guests/package.json index 8a87b5a343..a1aa9afcac 100644 --- a/modules/restricted-guests/package.json +++ b/modules/restricted-guests/package.json @@ -7,12 +7,7 @@ "license": "SEE LICENSE IN README.md", "scripts": { "build": "vite build", - "lint": "pnpm lint:types && pnpm lint:js", - "lint:types": "tsc --noEmit", - "lint:js": "eslint --max-warnings 0 src -c ../.eslintrc.cjs", - "test:playwright": "playwright test -c ../playwright.config.ts", - "test:playwright:open": "yarn test:playwright -c ../playwright.config.ts --ui", - "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + "lint:types": "tsc --noEmit" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", @@ -21,7 +16,6 @@ "@types/react": "catalog:", "@vitejs/plugin-react": "catalog:", "react": "catalog:", - "rollup-plugin-external-globals": "^0.13.0", "typescript": "catalog:", "vite": "catalog:", "vite-plugin-node-polyfills": "catalog:" diff --git a/modules/restricted-guests/vite.config.ts b/modules/restricted-guests/vite.config.ts index 9dfdb5dbed..8049efcc95 100644 --- a/modules/restricted-guests/vite.config.ts +++ b/modules/restricted-guests/vite.config.ts @@ -7,15 +7,15 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig, esmExternalRequirePlugin } from "vite"; +import { mergeConfig } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; +import baseConfig from "@element-hq/element-web-module-api/vite.base.ts"; const __dirname = dirname(fileURLToPath(import.meta.url)); -export default defineConfig({ +export default mergeConfig(baseConfig, { build: { lib: { entry: resolve(__dirname, "src/index.tsx"), @@ -23,22 +23,6 @@ export default defineConfig({ fileName: "index", formats: ["es"], }, - outDir: "lib", - target: "esnext", - sourcemap: true, - rolldownOptions: { - plugins: [ - esmExternalRequirePlugin({ - external: ["react"], - }), - ], - output: { - globals: { - // Reuse React from the host app - react: "window.React", - }, - }, - }, }, plugins: [ importCSSSheet(), @@ -46,15 +30,5 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": "'production'", - "process": { env: { NODE_ENV: "production" } }, - }, }); diff --git a/modules/widget-lifecycle/package.json b/modules/widget-lifecycle/package.json index 534af61d9e..f9e3bf644a 100644 --- a/modules/widget-lifecycle/package.json +++ b/modules/widget-lifecycle/package.json @@ -7,12 +7,7 @@ "license": "SEE LICENSE IN README.md", "scripts": { "build": "vite build", - "lint": "pnpm lint:types && pnpm lint:js", - "lint:types": "tsc --noEmit", - "lint:js": "eslint --max-warnings 0 src -c ../.eslintrc.cjs", - "test:playwright": "playwright test -c ../playwright.config.ts", - "test:playwright:open": "yarn test:playwright -c ../playwright.config.ts --ui", - "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + "lint:types": "tsc --noEmit" }, "devDependencies": { "@element-hq/element-web-module-api": "workspace:*", diff --git a/modules/widget-lifecycle/vite.config.ts b/modules/widget-lifecycle/vite.config.ts index 66bd04e4e1..3683872b2b 100644 --- a/modules/widget-lifecycle/vite.config.ts +++ b/modules/widget-lifecycle/vite.config.ts @@ -7,11 +7,12 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vitest/config"; +import { mergeConfig } from "vitest/config"; +import baseConfig from "@element-hq/element-web-module-api/vite.base.ts"; const __dirname = dirname(fileURLToPath(import.meta.url)); -export default defineConfig({ +export default mergeConfig(baseConfig, { build: { lib: { entry: resolve(__dirname, "src/index.ts"), @@ -19,10 +20,6 @@ export default defineConfig({ fileName: "index", formats: ["es"], }, - outDir: "lib", - target: "esnext", - sourcemap: true, - minify: false, }, test: { include: ["tests/**/*.test.ts"], @@ -31,7 +28,7 @@ export default defineConfig({ coverage: { provider: "v8", include: ["src/**/*.ts"], - reporter: [["lcov", { projectRoot: "../../../" }], "text"], + reporter: [["lcov", { projectRoot: "../../" }], "text"], }, }, }); diff --git a/modules/widget-toggles/package.json b/modules/widget-toggles/package.json index 172f73135b..2e32e5b064 100644 --- a/modules/widget-toggles/package.json +++ b/modules/widget-toggles/package.json @@ -7,13 +7,8 @@ "license": "SEE LICENSE IN README.md", "scripts": { "build": "vite build", - "lint": "pnpm lint:types && pnpm lint:js", "lint:types": "tsc --noEmit", - "lint:js": "eslint --max-warnings 0 src -c ../.eslintrc.cjs", - "test": "vitest run --coverage", - "test:playwright": "playwright test -c ../playwright.config.ts", - "test:playwright:open": "yarn test:playwright -c ../playwright.config.ts --ui", - "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + "test": "vitest run --coverage" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", @@ -28,7 +23,6 @@ "@vitest/coverage-v8": "catalog:", "react": "catalog:", "typescript": "catalog:", - "rollup-plugin-external-globals": "^0.13.0", "vite": "catalog:", "vite-plugin-node-polyfills": "catalog:", "vite-plugin-svgr": "catalog:", diff --git a/modules/widget-toggles/vite.config.ts b/modules/widget-toggles/vite.config.ts index af7dd703b9..403f9ccdf4 100644 --- a/modules/widget-toggles/vite.config.ts +++ b/modules/widget-toggles/vite.config.ts @@ -7,18 +7,17 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { esmExternalRequirePlugin } from "vite"; import react from "@vitejs/plugin-react"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import externalGlobals from "rollup-plugin-external-globals"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; -import { defineConfig } from "vitest/config"; +import { mergeConfig } from "vitest/config"; import { playwright } from "@vitest/browser-playwright"; +import baseConfig from "@element-hq/element-web-module-api/vite.base.ts"; const __dirname = dirname(fileURLToPath(import.meta.url)); -export default defineConfig({ +export default mergeConfig(baseConfig, { build: { lib: { entry: resolve(__dirname, "src/index.tsx"), @@ -26,22 +25,6 @@ export default defineConfig({ fileName: "index", formats: ["es"], }, - outDir: "lib", - target: "esnext", - sourcemap: true, - rolldownOptions: { - plugins: [ - esmExternalRequirePlugin({ - external: ["react"], - }), - ], - output: { - globals: { - // Reuse React from the host app - react: "window.React", - }, - }, - }, }, plugins: [ importCSSSheet(), @@ -50,17 +33,7 @@ export default defineConfig({ nodePolyfills({ include: ["events"], }), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), ], - define: { - // Use production mode for the build as it is tested against production builds of Element Web, - // this is required for React JSX versions to be compatible. - "process.env.NODE_ENV": "'production'", - "process": { env: { NODE_ENV: "production" } }, - }, test: { include: ["tests/**/*.test.{ts,tsx}"], exclude: ["./e2e/**/*", "./node_modules/**/*"], @@ -68,7 +41,7 @@ export default defineConfig({ coverage: { provider: "v8", include: ["src/**/*.ts"], - reporter: [["lcov", { projectRoot: "../../../" }], "text"], + reporter: [["lcov", { projectRoot: "../../" }], "text"], }, browser: { enabled: true, diff --git a/package.json b/package.json index 967d43c316..b74c1433dc 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "devDependencies": { "@action-validator/cli": "^0.6.0", "@action-validator/core": "^0.6.0", - "@element-hq/element-web-playwright-common": "workspace:*", "@nx-tools/nx-container": "^7.2.1", "@playwright/test": "catalog:", "@types/node": "22", diff --git a/packages/module-api/package.json b/packages/module-api/package.json index 925f03e2b7..1ea9ac4c65 100644 --- a/packages/module-api/package.json +++ b/packages/module-api/package.json @@ -19,10 +19,15 @@ "types": "./lib/element-web-module-api-alpha.d.ts", "import": "./lib/element-web-plugin-engine.js", "require": "./lib/element-web-plugin-engine.umd.cjs" + }, + "./vite.base.ts": { + "types": "./vite.base.ts", + "import": "./vite.base.ts" } }, "files": [ - "lib" + "lib", + "vite.base.ts" ], "scripts": { "prepack": "nx build", diff --git a/packages/module-api/vite.base.ts b/packages/module-api/vite.base.ts new file mode 100644 index 0000000000..4be510c587 --- /dev/null +++ b/packages/module-api/vite.base.ts @@ -0,0 +1,46 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Base vite config for building Element modules + */ + +import { defineConfig, esmExternalRequirePlugin } from "vite"; +import externalGlobals from "rollup-plugin-external-globals"; + +export default defineConfig({ + build: { + outDir: "lib", + target: "esnext", + sourcemap: true, + rolldownOptions: { + plugins: [ + esmExternalRequirePlugin({ + external: ["react"], + }), + ], + output: { + globals: { + // Reuse React from the host app + react: "window.React", + }, + }, + }, + }, + plugins: [ + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], + define: { + // Use production mode for the build as it is tested against production builds of Element Web, + // this is required for React JSX versions to be compatible. + "process.env.NODE_ENV": "'production'", + "process": { env: { NODE_ENV: "production" } }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6be5199799..96bc551804 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -309,9 +309,6 @@ importers: '@action-validator/core': specifier: ^0.6.0 version: 0.6.0 - '@element-hq/element-web-playwright-common': - specifier: workspace:* - version: link:packages/playwright-common '@nx-tools/nx-container': specifier: ^7.2.1 version: 7.2.3(@nx/devkit@22.6.5(nx@22.7.5))(@nx/js@22.6.5(@babel/traverse@7.29.7)(nx@22.7.5))(dotenv@17.4.2)(nx@22.7.5)(tslib@2.8.1) @@ -1098,6 +1095,36 @@ importers: specifier: 2.8.4 version: 2.8.4 + modules: + devDependencies: + '@element-hq/element-web-playwright-common': + specifier: workspace:* + version: link:../packages/playwright-common + eslint: + specifier: '8' + version: 8.57.1 + eslint-config-google: + specifier: ^0.14.0 + version: 0.14.0(eslint@8.57.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@8.57.1) + eslint-plugin-jsx-a11y: + specifier: ^6.10.2 + version: 6.10.2(eslint@8.57.1) + eslint-plugin-matrix-org: + specifier: ^3.0.0 + version: 3.0.0(dd8c44dac078085b4cf5561606e117de) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@8.57.1) + eslint-plugin-react-compiler: + specifier: ^19.1.0-rc.2 + version: 19.1.0-rc.2(eslint@8.57.1) + eslint-plugin-unicorn: + specifier: ^56.0.0 + version: 56.0.1(eslint@8.57.1) + modules/banner: dependencies: '@radix-ui/react-dialog': @@ -1146,9 +1173,6 @@ importers: react: specifier: 'catalog:' version: 19.2.6 - rollup-plugin-external-globals: - specifier: ^0.13.0 - version: 0.13.0(rollup@4.60.1) typescript: specifier: 'catalog:' version: 6.0.3 @@ -1195,9 +1219,6 @@ importers: react: specifier: 'catalog:' version: 19.2.6 - rollup-plugin-external-globals: - specifier: ^0.13.0 - version: 0.13.0(rollup@4.60.1) typescript: specifier: 'catalog:' version: 6.0.3 @@ -1284,9 +1305,6 @@ importers: react: specifier: 'catalog:' version: 19.2.6 - rollup-plugin-external-globals: - specifier: ^0.13.0 - version: 0.13.0(rollup@4.60.1) typescript: specifier: 'catalog:' version: 6.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0780d0f307..d03667db45 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,7 @@ ignoreWorkspaceRootCheck: true packages: - "apps/*" - "packages/*" + - "modules" - "modules/*" catalog: From 2192b2f5e35babdbbf52049b2cf2abdee845e1a3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:21:36 +0100 Subject: [PATCH 193/211] Fix vitest runs for modules --- modules/widget-lifecycle/package.json | 3 ++- modules/widget-toggles/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/widget-lifecycle/package.json b/modules/widget-lifecycle/package.json index f9e3bf644a..ce5acfe9ba 100644 --- a/modules/widget-lifecycle/package.json +++ b/modules/widget-lifecycle/package.json @@ -7,7 +7,8 @@ "license": "SEE LICENSE IN README.md", "scripts": { "build": "vite build", - "lint:types": "tsc --noEmit" + "lint:types": "tsc --noEmit", + "test:unit": "vitest run" }, "devDependencies": { "@element-hq/element-web-module-api": "workspace:*", diff --git a/modules/widget-toggles/package.json b/modules/widget-toggles/package.json index 2e32e5b064..496b86962a 100644 --- a/modules/widget-toggles/package.json +++ b/modules/widget-toggles/package.json @@ -8,7 +8,7 @@ "scripts": { "build": "vite build", "lint:types": "tsc --noEmit", - "test": "vitest run --coverage" + "test:unit": "vitest run" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", From 788c76d677535f80f47f1be442a6410a02265570 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:30:08 +0100 Subject: [PATCH 194/211] Fix widget toggles vitest browser test --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1134326e8b..8a0948ee8b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -139,7 +139,7 @@ jobs: - name: Setup playwright uses: ./.github/actions/setup-playwright - if: matrix.path == 'packages/shared-components' + if: matrix.path == 'packages/shared-components' || matrix.path == 'modules/widget-toggles' with: write-cache: ${{ github.event_name != 'merge_group' }} From af74f511ed44721cc676fc35c42c2edf5a2145c9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:42:00 +0100 Subject: [PATCH 195/211] Make tsc happier --- apps/web/test/unit-tests/Image-test.ts | 14 +++++++------- modules/banner/package.json | 4 ++-- modules/banner/project.json | 21 +++++++++++++++++++++ modules/restricted-guests/package.json | 4 ++-- modules/restricted-guests/project.json | 21 +++++++++++++++++++++ modules/widget-lifecycle/package.json | 6 +++--- modules/widget-lifecycle/project.json | 25 +++++++++++++++++++++++++ modules/widget-toggles/package.json | 6 +++--- modules/widget-toggles/project.json | 25 +++++++++++++++++++++++++ 9 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 modules/banner/project.json create mode 100644 modules/restricted-guests/project.json create mode 100644 modules/widget-lifecycle/project.json create mode 100644 modules/widget-toggles/project.json diff --git a/apps/web/test/unit-tests/Image-test.ts b/apps/web/test/unit-tests/Image-test.ts index 1104788e6f..be3c243e1c 100644 --- a/apps/web/test/unit-tests/Image-test.ts +++ b/apps/web/test/unit-tests/Image-test.ts @@ -32,28 +32,28 @@ describe("Image", () => { describe("blobIsAnimated", () => { it("Animated GIF", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.gif"))], { + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.gif")).slice()], { type: "image/gif", }); expect(await blobIsAnimated(img)).toBeTruthy(); }); it("Static GIF", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.gif"))], { + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.gif")).slice()], { type: "image/gif", }); expect(await blobIsAnimated(img)).toBeFalsy(); }); it("Animated WEBP", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.webp"))], { + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.webp")).slice()], { type: "image/webp", }); expect(await blobIsAnimated(img)).toBeTruthy(); }); it("Static WEBP", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.webp"))], { + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.webp")).slice()], { type: "image/webp", }); expect(await blobIsAnimated(img)).toBeFalsy(); @@ -61,14 +61,14 @@ describe("Image", () => { it("Static WEBP in extended file format", async () => { const img = new Blob( - [fs.readFileSync(path.resolve(__dirname, "images", "static-logo-extended-file-format.webp"))], + [fs.readFileSync(path.resolve(__dirname, "images", "static-logo-extended-file-format.webp")).slice()], { type: "image/webp" }, ); expect(await blobIsAnimated(img)).toBeFalsy(); }); it("Animated PNG", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.apng"))]); + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.apng")).slice()]); const pngBlob = img.slice(0, img.size, "image/png"); const apngBlob = img.slice(0, img.size, "image/apng"); expect(await blobIsAnimated(pngBlob)).toBeTruthy(); @@ -76,7 +76,7 @@ describe("Image", () => { }); it("Static PNG", async () => { - const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.png"))]); + const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "static-logo.png")).slice()]); const pngBlob = img.slice(0, img.size, "image/png"); const apngBlob = img.slice(0, img.size, "image/apng"); expect(await blobIsAnimated(pngBlob)).toBeFalsy(); diff --git a/modules/banner/package.json b/modules/banner/package.json index caf25854d9..6e75d52ac9 100644 --- a/modules/banner/package.json +++ b/modules/banner/package.json @@ -6,8 +6,8 @@ "main": "lib/index.js", "license": "SEE LICENSE IN README.md", "scripts": { - "build": "vite build", - "lint:types": "tsc --noEmit" + "build": "nx build", + "lint:types": "nx lint:types" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", diff --git a/modules/banner/project.json b/modules/banner/project.json new file mode 100644 index 0000000000..83c471023e --- /dev/null +++ b/modules/banner/project.json @@ -0,0 +1,21 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "vite build", + "inputs": ["src"], + "outputs": ["{projectRoot}/lib"], + "options": { "cwd": "modules/banner" }, + "dependsOn": ["^build"] + }, + "lint:types": { + "command": "tsc --noEmit", + "options": { + "cwd": "modules/banner" + }, + "dependsOn": ["^build"] + } + } +} diff --git a/modules/restricted-guests/package.json b/modules/restricted-guests/package.json index a1aa9afcac..842dbf10cc 100644 --- a/modules/restricted-guests/package.json +++ b/modules/restricted-guests/package.json @@ -6,8 +6,8 @@ "main": "lib/index.js", "license": "SEE LICENSE IN README.md", "scripts": { - "build": "vite build", - "lint:types": "tsc --noEmit" + "build": "nx build", + "lint:types": "nx lint:types" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", diff --git a/modules/restricted-guests/project.json b/modules/restricted-guests/project.json new file mode 100644 index 0000000000..ea14124b75 --- /dev/null +++ b/modules/restricted-guests/project.json @@ -0,0 +1,21 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "vite build", + "inputs": ["src"], + "outputs": ["{projectRoot}/lib"], + "options": { "cwd": "modules/restricted-guests" }, + "dependsOn": ["^build"] + }, + "lint:types": { + "command": "tsc --noEmit", + "options": { + "cwd": "modules/restricted-guests" + }, + "dependsOn": ["^build"] + } + } +} diff --git a/modules/widget-lifecycle/package.json b/modules/widget-lifecycle/package.json index ce5acfe9ba..fc25f1713b 100644 --- a/modules/widget-lifecycle/package.json +++ b/modules/widget-lifecycle/package.json @@ -6,9 +6,9 @@ "main": "lib/index.js", "license": "SEE LICENSE IN README.md", "scripts": { - "build": "vite build", - "lint:types": "tsc --noEmit", - "test:unit": "vitest run" + "build": "nx build", + "lint:types": "nx lint:types", + "test:unit": "nx test:unit" }, "devDependencies": { "@element-hq/element-web-module-api": "workspace:*", diff --git a/modules/widget-lifecycle/project.json b/modules/widget-lifecycle/project.json new file mode 100644 index 0000000000..1e166c083d --- /dev/null +++ b/modules/widget-lifecycle/project.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "vite build", + "inputs": ["src"], + "outputs": ["{projectRoot}/lib"], + "options": { "cwd": "modules/widget-lifecycle" }, + "dependsOn": ["^build"] + }, + "test:unit": { + "command": "vitest run", + "options": { "cwd": "modules/widget-lifecycle" } + }, + "lint:types": { + "command": "tsc --noEmit", + "options": { + "cwd": "modules/widget-lifecycle" + }, + "dependsOn": ["^build"] + } + } +} diff --git a/modules/widget-toggles/package.json b/modules/widget-toggles/package.json index 496b86962a..e4c903d299 100644 --- a/modules/widget-toggles/package.json +++ b/modules/widget-toggles/package.json @@ -6,9 +6,9 @@ "main": "lib/index.js", "license": "SEE LICENSE IN README.md", "scripts": { - "build": "vite build", - "lint:types": "tsc --noEmit", - "test:unit": "vitest run" + "build": "nx build", + "lint:types": "nx lint:types", + "test:unit": "nx test:unit" }, "devDependencies": { "@arcmantle/vite-plugin-import-css-sheet": "^1.0.12", diff --git a/modules/widget-toggles/project.json b/modules/widget-toggles/project.json new file mode 100644 index 0000000000..5c5854e3a6 --- /dev/null +++ b/modules/widget-toggles/project.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "build": { + "cache": true, + "command": "vite build", + "inputs": ["src"], + "outputs": ["{projectRoot}/lib"], + "options": { "cwd": "modules/widget-toggles" }, + "dependsOn": ["^build"] + }, + "test:unit": { + "command": "vitest run", + "options": { "cwd": "modules/widget-toggles" } + }, + "lint:types": { + "command": "tsc --noEmit", + "options": { + "cwd": "modules/widget-toggles" + }, + "dependsOn": ["^build"] + } + } +} From 180a9db9202a9f59404335cd50b8b7a7829c88fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:51:53 +0100 Subject: [PATCH 196/211] Make tsc happier --- apps/web/playwright/sample-files/index.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/web/playwright/sample-files/index.ts b/apps/web/playwright/sample-files/index.ts index 786e5be6a4..bef5fb197c 100644 --- a/apps/web/playwright/sample-files/index.ts +++ b/apps/web/playwright/sample-files/index.ts @@ -16,17 +16,14 @@ export function getSampleFilePath(file: string): string { return join(__dirname, file); } -export function readSampleFile(file: string, encoding: null): Promise; +export function readSampleFile(file: string, encoding: null): Promise; export function readSampleFile(file: string, encoding?: BufferEncoding): Promise; -export function readSampleFile( - file: string, - encoding: BufferEncoding | null = "utf-8", -): Promise { +export function readSampleFile(file: string, encoding: BufferEncoding | null = "utf-8"): Promise { return readFile(getSampleFilePath(file), encoding); } -export function readSampleFileSync(file: string, encoding: null): NonSharedBuffer; +export function readSampleFileSync(file: string, encoding: null): Buffer; export function readSampleFileSync(file: string, encoding?: BufferEncoding): string; -export function readSampleFileSync(file: string, encoding: BufferEncoding | null = "utf-8"): NonSharedBuffer | string { +export function readSampleFileSync(file: string, encoding: BufferEncoding | null = "utf-8"): Buffer | string { return readFileSync(getSampleFilePath(file), encoding); } From d4bdc706532a5bd115e6509b89b16c1024e3b888 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 11:59:47 +0100 Subject: [PATCH 197/211] Fix tests --- modules/widget-lifecycle/vite.config.ts | 10 -------- modules/widget-lifecycle/vitest.config.ts | 21 ++++++++++++++++ modules/widget-toggles/vite.config.ts | 19 +-------------- modules/widget-toggles/vitest.config.ts | 29 +++++++++++++++++++++++ 4 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 modules/widget-lifecycle/vitest.config.ts create mode 100644 modules/widget-toggles/vitest.config.ts diff --git a/modules/widget-lifecycle/vite.config.ts b/modules/widget-lifecycle/vite.config.ts index 3683872b2b..413eed0d8f 100644 --- a/modules/widget-lifecycle/vite.config.ts +++ b/modules/widget-lifecycle/vite.config.ts @@ -21,14 +21,4 @@ export default mergeConfig(baseConfig, { formats: ["es"], }, }, - test: { - include: ["tests/**/*.test.ts"], - exclude: ["./e2e/**/*", "./node_modules/**/*"], - reporters: ["default"], - coverage: { - provider: "v8", - include: ["src/**/*.ts"], - reporter: [["lcov", { projectRoot: "../../" }], "text"], - }, - }, }); diff --git a/modules/widget-lifecycle/vitest.config.ts b/modules/widget-lifecycle/vitest.config.ts new file mode 100644 index 0000000000..d5d1bdc6f7 --- /dev/null +++ b/modules/widget-lifecycle/vitest.config.ts @@ -0,0 +1,21 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["tests/**/*.test.ts"], + exclude: ["./e2e/**/*", "./node_modules/**/*"], + reporters: ["default"], + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + reporter: [["lcov", { projectRoot: "../../" }], "text"], + }, + }, +}); diff --git a/modules/widget-toggles/vite.config.ts b/modules/widget-toggles/vite.config.ts index 403f9ccdf4..efcb970ec7 100644 --- a/modules/widget-toggles/vite.config.ts +++ b/modules/widget-toggles/vite.config.ts @@ -12,7 +12,6 @@ import { nodePolyfills } from "vite-plugin-node-polyfills"; import svgr from "vite-plugin-svgr"; import { importCSSSheet } from "@arcmantle/vite-plugin-import-css-sheet"; import { mergeConfig } from "vitest/config"; -import { playwright } from "@vitest/browser-playwright"; import baseConfig from "@element-hq/element-web-module-api/vite.base.ts"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -25,6 +24,7 @@ export default mergeConfig(baseConfig, { fileName: "index", formats: ["es"], }, + minify: false, }, plugins: [ importCSSSheet(), @@ -34,21 +34,4 @@ export default mergeConfig(baseConfig, { include: ["events"], }), ], - test: { - include: ["tests/**/*.test.{ts,tsx}"], - exclude: ["./e2e/**/*", "./node_modules/**/*"], - reporters: ["default"], - coverage: { - provider: "v8", - include: ["src/**/*.ts"], - reporter: [["lcov", { projectRoot: "../../" }], "text"], - }, - browser: { - enabled: true, - headless: true, - provider: playwright({}), - instances: [{ browser: "chromium" }], - }, - setupFiles: ["tests/setupTests.ts"], - }, }); diff --git a/modules/widget-toggles/vitest.config.ts b/modules/widget-toggles/vitest.config.ts new file mode 100644 index 0000000000..f61ad52d58 --- /dev/null +++ b/modules/widget-toggles/vitest.config.ts @@ -0,0 +1,29 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig } from "vitest/config"; +import { playwright } from "@vitest/browser-playwright"; + +export default defineConfig({ + test: { + include: ["tests/**/*.test.{ts,tsx}"], + exclude: ["./e2e/**/*", "./node_modules/**/*"], + reporters: ["default"], + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + reporter: [["lcov", { projectRoot: "../../" }], "text"], + }, + browser: { + enabled: true, + headless: true, + provider: playwright({}), + instances: [{ browser: "chromium" }], + }, + setupFiles: ["tests/setupTests.ts"], + }, +}); From 763df4d425bce137efff1750182eb4b7ade42640 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 12:58:19 +0100 Subject: [PATCH 198/211] Fix docker test --- .github/workflows/docker.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 0174a5eb84..8ddfd255b0 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -53,7 +53,7 @@ jobs: set -x # Make a fake module to test the image - MODULE_PATH="modules/module_name/index.js" + MODULE_PATH="test_modules/module_name/index.js" mkdir -p $(dirname $MODULE_PATH) echo 'alert("Testing");' > $MODULE_PATH @@ -64,7 +64,7 @@ jobs: --rm \ -e "ELEMENT_WEB_PORT=$ELEMENT_WEB_PORT" \ -dp "$ELEMENT_WEB_PORT:$ELEMENT_WEB_PORT" \ - -v $(pwd)/modules:/modules \ + -v $(pwd)/test_modules:/modules \ "$IMAGEID" \ ) From c315848c47832dbfbaa613f8670bcc06f0db6548 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 13:14:26 +0100 Subject: [PATCH 199/211] Fix docker test --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 8ddfd255b0..ea651c1674 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -53,7 +53,7 @@ jobs: set -x # Make a fake module to test the image - MODULE_PATH="test_modules/module_name/index.js" + MODULE_PATH="modules/module_name/index.js" mkdir -p $(dirname $MODULE_PATH) echo 'alert("Testing");' > $MODULE_PATH From d6313dfae178cd02f2a22671fa302fad8c014ade Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 13:21:13 +0100 Subject: [PATCH 200/211] Set --workspace-concurrency=1 --- .github/workflows/static_analysis.yaml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index 0d10bfdc6e..bc806a9788 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -68,7 +68,7 @@ jobs: run: "pnpm install --frozen-lockfile" - name: Run ${{ matrix.command }} - run: pnpm --if-present run "$CMD" && pnpm -r --if-present run "$CMD" + run: pnpm --if-present run "$CMD" && pnpm -r --workspace-concurrency=1 --if-present run "$CMD" env: CMD: ${{ matrix.command }} diff --git a/package.json b/package.json index b74c1433dc..1b37c8f446 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "i18n": "pnpm -r i18n", "i18n:sort": "pnpm -r i18n:sort", "i18n:lint": "pnpm -r i18n:lint", - "lint": "pnpm -r lint:types && pnpm lint:prettier && pnpm -r lint:js && pnpm -r lint:style && pnpm lint:workflows && pnpm lint:knip", + "lint": "pnpm -r --workspace-concurrency=1 lint:types && pnpm lint:prettier && pnpm -r lint:js && pnpm -r lint:style && pnpm lint:workflows && pnpm lint:knip", "lint:prettier": "prettier --check .", "lint:prettier-fix": "prettier --log-level=warn --write .", "lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) -print -exec action-validator {} ';'", From 99dcfe7fe1586977010017a3ae2fbba46a9160ca Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 13:27:37 +0100 Subject: [PATCH 201/211] Fix docker --- .github/workflows/docker.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index ea651c1674..e0ec8e2307 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -53,9 +53,9 @@ jobs: set -x # Make a fake module to test the image - MODULE_PATH="modules/module_name/index.js" - mkdir -p $(dirname $MODULE_PATH) - echo 'alert("Testing");' > $MODULE_PATH + MODULE_PATH="module_name/index.js" + mkdir -p $(dirname test_modules/$MODULE_PATH) + echo 'alert("Testing");' > test_modules/$MODULE_PATH # Spin up a container of the image ELEMENT_WEB_PORT=8181 @@ -71,7 +71,7 @@ jobs: # Run some smoke tests wget --retry-connrefused --tries=5 -q --wait=3 --spider "http://localhost:$ELEMENT_WEB_PORT/modules/module_name/index.js" MODULE_0=$(curl "http://localhost:$ELEMENT_WEB_PORT/config.json" | jq -r .modules[0]) - test "$MODULE_0" = "/${MODULE_PATH}" + test "$MODULE_0" = "/modules/${MODULE_PATH}" # Check healthcheck until test "$(docker inspect -f {{.State.Health.Status}} $CONTAINER_ID)" == "healthy"; do From 6f46493b4d3ea7cc93b3eed8da214e678a3c5700 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Jun 2026 13:34:08 +0100 Subject: [PATCH 202/211] DRY --- .../e2e/widget-lifecycle.spec.ts | 6 +- modules/widget-toggles/tests/index.test.ts | 55 ++++++++----------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/modules/widget-lifecycle/e2e/widget-lifecycle.spec.ts b/modules/widget-lifecycle/e2e/widget-lifecycle.spec.ts index be91d8bcf1..d3b55db644 100644 --- a/modules/widget-lifecycle/e2e/widget-lifecycle.spec.ts +++ b/modules/widget-lifecycle/e2e/widget-lifecycle.spec.ts @@ -65,7 +65,7 @@ test.describe("Widget Lifecycle", () => { }); test("auto-approves preload and identity", async ({ page, user, homeserver }, testInfo) => { - rejectToastIfExists(page, "Verify this device"); + await rejectToastIfExists(page, "Verify this device"); // A bot creates a room with the widget pinned to the top panel, then invites the test user. // Because the widget was added by a different user (the bot), Element would normally show a @@ -123,7 +123,7 @@ test.describe("Widget Lifecycle", () => { }); test("prompts for capabilities not in the allowlist", async ({ page, user, homeserver }, testInfo) => { - rejectToastIfExists(page, "Verify this device"); + await rejectToastIfExists(page, "Verify this device"); const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( @@ -186,7 +186,7 @@ test.describe("Widget Lifecycle", () => { user, homeserver, }, testInfo) => { - rejectToastIfExists(page, "Verify this device"); + await rejectToastIfExists(page, "Verify this device"); const bot = await homeserver.registerUser(`bot_${testInfo.testId}`, "password", "Bot"); const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( diff --git a/modules/widget-toggles/tests/index.test.ts b/modules/widget-toggles/tests/index.test.ts index d1258e56a2..1040e6289f 100644 --- a/modules/widget-toggles/tests/index.test.ts +++ b/modules/widget-toggles/tests/index.test.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { beforeEach, describe, expect, test, vi } from "vitest"; -import { render, screen } from "@testing-library/react"; +import { render, type RenderResult, screen } from "@testing-library/react"; import { type Api } from "@element-hq/element-web-module-api"; import { type IWidget } from "matrix-widget-api"; @@ -98,6 +98,25 @@ describe("WidgetToggleModule", () => { return (api.extras.addRoomHeaderButtonCallback as ReturnType).mock.calls[0][0]; }; + const mockAndRender = async (api: Api): Promise => { + (api.i18n.translate as ReturnType).mockImplementation( + (key: string, vars?: Record) => { + let result = key; + if (vars) { + for (const [k, v] of Object.entries(vars)) { + result = result.replace(`%(${k})s`, v); + } + } + return result; + }, + ); + const callback = await getCallback(api); + + const result = callback(roomId); + expect(result).toBeDefined(); + return render(result!); + }; + test("returns undefined when there are no widgets in the room", async () => { const api = makeApi([]); const callback = await getCallback(api); @@ -119,22 +138,7 @@ describe("WidgetToggleModule", () => { mockWidget({ id: "w1", type: "m.custom", name: "Widget One" }), mockWidget({ id: "w2", type: "m.custom", name: "Widget Two" }), ]); - (api.i18n.translate as ReturnType).mockImplementation( - (key: string, vars?: Record) => { - let result = key; - if (vars) { - for (const [k, v] of Object.entries(vars)) { - result = result.replace(`%(${k})s`, v); - } - } - return result; - }, - ); - const callback = await getCallback(api); - - const result = callback(roomId); - expect(result).toBeDefined(); - render(result!); + await mockAndRender(api); expect(screen.getAllByRole("button").length).toBe(2); }); @@ -144,22 +148,7 @@ describe("WidgetToggleModule", () => { mockWidget({ id: "w1", type: "m.custom", name: "Widget One" }), mockWidget({ id: "w2", type: "m.other", name: "Widget Other" }), ]); - (api.i18n.translate as ReturnType).mockImplementation( - (key: string, vars?: Record) => { - let result = key; - if (vars) { - for (const [k, v] of Object.entries(vars)) { - result = result.replace(`%(${k})s`, v); - } - } - return result; - }, - ); - const callback = await getCallback(api); - - const result = callback(roomId); - expect(result).toBeDefined(); - render(result!); + await mockAndRender(api); expect(screen.getAllByRole("button").length).toBe(1); }); From 18a4fcae492c166ec2f0466c688439f8374e2104 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 12:42:52 +0100 Subject: [PATCH 203/211] Prettier --- pnpm-workspace.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f420ba68ed..e86b53511a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -36,7 +36,6 @@ catalog: "@vitejs/plugin-react": 6.0.2 unplugin-dts: 1.0.1 - packageExtensions: fdir: dependencies: From ab4f427cace34bf1903641daee3fb4a1b732cac9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 13:20:08 +0100 Subject: [PATCH 204/211] Fix playwright config --- .github/workflows/build-and-test.yaml | 2 +- modules/package.json | 4 ++-- modules/playwright.config.ts | 7 +------ 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 63c516074f..2978f58c81 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -218,7 +218,7 @@ jobs: write-cache: ${{ github.event_name != 'merge_group' }} - name: Run Playwright tests - run: pnpm test:playwright --reporter=blob + run: pnpm test:playwright working-directory: modules env: WEBAPP_PATH: ../webapp diff --git a/modules/package.json b/modules/package.json index 6e5ec59c17..40d99ccd15 100644 --- a/modules/package.json +++ b/modules/package.json @@ -5,8 +5,8 @@ "scripts": { "lint:js": "eslint --max-warnings 0 */src -c ./.eslintrc.cjs", "lint:ts": "tsc --noEmit", - "test:playwright": "playwright test -c ./playwright.config.ts", - "test:playwright:open": "yarn test:playwright -c ./playwright.config.ts --ui", + "test:playwright": "playwright test", + "test:playwright:open": "yarn test:playwright --ui", "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" }, "devDependencies": { diff --git a/modules/playwright.config.ts b/modules/playwright.config.ts index 3eab206c06..6d3d13fc81 100644 --- a/modules/playwright.config.ts +++ b/modules/playwright.config.ts @@ -91,12 +91,7 @@ export default defineConfig({ workers: 1, retries: process.env.CI ? 2 : 0, reporter: process.env.CI - ? [ - ["list"], - ["html"], - ["github"], - ["@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js"], - ] + ? [["list"], ["blob"], ["github"]] : [["list"], ["html", { outputFolder: "playwright-html-report" }]], // When running the browser in docker, set the platform to `linux` as that is the platform where the browser is running snapshotPathTemplate: `{snapshotDir}/{testFilePath}/{arg}-${process.env.PW_TEST_CONNECT_WS_ENDPOINT ? "linux" : "{platform}"}{ext}`, From c1ecf49dca86de1701834f0d802317378018754a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 13:55:23 +0100 Subject: [PATCH 205/211] Fix WEBAPP_PATH --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 2978f58c81..1af7394f93 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -221,7 +221,7 @@ jobs: run: pnpm test:playwright working-directory: modules env: - WEBAPP_PATH: ../webapp + WEBAPP_PATH: ../apps/web/webapp - name: Upload blob report to GitHub Actions Artifacts if: always() From e8244ae8a9f03d9849829e1ec64daa6db3760448 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 14:36:21 +0100 Subject: [PATCH 206/211] Add --workspace-concurrency=1 --- .github/workflows/build-and-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 1af7394f93..a8b7c0f8e8 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -209,7 +209,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build modules - run: pnpm -r --filter "./modules/**" run build + run: pnpm -r --workspace-concurrency=1 --filter "./modules/**" run build - name: Setup playwright uses: ./.github/actions/setup-playwright From a0ed801d000e3533dc36167eaf98ee5d26e44b74 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 15:06:43 +0100 Subject: [PATCH 207/211] Fix playwright modules glob --- modules/playwright.config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/playwright.config.ts b/modules/playwright.config.ts index 6d3d13fc81..faaed244e4 100644 --- a/modules/playwright.config.ts +++ b/modules/playwright.config.ts @@ -7,10 +7,13 @@ Please see LICENSE files in the repository root for full details. import { defineConfig, devices, type Project } from "@playwright/test"; import fs, { globSync } from "node:fs"; -import path from "node:path"; +import path, { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; import type { Options } from "./playwright/element-web-test.ts"; +const __dirname = dirname(fileURLToPath(import.meta.url)); + const chromeProject = { ...devices["Desktop Chrome"], channel: "chromium", @@ -42,7 +45,7 @@ playwright project. const projects: Project[] = []; // Get all the directories that hold playwright tests -const moduleTestDirectories = globSync("modules/*/e2e", {}); +const moduleTestDirectories = globSync("*/e2e", { cwd: __dirname }); // Process each directory for (const testDirectory of moduleTestDirectories) { From a16b72732ecc2124de92df5770b62959ac9e8074 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 15:46:41 +0100 Subject: [PATCH 208/211] Fix playwright modules tests --- modules/package.json | 6 +++--- modules/project.json | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 modules/project.json diff --git a/modules/package.json b/modules/package.json index 40d99ccd15..10c7caa4a0 100644 --- a/modules/package.json +++ b/modules/package.json @@ -5,9 +5,9 @@ "scripts": { "lint:js": "eslint --max-warnings 0 */src -c ./.eslintrc.cjs", "lint:ts": "tsc --noEmit", - "test:playwright": "playwright test", - "test:playwright:open": "yarn test:playwright --ui", - "test:playwright:screenshots": "playwright-screenshots yarn test:playwright --update-snapshots --grep @screenshot" + "test:playwright": "nx test:playwright --", + "test:playwright:open": "nx test:playwright -- --ui", + "test:playwright:screenshots": "nx test:playwright:screenshots --" }, "devDependencies": { "@element-hq/element-web-playwright-common": "workspace:*", diff --git a/modules/project.json b/modules/project.json new file mode 100644 index 0000000000..7f65e1cf91 --- /dev/null +++ b/modules/project.json @@ -0,0 +1,16 @@ +{ + "$schema": "../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "test:playwright": { + "command": "playwright test", + "options": { "cwd": "modules" }, + "dependsOn": ["^build:playwright"] + }, + "test:playwright:screenshots": { + "command": "playwright-screenshots playwright test --update-snapshots --grep @screenshot", + "options": { "cwd": "modules" }, + "dependsOn": ["^build:playwright"] + } + } +} From bb2c6d68ff035e33b7cefb43a374f1ae1f9b2f7e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jun 2026 18:17:37 +0100 Subject: [PATCH 209/211] Fix playwright modules tests --- .github/workflows/build-and-test.yaml | 3 --- modules/project.json | 5 +++-- modules/restricted-guests/e2e/services.ts | 20 +++++++++++++------- modules/restricted-guests/package.json | 1 + pnpm-lock.yaml | 3 +++ 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index a8b7c0f8e8..e367a64450 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -208,9 +208,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build modules - run: pnpm -r --workspace-concurrency=1 --filter "./modules/**" run build - - name: Setup playwright uses: ./.github/actions/setup-playwright with: diff --git a/modules/project.json b/modules/project.json index 7f65e1cf91..0d7a172ea8 100644 --- a/modules/project.json +++ b/modules/project.json @@ -1,16 +1,17 @@ { "$schema": "../node_modules/nx/schemas/project-schema.json", "projectType": "library", + "implicitDependencies": ["modules/*"], "targets": { "test:playwright": { "command": "playwright test", "options": { "cwd": "modules" }, - "dependsOn": ["^build:playwright"] + "dependsOn": ["^build:playwright", "^build"] }, "test:playwright:screenshots": { "command": "playwright-screenshots playwright test --update-snapshots --grep @screenshot", "options": { "cwd": "modules" }, - "dependsOn": ["^build:playwright"] + "dependsOn": ["^build:playwright", "^build"] } } } diff --git a/modules/restricted-guests/e2e/services.ts b/modules/restricted-guests/e2e/services.ts index 8122e671b3..f99b15f950 100644 --- a/modules/restricted-guests/e2e/services.ts +++ b/modules/restricted-guests/e2e/services.ts @@ -9,10 +9,8 @@ import { StartedSynapseContainer, SynapseContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; -import path, { dirname } from "node:path"; -import { fileURLToPath } from "node:url"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); +import { Readable } from "node:stream"; +import { GenericContainer, Wait } from "testcontainers"; // We use the SynapseContainer as a base to have all of its utilities for config setting export class RestrictedGuestsSynapseContainer extends SynapseContainer { @@ -21,14 +19,22 @@ export class RestrictedGuestsSynapseContainer extends SynapseContainer { } public override async start(): Promise { - this.withCopyDirectoriesToContainer([ + // Download the synapse module + const initContainer = await new GenericContainer("ghcr.io/element-hq/synapse-guest-module:861f7ee") + .withWaitStrategy(Wait.forOneShotStartup()) + .start(); + const tarStream = await initContainer.copyArchiveFromContainer(`/src/synapse_guest_module`); + await initContainer.stop(); + + this.withCopyArchivesToContainer([ { - source: path.resolve(__dirname, "..", "..", "synapse", "synapse_guest_module"), - target: "/modules/synapse_guest_module/", + tar: Readable.from(tarStream), + target: "/", }, ]).withEnvironment({ PYTHONPATH: "/modules", }); + this.config.modules.push({ module: "synapse_guest_module.GuestModule", config: this.getModuleConfig(), diff --git a/modules/restricted-guests/package.json b/modules/restricted-guests/package.json index 842dbf10cc..1b84b13fe8 100644 --- a/modules/restricted-guests/package.json +++ b/modules/restricted-guests/package.json @@ -16,6 +16,7 @@ "@types/react": "catalog:", "@vitejs/plugin-react": "catalog:", "react": "catalog:", + "testcontainers": "^12.0.1", "typescript": "catalog:", "vite": "catalog:", "vite-plugin-node-polyfills": "catalog:" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0cba11811..6c6795a1ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1219,6 +1219,9 @@ importers: react: specifier: 'catalog:' version: 19.2.6 + testcontainers: + specifier: ^12.0.1 + version: 12.0.1 typescript: specifier: 'catalog:' version: 6.0.3 From 4b72f03bee8024fc160fc4ba6ec39694f3e5c229 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Jun 2026 09:39:46 +0100 Subject: [PATCH 210/211] Update screenshots --- .../dialog-linux.png | Bin 11953 -> 11998 bytes .../dialog-login-linux.png | Bin 9591 -> 9625 bytes .../preview-bar-linux.png | Bin 5173 -> 5164 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png index 2f8c5f8ca3a4de4b6a086e47a68092aefbf9a6da..89a414827cb46dacd93be4db37e7b775f3a2e8b8 100644 GIT binary patch literal 11998 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 diff --git a/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-login-linux.png b/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-login-linux.png index 336427c53708a352cb16c26c70a4b39301c9cfe9..183400e78ecbd69a805c12812d7cf25ce3aeb28f 100644 GIT binary patch literal 9625 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 aD4?y_fmh@{sK$su$;+s`t&%eP`u_k2+TKn8 literal 9591 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 aR09C1g;>3&P5kEo00007Qif}nN!#<#s@tzk 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 Date: Wed, 10 Jun 2026 10:48:44 +0100 Subject: [PATCH 211/211] Update screenshots --- .../dialog-linux.png | Bin 11998 -> 11953 bytes .../dialog-login-linux.png | Bin 9625 -> 9591 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/e2e/snapshots/restricted-guests.spec.ts/dialog-linux.png index 89a414827cb46dacd93be4db37e7b775f3a2e8b8..2f8c5f8ca3a4de4b6a086e47a68092aefbf9a6da 100644 GIT binary patch literal 11953 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 literal 11998 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 aD4?y_fmh@{sK$su$;+s`t&%eP`u_k2+TKn8
  • e zAU00t7#JMV6}<(*ETygW=jqBcz%tk(Qy@$L zXo_f7-opbnzt#i0(uuPFn4bO%*xBsf{nBs8KZuOn+$Xi`hP}_iU{_jKcoOV*igbmn zRS#hK!_;H{txkCDFxbHye)SME0$?$0Ut9r(u-6so_wPRdJCA+)_Fez=0L#}-WczfE zsHolDogKS&?YQ-i#_aIL?IZVx6t_9++5Fvi@*3~+sq6NwhLS?TBJLv%{>+ov8IW( z>>*S?b@2JjOiRlzFZ`cr8+_^@FHciYaQvUSPx~+|D+&3Jt-SyKb?ex;ePSZyKDLsO z@S04Tn3ndl@W)oRo@JkVdie0+g9i^;^|`0Ld-uSB!SLsv!h~7E-v9sr|Njk#2%`W1 f00v1!K~w_(C<+;IVsU}n00000NkvXXu0mjf!?B4Q From f99db77b34b6363fa4473ee3080083444f622be6 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 18 Dec 2025 18:06:58 +0100 Subject: [PATCH 090/211] style: set font for restricted guest dialog --- .../element-web/src/RegisterDialog.tsx | 11 +++++++++-- .../dialog-linux.png | Bin 13313 -> 13444 bytes 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/element-web/src/RegisterDialog.tsx b/modules/restricted-guests/element-web/src/RegisterDialog.tsx index 504f266600..1c65708b03 100644 --- a/modules/restricted-guests/element-web/src/RegisterDialog.tsx +++ b/modules/restricted-guests/element-web/src/RegisterDialog.tsx @@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details. import { type FC, useState, type JSX, type FormEvent } from "react"; import { type Api, type AccountAuthInfo, type DialogProps } from "@element-hq/element-web-module-api"; import { Form } from "@vector-im/compound-web"; +import styled from "styled-components"; import { type ModuleConfig } from "./config.ts"; @@ -22,6 +23,12 @@ const enum State { Error, } +const StyledFormRoot = styled(Form.Root)` + font: var(--cpd-font-body-md-regular); + letter-spacing: var(--cpd-font-letter-spacing-body-md); + font-feature-settings: normal; +`; + const RegisterDialog: FC = ({ api, config, onCancel, onSubmit }) => { const [username, setUsername] = useState(""); const [state, setState] = useState(State.Idle); @@ -61,7 +68,7 @@ const RegisterDialog: FC = ({ api, config, onCancel, onSubm const disabled = state !== State.Idle; return ( - + {api.i18n.translate("register_dialog_register_username_label")} = ({ api, config, onCancel, onSubm {api.i18n.translate("register_dialog_continue_label")} - + ); }; diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png index b95326afceafe9bc2cf2b42f53ecb0b49b9c5ece..f053bea1d844e988dc8099ea411d7d0acf64d2d4 100644 GIT binary patch literal 13444 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 DP%D7P literal 13313 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 Date: Thu, 15 Jan 2026 11:25:02 +0000 Subject: [PATCH 091/211] Unbreak CI: pin prometheus-client dep --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 4 ++++ modules/restricted-guests/synapse/tox.ini | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index 23adf1e03b..fee32d2fa1 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -20,6 +20,10 @@ venv_path=${repo_root:-./}.venv if [ ! -d "$venv_path" ]; then "python${PYTHON_VERSION:-3}" -m venv "$venv_path" "$venv_path/bin/pip" install -e '.[dev]' + + # Temp: downgrade prometheus-client until Synapse is compatible with 0.24.0+ + # See https://github.com/element-hq/synapse/issues/19375 + "$venv_path/bin/pip" install --force prometheus-client==0.23.1 fi [ $# -gt 1 ] && . "$venv_path/bin/activate" && "$@" diff --git a/modules/restricted-guests/synapse/tox.ini b/modules/restricted-guests/synapse/tox.ini index 0da1d491aa..33f6df7e3c 100644 --- a/modules/restricted-guests/synapse/tox.ini +++ b/modules/restricted-guests/synapse/tox.ini @@ -6,6 +6,11 @@ isolated_build = true [testenv:py] +deps = + # Temp: downgrade prometheus-client until Synapse is compatible with 0.24.0+ + # See https://github.com/element-hq/synapse/issues/19375 + prometheus-client==0.23.1 + extras = dev commands = From f117e745d509c0f234879697866d57d4ab48a376 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Thu, 15 Jan 2026 11:45:41 +0000 Subject: [PATCH 092/211] Remove superfluous pin The tox venv is what actually matters here. --- modules/restricted-guests/synapse/scripts/run_in_venv.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/restricted-guests/synapse/scripts/run_in_venv.sh b/modules/restricted-guests/synapse/scripts/run_in_venv.sh index fee32d2fa1..23adf1e03b 100755 --- a/modules/restricted-guests/synapse/scripts/run_in_venv.sh +++ b/modules/restricted-guests/synapse/scripts/run_in_venv.sh @@ -20,10 +20,6 @@ venv_path=${repo_root:-./}.venv if [ ! -d "$venv_path" ]; then "python${PYTHON_VERSION:-3}" -m venv "$venv_path" "$venv_path/bin/pip" install -e '.[dev]' - - # Temp: downgrade prometheus-client until Synapse is compatible with 0.24.0+ - # See https://github.com/element-hq/synapse/issues/19375 - "$venv_path/bin/pip" install --force prometheus-client==0.23.1 fi [ $# -gt 1 ] && . "$venv_path/bin/activate" && "$@" From 1cb888e4e726a34cbbcb5d8c9c5c38461ae4902a Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 15:45:46 +0000 Subject: [PATCH 093/211] Configuration options for MAS In order to create and manage users with MAS enabled, we'll need to reach out to the MAS admin API. We can do so automatically by requesting an admin-enabled token, assuming a matching client has been configured on the MAS side. Add some config options for the guest module (OAuth2 client) side. --- modules/restricted-guests/synapse/README.md | 42 ++++++++++++++++++ .../synapse/synapse_guest_module/config.py | 12 +++++ .../synapse_guest_module/guest_module.py | 44 +++++++++++++++++-- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index 11fbc70afe..e3ed2ccac8 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -41,6 +41,17 @@ The module provides (optional) configuration options: - `enable_user_reaper` - if true, the module disables all users that are older than the configured expiration time. Default: `true`. - `user_expiration_seconds` - the expiration time in seconds when a guest user expires after their creation. Default: `86400` (=24 hours). +If matrix-authentication-service (MAS) is configured, the module will need to +interface with it in order to register/deactivate users. Provide the below +options in order to give the module access to [MAS' Admin +API](https://element-hq.github.io/matrix-authentication-service/topics/admin-api.html). + +- `mas` - optional configuration for Matrix Authentication Service (MAS). When set, the module creates users via MAS' admin API. + - `admin_api_base_url` - Base URL for MAS' admin API (e.g. `https://mas.example.org`). Trailing slashes will be automatically stripped. + - `oauth_base_url` - Base URL for MAS' OAuth endpoints (defaults to `admin_api_base_url` if not set). Trailing slashes will be automatically stripped. + - `client_id` - client ID for the automated tool. Must be a valid [ULID](https://github.com/ulid/spec). Generate one [here](https://ulidtools.com/). + - `client_secret` - client secret for the automated tool. Ideally long and cryptographically secure. Keep it a secret! + Example configuration: ```yaml @@ -49,6 +60,37 @@ modules: config: # Use a german suffix display_name_suffix: " (Gast)" + # The below is required if using MAS + mas: + admin_api_base_url: https://mas.example.org + oauth_base_url: https://mas.example.org + # The `client_id` must be a valid ULID: + # https://github.com/ulid/spec + # Generate ULID's easily at: + # https://ulidtools.com/ + client_id: 000000000000000000000G0EST + client_secret: your-client-secret +``` + +Enable [the Admin API on a MAS +listener](https://element-hq.github.io/matrix-authentication-service/topics/admin-api.html#enabling-the-api). +Then, add the following to your MAS config file: + +```yaml +policy: + data: + admin_clients: + - 000000000000000000000G0EST + +# ... + +clients: + # The `client_id` must be a valid ULID https://github.com/ulid/spec + # Generate ULID's easily at: https://ulidtools.com/ + - client_id: 000000000000000000000G0EST + # The guest module uses the client_secret_basic authentication method. + client_auth_method: client_secret_basic + client_secret: your-client-secret ``` ## Production installation diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index a0ddbe569a..5e54f2f642 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -7,12 +7,24 @@ # Originally licensed under the Apache License, Version 2.0: # . +from typing import Optional + import attr +@attr.s(frozen=True, auto_attribs=True) +class MasConfig: + admin_api_base_url: str + oauth_base_url: str + client_id: str + # TODO: Add a filepath option for the secret as well. + client_secret: str + + @attr.s(frozen=True, auto_attribs=True) class GuestModuleConfig: user_id_prefix: str display_name_suffix: str enable_user_reaper: bool user_expiration_seconds: int + mas: Optional[MasConfig] = None diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 156012c35d..174c3474f6 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -8,7 +8,7 @@ # . import logging -from typing import Any, Dict, Literal, Tuple, Union +from typing import Any, Dict, Literal, Optional, Tuple, Union from synapse.module_api import ( NOT_SPAM, @@ -21,7 +21,8 @@ from synapse.module_api import ( from synapse.module_api.errors import ConfigError from synapse.types import UserID -from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.config import GuestModuleConfig, MasConfig +from synapse_guest_module.mas_admin_client import MasAdminClient from synapse_guest_module.guest_registration_servlet import GuestRegistrationServlet from synapse_guest_module.guest_user_reaper import GuestUserReaper @@ -33,7 +34,12 @@ class GuestModule: self._api = api self._config = config - self.registration_servlet = GuestRegistrationServlet(config, api) + mas_admin_client = ( + MasAdminClient(api, config.mas) if config.mas is not None else None + ) + self.registration_servlet = GuestRegistrationServlet( + config, api, mas_admin_client + ) self._api.register_web_resource( "/_synapse/client/register_guest", self.registration_servlet ) @@ -81,11 +87,43 @@ class GuestModule: "Config option 'user_expiration_seconds' must be a number" ) + mas_config = config.get("mas") + mas: Optional[MasConfig] = None + if mas_config is not None: + if not isinstance(mas_config, dict): + raise ConfigError("Config option 'mas' must be an object") + + admin_api_base_url = mas_config.get("admin_api_base_url") + if not isinstance(admin_api_base_url, str) or len(admin_api_base_url.strip()) == 0: + raise ConfigError("Config option 'mas.admin_api_base_url' is required and must be a string") + + oauth_base_url = mas_config.get("oauth_base_url", admin_api_base_url) + if not isinstance(oauth_base_url, str) or len(oauth_base_url.strip()) == 0: + raise ConfigError( + "Config option 'mas.oauth_base_url' must be a string" + ) + + client_id = mas_config.get("client_id") + if not isinstance(client_id, str) or len(client_id.strip()) == 0: + raise ConfigError("Config option 'mas.client_id' is required and must be a string") + + client_secret = mas_config.get("client_secret") + if not isinstance(client_secret, str) or len(client_secret.strip()) == 0: + raise ConfigError("Config option 'mas.client_secret' is required and must be a string") + + mas = MasConfig( + admin_api_base_url.strip(), + oauth_base_url.strip(), + client_id.strip(), + client_secret.strip(), + ) + return GuestModuleConfig( user_id_prefix, display_name_suffix, enable_user_reaper, user_expiration_seconds, + mas, ) async def profile_update( From 04414e06a9bd18453e8321fb626682c9858e4f0e Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 15:46:49 +0000 Subject: [PATCH 094/211] Add `MasAdminClient` class A class to request OAuth2 tokens from MAS, and create users using them. Requires data from the config options we defined. --- .../synapse_guest_module/mas_admin_client.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py new file mode 100644 index 0000000000..50fd38dae5 --- /dev/null +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -0,0 +1,78 @@ +# Copyright 2025 New Vector Ltd. +# +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the project root for full details. + +import base64 +import logging +from typing import Any, Dict + +from synapse.module_api import ModuleApi + +from synapse_guest_module.config import MasConfig + +logger = logging.getLogger("synapse.contrib." + __name__) + + +class MasAdminClient: + def __init__(self, api: ModuleApi, config: MasConfig): + self._api = api + self._config = config + # Strip trailing any slashes if present + self._admin_api_base_url = config.admin_api_base_url.rstrip("/") + self._oauth_base_url = config.oauth_base_url.rstrip("/") + + async def create_user(self, username: str) -> None: + token = await self.request_admin_token() + url = self._build_admin_url("/api/admin/v1/users") + + await self._api.http_client.post_json_get_json( + uri=url, + post_json={"username": username}, + headers={"Authorization": [f"Bearer {token}"]}, + ) + + async def request_admin_token(self) -> str: + url = self._build_oauth_url("/oauth2/token") + basic_auth = base64.b64encode( + f"{self._config.client_id}:{self._config.client_secret}".encode("utf-8") + ).decode("ascii") + headers = { + "Authorization": [f"Basic {basic_auth}"], + "Content-Type": ["application/x-www-form-urlencoded"], + } + data = { + "grant_type": "client_credentials", + "scope": "urn:mas:admin", + } + + response = await self._post_urlencoded_get_json(url, data, headers) + access_token = response.get("access_token") + if not isinstance(access_token, str) or len(access_token) == 0: + raise ValueError("MAS token response missing access_token") + return access_token + + async def _post_urlencoded_get_json( + self, url: str, data: Dict[str, str], headers: Dict[str, Any] + ) -> Any: + http_client = self._api.http_client + post_urlencoded = getattr(http_client, "post_urlencoded_get_json", None) + if callable(post_urlencoded): + return await post_urlencoded(url, data, headers=headers) + + logger.debug( + "MAS client falling back to post_json_get_json for %s", url + ) + return await http_client.post_json_get_json( + uri=url, post_json=data, headers=headers + ) + + def _build_admin_url(self, path: str) -> str: + if not path.startswith("/"): + path = "/" + path + return f"{self._admin_api_base_url}{path}" + + def _build_oauth_url(self, path: str) -> str: + if not path.startswith("/"): + path = "/" + path + return f"{self._oauth_base_url}{path}" From 9c2b9463a9ae39734647f9457a654f8fe33d1031 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 15:47:21 +0000 Subject: [PATCH 095/211] Wire the new `MasAdminClient` in If `mas` is defined in the config, then use it to create users instead of Synapse's module API. --- .../guest_registration_servlet.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 6cdb382831..f60ce87b1f 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -17,9 +17,11 @@ from synapse.module_api import ( ModuleApi, parse_json_object_from_request, ) +from synapse.types import UserID from twisted.web.server import Request from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.mas_admin_client import MasAdminClient logger = logging.getLogger("synapse.contrib." + __name__) @@ -35,10 +37,12 @@ class GuestRegistrationServlet(DirectServeJsonResource): self, config: GuestModuleConfig, api: ModuleApi, + mas_admin_client: MasAdminClient | None = None, ): super().__init__() self._api = api self._config = config + self._mas_admin_client = mas_admin_client async def _async_render_POST(self, request: Request) -> Tuple[int, Dict[str, Any]]: """On POST requests, generate a new username for a guest, check that it @@ -51,6 +55,8 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname = json_dict.get("displayname") if not isinstance(displayname, str) or len(displayname.strip()) == 0: return 400, {"msg": "You must provide a 'displayname' as a string"} + + displayname = displayname.strip() # make sure the regex is unique for _ in range(10): @@ -67,14 +73,25 @@ class GuestRegistrationServlet(DirectServeJsonResource): ): continue - logger.info("Register guest with user %s", localpart) - user_id = await self._api.register_user( - localpart, displayname.strip() + self._config.display_name_suffix - ) + if self._mas_admin_client is None: + logger.info("Registering local Synapse guest user with localpart '%s'", localpart) + user_id = await self._api.register_user( + localpart, displayname + self._config.display_name_suffix + ) + else: + logger.info("Registering MAS guest user with username '%s'", localpart) + await self._mas_admin_client.create_user(localpart) + + user_id = self._api.get_qualified_user_id(localpart) + + await self._api.set_displayname( + UserID.from_string(user_id), + displayname + self._config.display_name_suffix, + ) device_id, access_token, _, _ = await self._api.register_device(user_id) - logger.debug("Registered user %s", user_id) + logger.debug("Registered user '%s'", user_id) res = { "userId": user_id, From 801d28f3ca4a581966510e4d343f0c9f19704d6e Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 16:38:35 +0000 Subject: [PATCH 096/211] Create a personal session on MAS In order to get an access token for a user, one needs to create a personal session on MAS. We now do so, and extract the access token and device ID from the response. TODO: We're handing back the MAS user ID as the device ID. Is that correct? --- .../guest_registration_servlet.py | 26 ++++++++-- .../synapse_guest_module/mas_admin_client.py | 50 ++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index f60ce87b1f..41e2be9ed6 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -74,13 +74,20 @@ class GuestRegistrationServlet(DirectServeJsonResource): continue if self._mas_admin_client is None: - logger.info("Registering local Synapse guest user with localpart '%s'", localpart) + logger.info( + "Registering local Synapse guest user with localpart '%s'", + localpart, + ) user_id = await self._api.register_user( localpart, displayname + self._config.display_name_suffix ) + + device_id, access_token, _, _ = await self._api.register_device( + user_id + ) else: logger.info("Registering MAS guest user with username '%s'", localpart) - await self._mas_admin_client.create_user(localpart) + mas_user_id = await self._mas_admin_client.create_user(localpart) user_id = self._api.get_qualified_user_id(localpart) @@ -89,7 +96,20 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname + self._config.display_name_suffix, ) - device_id, access_token, _, _ = await self._api.register_device(user_id) + # Determine how long to keep the access token valid for. + # + # If a user reaper is enabled, just have the token expire after + # the configured period. + expires_in = ( + self._config.user_expiration_seconds + if self._config.enable_user_reaper + else 0 + ) + device_id, access_token = ( + await self._mas_admin_client.create_personal_session( + mas_user_id, expires_in + ) + ) logger.debug("Registered user '%s'", user_id) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 50fd38dae5..286a2e2f12 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -22,15 +22,61 @@ class MasAdminClient: self._admin_api_base_url = config.admin_api_base_url.rstrip("/") self._oauth_base_url = config.oauth_base_url.rstrip("/") - async def create_user(self, username: str) -> None: + async def create_user(self, username: str) -> str: + """Creates a new user in MAS with the given username. + + Args: + username: The username (localpart) of the user to create. + + Returns: + The MAS ID of the created user. + """ token = await self.request_admin_token() url = self._build_admin_url("/api/admin/v1/users") - await self._api.http_client.post_json_get_json( + response = await self._api.http_client.post_json_get_json( uri=url, post_json={"username": username}, headers={"Authorization": [f"Bearer {token}"]}, ) + + mas_user_id = response.get("data", {}).get("id") + if mas_user_id is None or not isinstance(mas_user_id, str): + raise ValueError("MAS user creation response missing `data.id` field") + + return mas_user_id + + async def create_personal_session( + self, mas_user_id: str, expires_in: int + ) -> tuple[str, str]: + token = await self.request_admin_token() + url = self._build_admin_url("/api/admin/v1/personal-sessions") + + request_body = { + "actor_user_id": mas_user_id, + "expires_in": expires_in, + "scope": "openid urn:matrix:client:api:*", + "human_name": "guest user", + } + + response = await self._api.http_client.post_json_get_json( + uri=url, + post_json=request_body, + headers={"Authorization": [f"Bearer {token}"]}, + ) + + data = response.get("data", {}) + attributes = data.get("attributes", {}) if isinstance(data, dict) else {} + access_token = attributes.get("access_token") + # TODO: Is this the correct device ID? + device_id = data.get("id") if isinstance(data, dict) else None + + if not isinstance(access_token, str) or len(access_token) == 0: + raise ValueError("MAS session response missing `access_token` field") + if not isinstance(device_id, str) or len(device_id) == 0: + raise ValueError("MAS session response missing device id") + + return device_id, access_token async def request_admin_token(self) -> str: url = self._build_oauth_url("/oauth2/token") From d0e17ae274054734648a5a4b112e23fdb4b612a1 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 16:59:05 +0000 Subject: [PATCH 097/211] Add the ability to deactivate users in the MAS client As we won't be able to deactivate them using the Synapse module API --- .../synapse/synapse_guest_module/mas_admin_client.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 286a2e2f12..299481da68 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -78,6 +78,17 @@ class MasAdminClient: return device_id, access_token + async def deactivate_user(self, mas_user_id: str, token: str | None = None) -> None: + if token is None: + token = await self.request_admin_token() + + url = self._build_admin_url(f"/api/admin/v1/users/{mas_user_id}/deactivate") + await self._api.http_client.post_json_get_json( + uri=url, + post_json={"skip_erase": True}, + headers={"Authorization": [f"Bearer {token}"]}, + ) + async def request_admin_token(self) -> str: url = self._build_oauth_url("/oauth2/token") basic_auth = base64.b64encode( From 9f4ca6f7e6fbfda209f650a701e2b46182951f52 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 14 Jan 2026 17:00:38 +0000 Subject: [PATCH 098/211] Store registered users in a namespaced table, deactivate upon expiry To deactivate users, we need their `actor_user_id` (MAS-specific). I don't believe there's a way to get this from Synapse. So, we store user's detailed in a namespaced table upon registering them, along with the creation timestamp, and deactivate them once they're considered expired. --- .../synapse_guest_module/guest_module.py | 49 +++++++++++++- .../guest_registration_servlet.py | 27 +++++++- .../synapse_guest_module/guest_user_reaper.py | 64 ++++++++++++++++++- 3 files changed, 136 insertions(+), 4 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 174c3474f6..3fd463fb98 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -7,11 +7,13 @@ # Originally licensed under the Apache License, Version 2.0: # . +import asyncio import logging from typing import Any, Dict, Literal, Optional, Tuple, Union from synapse.module_api import ( NOT_SPAM, + LoggingTransaction, ModuleApi, ProfileInfo, UserProfile, @@ -33,12 +35,20 @@ class GuestModule: def __init__(self, config: GuestModuleConfig, api: ModuleApi): self._api = api self._config = config + self._mas_tables_ready: asyncio.Event | None = None mas_admin_client = ( MasAdminClient(api, config.mas) if config.mas is not None else None ) + if config.mas is not None: + self._mas_tables_ready = asyncio.Event() + run_as_background_process( + "guest_module_mas_db_init", + self._init_mas_tables, + bg_start_span=False, + ) self.registration_servlet = GuestRegistrationServlet( - config, api, mas_admin_client + config, api, mas_admin_client, self._mas_tables_ready ) self._api.register_web_resource( "/_synapse/client/register_guest", self.registration_servlet @@ -54,7 +64,9 @@ class GuestModule: ) # Start the user reaper - self.reaper = GuestUserReaper(api, config) + self.reaper = GuestUserReaper( + api, config, mas_admin_client, self._mas_tables_ready + ) if config.enable_user_reaper: run_as_background_process( "guest_module_reaper_bg_task", @@ -152,6 +164,39 @@ class GuestModule: ) await self._api.set_displayname(user_id_1, guest_display_name) + async def _init_mas_tables(self) -> None: + if self._mas_tables_ready is None: + return + + try: + await self._api.run_db_interaction( + "guest_module_create_mas_tables", + self._create_mas_tables, + ) + except Exception as err: + logger.error("Failed to initialize MAS tables: %s", err) + finally: + self._mas_tables_ready.set() + + @staticmethod + def _create_mas_tables(txn: LoggingTransaction) -> None: + txn.execute( + """ + CREATE TABLE IF NOT EXISTS guest_module_mas_users ( + mas_user_id TEXT PRIMARY KEY, + created_at BIGINT NOT NULL + ) + """, + (), + ) + txn.execute( + """ + CREATE INDEX IF NOT EXISTS guest_module_mas_users_created_at + ON guest_module_mas_users (created_at) + """, + (), + ) + async def callback_user_may_create_room( self, user_id: str, diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 41e2be9ed6..a0ac7fceae 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -7,12 +7,15 @@ # Originally licensed under the Apache License, Version 2.0: # . +import asyncio import logging import secrets import string +import time from typing import Any, Dict, Tuple from synapse.module_api import ( + DatabasePool, DirectServeJsonResource, ModuleApi, parse_json_object_from_request, @@ -38,11 +41,13 @@ class GuestRegistrationServlet(DirectServeJsonResource): config: GuestModuleConfig, api: ModuleApi, mas_admin_client: MasAdminClient | None = None, + mas_tables_ready: asyncio.Event | None = None, ): super().__init__() self._api = api self._config = config self._mas_admin_client = mas_admin_client + self._mas_tables_ready = mas_tables_ready async def _async_render_POST(self, request: Request) -> Tuple[int, Dict[str, Any]]: """On POST requests, generate a new username for a guest, check that it @@ -55,7 +60,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname = json_dict.get("displayname") if not isinstance(displayname, str) or len(displayname.strip()) == 0: return 400, {"msg": "You must provide a 'displayname' as a string"} - + displayname = displayname.strip() # make sure the regex is unique @@ -96,6 +101,8 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname + self._config.display_name_suffix, ) + await self._store_mas_user(mas_user_id, int(time.time())) + # Determine how long to keep the access token valid for. # # If a user reaper is enabled, just have the token expire after @@ -123,3 +130,21 @@ class GuestRegistrationServlet(DirectServeJsonResource): return 201, res return 500, {"msg": "Internal error: Could not find a free username"} + + async def _store_mas_user(self, mas_user_id: str, created_at: int) -> None: + if self._mas_tables_ready is not None: + await self._mas_tables_ready.wait() + + def store_user(txn: Any) -> None: + DatabasePool.simple_insert_txn( + txn, + table="guest_module_mas_users", + values={ + "mas_user_id": mas_user_id, + "created_at": created_at, + }, + ) + + await self._api.run_db_interaction( + "guest_module_store_mas_user", store_user + ) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index 7b3f2c7288..2c75ccec34 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -7,6 +7,7 @@ # Originally licensed under the Apache License, Version 2.0: # . +import asyncio import logging import time from typing import List @@ -14,14 +15,23 @@ from typing import List from synapse.module_api import DatabasePool, LoggingTransaction, ModuleApi from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.mas_admin_client import MasAdminClient logger = logging.getLogger("synapse.contrib." + __name__) class GuestUserReaper: - def __init__(self, api: ModuleApi, config: GuestModuleConfig): + def __init__( + self, + api: ModuleApi, + config: GuestModuleConfig, + mas_admin_client: MasAdminClient | None = None, + mas_tables_ready: asyncio.Event | None = None, + ): self._api = api self._config = config + self._mas_admin_client = mas_admin_client + self._mas_tables_ready = mas_tables_ready self.reaper_user = f"{config.user_id_prefix}reaper" async def run(self) -> None: @@ -43,6 +53,9 @@ class GuestUserReaper: """Deactivate all users that are older than the specified expiration interval. This uses the admin API to disable the user. """ + if self._mas_admin_client is not None: + await self._deactivate_expired_mas_users() + return def get_expired_users(txn: LoggingTransaction) -> List[str]: sql = """ @@ -92,6 +105,55 @@ class GuestUserReaper: except Exception as e: logger.error('Failed to delete user "%s": %s', user_id, e) + async def _deactivate_expired_mas_users(self) -> None: + if self._mas_tables_ready is not None: + await self._mas_tables_ready.wait() + + def get_expired_users(txn: LoggingTransaction) -> List[str]: + expire_ts_seconds = int(time.time() - self._config.user_expiration_seconds) + txn.execute( + """ + SELECT mas_user_id + FROM guest_module_mas_users + WHERE created_at < ? + """, + (expire_ts_seconds,), + ) + expired_users_rows = txn.fetchall() + + return [row[0] for row in expired_users_rows] + + expired_users: List[str] = await self._api.run_db_interaction( + "guest_module_get_expired_mas_users", + get_expired_users, + ) + + if len(expired_users) == 0: + return + + logger.info("Deactivating %d expired MAS users", len(expired_users)) + + token = await self._mas_admin_client.request_admin_token() + + for mas_user_id in expired_users: + try: + await self._mas_admin_client.deactivate_user(mas_user_id, token) + await self._remove_mas_user(mas_user_id) + except Exception as e: + logger.error('Failed to deactivate MAS user "%s": %s', mas_user_id, e) + + async def _remove_mas_user(self, mas_user_id: str) -> None: + def delete_user(txn: LoggingTransaction) -> None: + txn.execute( + "DELETE FROM guest_module_mas_users WHERE mas_user_id = ?", + (mas_user_id,), + ) + + await self._api.run_db_interaction( + "guest_module_delete_mas_user", + delete_user, + ) + async def get_admin_token(self) -> str: """Create a new admin user in synapse so the module can call the admin api. If no user or login session exists, we create new ones. From d45fd845b3e10bad90b07eb3912f2f36a4aca11f Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 16 Jan 2026 16:37:11 +0000 Subject: [PATCH 099/211] Add `client_secret_filepath` option To allow supplying the `client_secret` in a file, instead of directly in the configuration. I've found that ESS prefer this method in the past, so the entire config does not need to be a secret. --- modules/restricted-guests/synapse/README.md | 3 ++ .../synapse/synapse_guest_module/config.py | 4 +-- .../synapse_guest_module/guest_module.py | 29 ++++++++++++++-- .../synapse_guest_module/mas_admin_client.py | 34 +++++++++++++++++-- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index e3ed2ccac8..c03c874865 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -51,6 +51,7 @@ API](https://element-hq.github.io/matrix-authentication-service/topics/admin-api - `oauth_base_url` - Base URL for MAS' OAuth endpoints (defaults to `admin_api_base_url` if not set). Trailing slashes will be automatically stripped. - `client_id` - client ID for the automated tool. Must be a valid [ULID](https://github.com/ulid/spec). Generate one [here](https://ulidtools.com/). - `client_secret` - client secret for the automated tool. Ideally long and cryptographically secure. Keep it a secret! + - `client_secret_filepath` - path to a plaintext file containing the client secret. If set, this is used instead of `client_secret`. Example configuration: @@ -70,6 +71,8 @@ modules: # https://ulidtools.com/ client_id: 000000000000000000000G0EST client_secret: your-client-secret + # Alternatively, load the secret from a file: + # client_secret_filepath: /run/secrets/mas-client-secret ``` Enable [the Admin API on a MAS diff --git a/modules/restricted-guests/synapse/synapse_guest_module/config.py b/modules/restricted-guests/synapse/synapse_guest_module/config.py index 5e54f2f642..7841d1433e 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/config.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/config.py @@ -17,8 +17,8 @@ class MasConfig: admin_api_base_url: str oauth_base_url: str client_id: str - # TODO: Add a filepath option for the secret as well. - client_secret: str + client_secret: Optional[str] = None + client_secret_filepath: Optional[str] = None @attr.s(frozen=True, auto_attribs=True) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 3fd463fb98..525482294b 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -120,14 +120,37 @@ class GuestModule: raise ConfigError("Config option 'mas.client_id' is required and must be a string") client_secret = mas_config.get("client_secret") - if not isinstance(client_secret, str) or len(client_secret.strip()) == 0: - raise ConfigError("Config option 'mas.client_secret' is required and must be a string") + if client_secret is not None: + if not isinstance(client_secret, str) or len(client_secret.strip()) == 0: + raise ConfigError( + "Config option 'mas.client_secret' must be a string" + ) + client_secret = client_secret.strip() + + client_secret_filepath = mas_config.get("client_secret_filepath") + if client_secret_filepath is not None: + if not isinstance(client_secret_filepath, str) or len(client_secret_filepath.strip()) == 0: + raise ConfigError( + "Config option 'mas.client_secret_filepath' must be a string" + ) + client_secret_filepath = client_secret_filepath.strip() + + if client_secret is None and client_secret_filepath is None: + raise ConfigError( + "Config option 'mas.client_secret' or 'mas.client_secret_filepath' is required" + ) + + if client_secret is not None and client_secret_filepath is not None: + raise ConfigError( + "Config option 'mas.client_secret' and 'mas.client_secret_filepath' are mutually exclusive" + ) mas = MasConfig( admin_api_base_url.strip(), oauth_base_url.strip(), client_id.strip(), - client_secret.strip(), + client_secret, + client_secret_filepath, ) return GuestModuleConfig( diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 299481da68..a650450531 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -18,9 +18,10 @@ class MasAdminClient: def __init__(self, api: ModuleApi, config: MasConfig): self._api = api self._config = config - # Strip trailing any slashes if present + # Strip any trailing slashes if present self._admin_api_base_url = config.admin_api_base_url.rstrip("/") self._oauth_base_url = config.oauth_base_url.rstrip("/") + self._client_secret = self._load_client_secret() async def create_user(self, username: str) -> str: """Creates a new user in MAS with the given username. @@ -56,7 +57,7 @@ class MasAdminClient: "actor_user_id": mas_user_id, "expires_in": expires_in, "scope": "openid urn:matrix:client:api:*", - "human_name": "guest user", + "human_name": "guest session", } response = await self._api.http_client.post_json_get_json( @@ -92,7 +93,7 @@ class MasAdminClient: async def request_admin_token(self) -> str: url = self._build_oauth_url("/oauth2/token") basic_auth = base64.b64encode( - f"{self._config.client_id}:{self._config.client_secret}".encode("utf-8") + f"{self._config.client_id}:{self._client_secret}".encode("utf-8") ).decode("ascii") headers = { "Authorization": [f"Basic {basic_auth}"], @@ -108,6 +109,33 @@ class MasAdminClient: if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS token response missing access_token") return access_token + + def _load_client_secret(self) -> str: + """Source the MAS client secret from either configuration or a file.""" + if self._config.client_secret_filepath is not None: + try: + with open( + self._config.client_secret_filepath, "r", encoding="utf-8" + ) as secret_file: + client_secret = secret_file.read().strip() + except Exception as err: + raise ValueError( + f"Failed to read MAS client secret file: {err}" + ) from err + + if len(client_secret) == 0: + raise ValueError("MAS client secret file is empty") + + return client_secret + + if self._config.client_secret is None: + raise ValueError("MAS client secret is not configured") + + client_secret = self._config.client_secret.strip() + if len(client_secret) == 0: + raise ValueError("MAS client secret is empty") + + return client_secret async def _post_urlencoded_get_json( self, url: str, data: Dict[str, str], headers: Dict[str, Any] From 2aa16a8222ca3636810b71450699aba05dd81895 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 16 Jan 2026 16:49:12 +0000 Subject: [PATCH 100/211] lint --- modules/restricted-guests/synapse/README.md | 28 ++++++++--------- .../synapse_guest_module/guest_module.py | 31 +++++++++++++------ .../guest_registration_servlet.py | 17 +++++----- .../synapse_guest_module/guest_user_reaper.py | 6 ++++ .../synapse_guest_module/mas_admin_client.py | 20 ++++++------ 5 files changed, 58 insertions(+), 44 deletions(-) diff --git a/modules/restricted-guests/synapse/README.md b/modules/restricted-guests/synapse/README.md index c03c874865..4cf5c743ce 100644 --- a/modules/restricted-guests/synapse/README.md +++ b/modules/restricted-guests/synapse/README.md @@ -47,11 +47,11 @@ options in order to give the module access to [MAS' Admin API](https://element-hq.github.io/matrix-authentication-service/topics/admin-api.html). - `mas` - optional configuration for Matrix Authentication Service (MAS). When set, the module creates users via MAS' admin API. - - `admin_api_base_url` - Base URL for MAS' admin API (e.g. `https://mas.example.org`). Trailing slashes will be automatically stripped. - - `oauth_base_url` - Base URL for MAS' OAuth endpoints (defaults to `admin_api_base_url` if not set). Trailing slashes will be automatically stripped. - - `client_id` - client ID for the automated tool. Must be a valid [ULID](https://github.com/ulid/spec). Generate one [here](https://ulidtools.com/). - - `client_secret` - client secret for the automated tool. Ideally long and cryptographically secure. Keep it a secret! - - `client_secret_filepath` - path to a plaintext file containing the client secret. If set, this is used instead of `client_secret`. + - `admin_api_base_url` - Base URL for MAS' admin API (e.g. `https://mas.example.org`). Trailing slashes will be automatically stripped. + - `oauth_base_url` - Base URL for MAS' OAuth endpoints (defaults to `admin_api_base_url` if not set). Trailing slashes will be automatically stripped. + - `client_id` - client ID for the automated tool. Must be a valid [ULID](https://github.com/ulid/spec). Generate one [here](https://ulidtools.com/). + - `client_secret` - client secret for the automated tool. Ideally long and cryptographically secure. Keep it a secret! + - `client_secret_filepath` - path to a plaintext file containing the client secret. If set, this is used instead of `client_secret`. Example configuration: @@ -81,19 +81,19 @@ Then, add the following to your MAS config file: ```yaml policy: - data: - admin_clients: - - 000000000000000000000G0EST + data: + admin_clients: + - 000000000000000000000G0EST # ... clients: - # The `client_id` must be a valid ULID https://github.com/ulid/spec - # Generate ULID's easily at: https://ulidtools.com/ - - client_id: 000000000000000000000G0EST - # The guest module uses the client_secret_basic authentication method. - client_auth_method: client_secret_basic - client_secret: your-client-secret + # The `client_id` must be a valid ULID https://github.com/ulid/spec + # Generate ULID's easily at: https://ulidtools.com/ + - client_id: 000000000000000000000G0EST + # The guest module uses the client_secret_basic authentication method. + client_auth_method: client_secret_basic + client_secret: your-client-secret ``` ## Production installation diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 525482294b..281cf9dd6b 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -24,9 +24,9 @@ from synapse.module_api.errors import ConfigError from synapse.types import UserID from synapse_guest_module.config import GuestModuleConfig, MasConfig -from synapse_guest_module.mas_admin_client import MasAdminClient from synapse_guest_module.guest_registration_servlet import GuestRegistrationServlet from synapse_guest_module.guest_user_reaper import GuestUserReaper +from synapse_guest_module.mas_admin_client import MasAdminClient logger = logging.getLogger("synapse.contrib." + __name__) @@ -106,22 +106,30 @@ class GuestModule: raise ConfigError("Config option 'mas' must be an object") admin_api_base_url = mas_config.get("admin_api_base_url") - if not isinstance(admin_api_base_url, str) or len(admin_api_base_url.strip()) == 0: - raise ConfigError("Config option 'mas.admin_api_base_url' is required and must be a string") + if ( + not isinstance(admin_api_base_url, str) + or len(admin_api_base_url.strip()) == 0 + ): + raise ConfigError( + "Config option 'mas.admin_api_base_url' is required and must be a string" + ) oauth_base_url = mas_config.get("oauth_base_url", admin_api_base_url) if not isinstance(oauth_base_url, str) or len(oauth_base_url.strip()) == 0: - raise ConfigError( - "Config option 'mas.oauth_base_url' must be a string" - ) + raise ConfigError("Config option 'mas.oauth_base_url' must be a string") client_id = mas_config.get("client_id") if not isinstance(client_id, str) or len(client_id.strip()) == 0: - raise ConfigError("Config option 'mas.client_id' is required and must be a string") + raise ConfigError( + "Config option 'mas.client_id' is required and must be a string" + ) client_secret = mas_config.get("client_secret") if client_secret is not None: - if not isinstance(client_secret, str) or len(client_secret.strip()) == 0: + if ( + not isinstance(client_secret, str) + or len(client_secret.strip()) == 0 + ): raise ConfigError( "Config option 'mas.client_secret' must be a string" ) @@ -129,7 +137,10 @@ class GuestModule: client_secret_filepath = mas_config.get("client_secret_filepath") if client_secret_filepath is not None: - if not isinstance(client_secret_filepath, str) or len(client_secret_filepath.strip()) == 0: + if ( + not isinstance(client_secret_filepath, str) + or len(client_secret_filepath.strip()) == 0 + ): raise ConfigError( "Config option 'mas.client_secret_filepath' must be a string" ) @@ -139,7 +150,7 @@ class GuestModule: raise ConfigError( "Config option 'mas.client_secret' or 'mas.client_secret_filepath' is required" ) - + if client_secret is not None and client_secret_filepath is not None: raise ConfigError( "Config option 'mas.client_secret' and 'mas.client_secret_filepath' are mutually exclusive" diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index a0ac7fceae..4eb6e0b607 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -87,9 +87,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): localpart, displayname + self._config.display_name_suffix ) - device_id, access_token, _, _ = await self._api.register_device( - user_id - ) + device_id, access_token, _, _ = await self._api.register_device(user_id) else: logger.info("Registering MAS guest user with username '%s'", localpart) mas_user_id = await self._mas_admin_client.create_user(localpart) @@ -112,10 +110,11 @@ class GuestRegistrationServlet(DirectServeJsonResource): if self._config.enable_user_reaper else 0 ) - device_id, access_token = ( - await self._mas_admin_client.create_personal_session( - mas_user_id, expires_in - ) + ( + device_id, + access_token, + ) = await self._mas_admin_client.create_personal_session( + mas_user_id, expires_in ) logger.debug("Registered user '%s'", user_id) @@ -145,6 +144,4 @@ class GuestRegistrationServlet(DirectServeJsonResource): }, ) - await self._api.run_db_interaction( - "guest_module_store_mas_user", store_user - ) + await self._api.run_db_interaction("guest_module_store_mas_user", store_user) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index 2c75ccec34..dd2163b428 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -106,6 +106,12 @@ class GuestUserReaper: logger.error('Failed to delete user "%s": %s', user_id, e) async def _deactivate_expired_mas_users(self) -> None: + """Deactivate all MAS users that are older than the specified expiration + interval. This uses the MAS admin API to disable the user. + """ + + assert self._mas_admin_client is not None + if self._mas_tables_ready is not None: await self._mas_tables_ready.wait() diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index a650450531..50ff0e2480 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -25,10 +25,10 @@ class MasAdminClient: async def create_user(self, username: str) -> str: """Creates a new user in MAS with the given username. - + Args: username: The username (localpart) of the user to create. - + Returns: The MAS ID of the created user. """ @@ -40,8 +40,8 @@ class MasAdminClient: post_json={"username": username}, headers={"Authorization": [f"Bearer {token}"]}, ) - - mas_user_id = response.get("data", {}).get("id") + + mas_user_id: str = response.get("data", {}).get("id") if mas_user_id is None or not isinstance(mas_user_id, str): raise ValueError("MAS user creation response missing `data.id` field") @@ -109,7 +109,7 @@ class MasAdminClient: if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS token response missing access_token") return access_token - + def _load_client_secret(self) -> str: """Source the MAS client secret from either configuration or a file.""" if self._config.client_secret_filepath is not None: @@ -141,13 +141,13 @@ class MasAdminClient: self, url: str, data: Dict[str, str], headers: Dict[str, Any] ) -> Any: http_client = self._api.http_client - post_urlencoded = getattr(http_client, "post_urlencoded_get_json", None) - if callable(post_urlencoded): + post_urlencoded: Optional[Awaitable[Any]] = getattr( + http_client, "post_urlencoded_get_json", None + ) + if post_urlencoded is not None and callable(post_urlencoded): return await post_urlencoded(url, data, headers=headers) - logger.debug( - "MAS client falling back to post_json_get_json for %s", url - ) + logger.debug("MAS client falling back to post_json_get_json for %s", url) return await http_client.post_json_get_json( uri=url, post_json=data, headers=headers ) From 55ca2be0f6758f18c9758f83cf4612bfe9b0edda Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 16 Jan 2026 18:05:26 +0000 Subject: [PATCH 101/211] Update unit tests to check MAS config, deactivation works --- .../synapse_guest_module/mas_admin_client.py | 2 +- .../synapse/tests/__init__.py | 24 ++++++++--- .../synapse/tests/test_guest_module.py | 34 ++++++++++++++- .../synapse/tests/test_guest_user_reaper.py | 43 +++++++++++++++++++ 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 50ff0e2480..70e9e1b0f8 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -5,7 +5,7 @@ import base64 import logging -from typing import Any, Dict +from typing import Any, Awaitable, Dict, Optional from synapse.module_api import ModuleApi diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index 04d9c250b8..d6bc7b5f3f 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -9,7 +9,7 @@ import sqlite3 from asyncio import Future -from typing import Any, Awaitable, Callable, Tuple, TypeVar +from typing import Any, Awaitable, Callable, Dict, Tuple, TypeVar from unittest.mock import Mock from synapse.http.client import SimpleHttpClient @@ -89,7 +89,9 @@ async def register_user(localpart: str, admin: bool = False) -> str: return f"@{localpart}:matrix.local" -def create_module() -> Tuple[GuestModule, Mock, SQLiteStore]: +def create_module( + config_override: Dict[str, Any] | None = None, +) -> Tuple[GuestModule, Mock, SQLiteStore]: store = SQLiteStore() _setup_db(store.conn) @@ -111,14 +113,19 @@ def create_module() -> Tuple[GuestModule, Mock, SQLiteStore]: ) # If necessary, give parse_config some configuration to parse. - config = GuestModule.parse_config( - { - "enable_user_reaper": False, - } - ) + config_dict: Dict[str, Any] = { + "enable_user_reaper": False, + } + if config_override is not None: + config_dict.update(config_override) + + config = GuestModule.parse_config(config_dict) module = GuestModule(config, module_api) + if getattr(module, "_mas_tables_ready", None) is not None: + module._mas_tables_ready.set() # type: ignore[union-attr] + return module, module_api, store @@ -127,3 +134,6 @@ def _setup_db(conn: sqlite3.Connection) -> None: conn.execute( "CREATE TABLE users(name text, deactivated smallint, creation_ts bigint)" ) + conn.execute( + "CREATE TABLE guest_module_mas_users(mas_user_id text, created_at bigint)" + ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index f92614c714..01605528a0 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -12,7 +12,7 @@ from synapse.module_api import ProfileInfo, UserProfile from synapse.module_api.errors import ConfigError from synapse.types import UserID -from synapse_guest_module.config import GuestModuleConfig +from synapse_guest_module.config import GuestModuleConfig, MasConfig from synapse_guest_module.guest_module import GuestModule from tests import create_module @@ -28,10 +28,11 @@ class GuestModuleTest(aiounittest.AsyncTestCase): display_name_suffix=" (Guest)", enable_user_reaper=True, user_expiration_seconds=24 * 60 * 60, + mas=None, ), ) - async def test_parse_config_custom(self) -> None: + async def test_parse_config_no_mas(self) -> None: config = GuestModule.parse_config( { "user_id_prefix": "tmp-", @@ -48,6 +49,35 @@ class GuestModuleTest(aiounittest.AsyncTestCase): display_name_suffix=" (Temporary)", enable_user_reaper=False, user_expiration_seconds=100, + mas=None, + ), + ) + + async def test_parse_config_mas(self) -> None: + config = GuestModule.parse_config( + { + "mas": { + "admin_api_base_url": "https://mas.example.org", + "client_id": "client-id", + "client_secret": "client-secret", + }, + } + ) + + self.assertEqual( + config, + GuestModuleConfig( + user_id_prefix="guest-", + display_name_suffix=" (Guest)", + enable_user_reaper=True, + user_expiration_seconds=24 * 60 * 60, + mas=MasConfig( + admin_api_base_url="https://mas.example.org", + oauth_base_url="https://mas.example.org", + client_id="client-id", + client_secret="client-secret", + client_secret_filepath=None, + ), ), ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index 0a865c3f2d..06815e1427 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -121,3 +121,46 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): ), ] ) + + async def test_deactivate_expired_mas_users_success(self) -> None: + module, module_api, store = create_module( + { + "mas": { + "admin_api_base_url": "https://mas.example.org", + "oauth_base_url": "https://oauth.mas.example.org", + "client_id": "client-id", + "client_secret": "client-secret", + }, + } + ) + + now = int(time.time()) + store.conn.executemany( + "INSERT INTO guest_module_mas_users VALUES (?, ?)", + [ + ["mas-old-1", 0], + ["mas-active", now], + ], + ) + + # These two methods are `AsyncMock`s, so no need to use `make_awaitable`.` + token_response = {"access_token": "mas_admin_token"} + module_api.http_client.post_urlencoded_get_json.return_value = token_response + module_api.http_client.post_json_get_json.return_value = {} + + await module.reaper.deactivate_expired_guest_users() + + deactivate_call = call( + uri="https://mas.example.org/api/admin/v1/users/mas-old-1/deactivate", + post_json={"skip_erase": True}, + headers={"Authorization": ["Bearer mas_admin_token"]}, + ) + self.assertIn( + deactivate_call, + module_api.http_client.post_json_get_json.await_args_list, + ) + + remaining_users = store.conn.execute( + "SELECT mas_user_id FROM guest_module_mas_users" + ).fetchall() + self.assertEqual(remaining_users, [("mas-active",)]) From 41922415badb87dd993f9ec006cbaf68a30748be Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 16 Jan 2026 19:20:13 +0000 Subject: [PATCH 102/211] Update tests to run with MAS module variant We now run the standard battery of tests with a MAS-enabled module. Some minimal adjustment was needed to check MAS-specific outputs. --- modules/restricted-guests/synapse/setup.cfg | 1 + .../synapse/tests/__init__.py | 29 ++++++- .../synapse/tests/test_guest_module.py | 37 ++++++--- .../tests/test_guest_registration_servlet.py | 83 +++++++++++++++---- 4 files changed, 119 insertions(+), 31 deletions(-) diff --git a/modules/restricted-guests/synapse/setup.cfg b/modules/restricted-guests/synapse/setup.cfg index 75fead2743..d190edbb11 100644 --- a/modules/restricted-guests/synapse/setup.cfg +++ b/modules/restricted-guests/synapse/setup.cfg @@ -30,6 +30,7 @@ dev = twisted aiounittest coverage + parameterized # for type checking mypy == 1.10.0 pydantic == 2.4.2 diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index d6bc7b5f3f..d79a60a891 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -9,8 +9,8 @@ import sqlite3 from asyncio import Future -from typing import Any, Awaitable, Callable, Dict, Tuple, TypeVar -from unittest.mock import Mock +from typing import Any, Awaitable, Callable, Dict, List, Tuple, TypeVar +from unittest.mock import AsyncMock, Mock from synapse.http.client import SimpleHttpClient from synapse.module_api import ModuleApi @@ -81,6 +81,20 @@ def make_awaitable(result: TV) -> Awaitable[TV]: return future +def set_async_return_value(target: Any, value: Any) -> None: + if isinstance(target, AsyncMock): + target.return_value = value + else: + target.return_value = make_awaitable(value) + + +def set_async_side_effect(target: Any, values: List[Any]) -> None: + if isinstance(target, AsyncMock): + target.side_effect = values + else: + target.side_effect = [make_awaitable(value) for value in values] + + def get_qualified_user_id(username: str) -> str: return f"@{username}:matrix.local" @@ -129,6 +143,17 @@ def create_module( return module, module_api, store +def mas_config_override() -> Dict[str, Any]: + return { + "mas": { + "admin_api_base_url": "https://mas.example.org", + "oauth_base_url": "https://oauth.mas.example.org", + "client_id": "client-id", + "client_secret": "client-secret", + }, + } + + def _setup_db(conn: sqlite3.Connection) -> None: conn.execute("CREATE TABLE access_tokens(user_id text, token text)") conn.execute( diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index 01605528a0..10e51a178e 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -7,17 +7,20 @@ # Originally licensed under the Apache License, Version 2.0: # . +from typing import Tuple +from unittest.mock import Mock import aiounittest +from parameterized import parameterized_class # type: ignore[import-untyped] from synapse.module_api import ProfileInfo, UserProfile from synapse.module_api.errors import ConfigError from synapse.types import UserID from synapse_guest_module.config import GuestModuleConfig, MasConfig from synapse_guest_module.guest_module import GuestModule -from tests import create_module +from tests import SQLiteStore, create_module, mas_config_override -class GuestModuleTest(aiounittest.AsyncTestCase): +class GuestModuleConfigTest(aiounittest.AsyncTestCase): async def test_parse_config_empty(self) -> None: config = GuestModule.parse_config({}) @@ -121,8 +124,20 @@ class GuestModuleTest(aiounittest.AsyncTestCase): } ) + +@parameterized_class( + ("variant", "config_override"), + [ + ("synapse", None), + ("mas", mas_config_override()), + ], +) +class GuestModuleRuntimeTest(aiounittest.AsyncTestCase): + def create_module(self) -> Tuple[GuestModule, Mock, SQLiteStore]: + return create_module(self.config_override) + async def test_profile_update_no_guest(self) -> None: - module, module_api, _ = create_module() + module, module_api, _ = self.create_module() await module.profile_update( "@my-user:matrix.local", @@ -134,7 +149,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): module_api.set_displayname.assert_not_called() async def test_profile_update_guest_keep(self) -> None: - module, module_api, _ = create_module() + module, module_api, _ = self.create_module() await module.profile_update( "@guest-asdf:matrix.local", @@ -146,7 +161,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): module_api.set_displayname.assert_not_called() async def test_profile_update_guest_add_and_trim(self) -> None: - module, module_api, _ = create_module() + module, module_api, _ = self.create_module() await module.profile_update( "@guest-asdf:matrix.local", @@ -161,7 +176,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): ) async def test_callback_user_may_create_room_no_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_user_may_create_room( "@my-user:matrix.local", @@ -170,7 +185,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): self.assertTrue(allow) async def test_callback_user_may_create_room_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_user_may_create_room( "@guest-asdf:matrix.local", @@ -179,7 +194,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): self.assertFalse(allow) async def test_callback_user_may_invite_no_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_user_may_invite( "@my-user:matrix.local", @@ -190,7 +205,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): self.assertTrue(allow) async def test_callback_user_may_invite_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_user_may_invite( "@guest-asdf:matrix.local", @@ -201,7 +216,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): self.assertFalse(allow) async def test_callback_check_username_for_spam_no_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_check_username_for_spam( UserProfile( @@ -214,7 +229,7 @@ class GuestModuleTest(aiounittest.AsyncTestCase): self.assertFalse(allow) async def test_callback_check_username_for_spam_guest(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() allow = await module.callback_check_username_for_spam( UserProfile( diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index f0a63b7a62..402b52020e 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -8,19 +8,38 @@ # . import io -from typing import cast -from unittest.mock import ANY +from typing import Tuple, cast +from unittest.mock import ANY, Mock import aiounittest +from parameterized import parameterized_class # type: ignore[import-untyped] from twisted.web.server import Request from twisted.web.test.requesthelper import DummyRequest +from synapse_guest_module import GuestModule -from tests import create_module, make_awaitable +from tests import ( + SQLiteStore, + create_module, + make_awaitable, + mas_config_override, + set_async_return_value, + set_async_side_effect, +) +@parameterized_class( + ("variant", "config_override"), + [ + ("synapse", None), + ("mas", mas_config_override()), + ], +) class GuestUserReaperTest(aiounittest.AsyncTestCase): + def create_module(self) -> Tuple[GuestModule, Mock, SQLiteStore]: + return create_module(self.config_override) + async def test_async_render_POST_missing_displayname(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() request = cast(Request, DummyRequest([])) request.content = io.BytesIO(b"{}") @@ -33,7 +52,7 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): ) async def test_async_render_POST_empty_displayname(self) -> None: - module, _, _ = create_module() + module, _, _ = self.create_module() request = cast(Request, DummyRequest([])) request.content = io.BytesIO(b'{"displayname":" "}') @@ -46,7 +65,7 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): ) async def test_async_render_POST_no_free_username(self) -> None: - module, module_api, _ = create_module() + module, module_api, _ = self.create_module() request = cast(Request, DummyRequest([])) request.content = io.BytesIO(b'{"displayname":"My Name"}') @@ -63,24 +82,52 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): self.assertEqual(module_api.check_user_exists.call_count, 10) async def test_async_render_POST_success(self) -> None: - module, module_api, _ = create_module() + module, module_api, _ = self.create_module() request = cast(Request, DummyRequest([])) request.content = io.BytesIO(b'{"displayname":"My Name "}') + if self.config_override is not None: + set_async_return_value( + module_api.http_client.post_urlencoded_get_json, + {"access_token": "mas_admin_token"}, + ) + set_async_side_effect( + module_api.http_client.post_json_get_json, + [ + {"data": {"id": "mas-user-id"}}, + { + "data": { + "id": "MASDEVICE", + "attributes": {"access_token": "mas_access_token"}, + } + }, + ], + ) + status, response = await module.registration_servlet._async_render_POST(request) self.assertEqual(status, 201) self.assertRegex(response.pop("userId"), r"^@guest-[A-Za-z0-9]+:matrix.local$") - self.assertDictEqual( - response, - { - "accessToken": "syn_registered_token", - "deviceId": "DEVICEID", - "homeserverUrl": "https://matrix.local:1234/", - # "userId" was already checked by self.assertRegex and was removed from the object - }, - ) - - module_api.register_user.assert_called_with(ANY, "My Name (Guest)") + if self.config_override is None: + self.assertDictEqual( + response, + { + "accessToken": "syn_registered_token", + "deviceId": "DEVICEID", + "homeserverUrl": "https://matrix.local:1234/", + # "userId" was already checked by self.assertRegex and was removed from the object + }, + ) + module_api.register_user.assert_called_with(ANY, "My Name (Guest)") + else: + self.assertDictEqual( + response, + { + "accessToken": "mas_access_token", + "deviceId": "MASDEVICE", + "homeserverUrl": "https://matrix.local:1234/", + # "userId" was already checked by self.assertRegex and was removed from the object + }, + ) From b8fda8acdfec54e0b09fafcecece956642bed916 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 19 Jan 2026 13:04:31 +0000 Subject: [PATCH 103/211] lint --- modules/restricted-guests/synapse/tests/test_guest_module.py | 1 + .../synapse/tests/test_guest_registration_servlet.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/tests/test_guest_module.py b/modules/restricted-guests/synapse/tests/test_guest_module.py index 10e51a178e..714e1fd918 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_module.py +++ b/modules/restricted-guests/synapse/tests/test_guest_module.py @@ -9,6 +9,7 @@ from typing import Tuple from unittest.mock import Mock + import aiounittest from parameterized import parameterized_class # type: ignore[import-untyped] from synapse.module_api import ProfileInfo, UserProfile diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index 402b52020e..81390efa29 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -15,8 +15,8 @@ import aiounittest from parameterized import parameterized_class # type: ignore[import-untyped] from twisted.web.server import Request from twisted.web.test.requesthelper import DummyRequest -from synapse_guest_module import GuestModule +from synapse_guest_module import GuestModule from tests import ( SQLiteStore, create_module, From 3f53333448a1e24de10a44a35b1c9875c3af9575 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 19 Jan 2026 13:56:57 +0000 Subject: [PATCH 104/211] Return the correct device ID Turns out the client needs to generate it, and pass it as a scope. Clever! --- .../synapse_guest_module/mas_admin_client.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 70e9e1b0f8..2535790ad2 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -5,6 +5,8 @@ import base64 import logging +import secrets +import string from typing import Any, Awaitable, Dict, Optional from synapse.module_api import ModuleApi @@ -53,10 +55,11 @@ class MasAdminClient: token = await self.request_admin_token() url = self._build_admin_url("/api/admin/v1/personal-sessions") + device_id = self._generate_device_id() request_body = { "actor_user_id": mas_user_id, "expires_in": expires_in, - "scope": "openid urn:matrix:client:api:*", + "scope": f"openid urn:matrix:client:api:* urn:matrix:client:device:{device_id}", "human_name": "guest session", } @@ -69,13 +72,11 @@ class MasAdminClient: data = response.get("data", {}) attributes = data.get("attributes", {}) if isinstance(data, dict) else {} access_token = attributes.get("access_token") - # TODO: Is this the correct device ID? - device_id = data.get("id") if isinstance(data, dict) else None + + print(response) if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS session response missing `access_token` field") - if not isinstance(device_id, str) or len(device_id) == 0: - raise ValueError("MAS session response missing device id") return device_id, access_token @@ -137,6 +138,23 @@ class MasAdminClient: return client_secret + @staticmethod + def _generate_device_id() -> str: + """Generate a MAS device ID. + + Device IDs must be at least 10 characters long and contain only + [A-Za-z0-9-]. + + The generated device ID is purposefully non-cryptographically random, + as the value is public. + + Returns: + The generated Device ID. + """ + length = 16 + alphabet = string.ascii_letters + string.digits + "-" + return "".join(secrets.choice(alphabet) for _ in range(length)) + async def _post_urlencoded_get_json( self, url: str, data: Dict[str, str], headers: Dict[str, Any] ) -> Any: From 2a9c384213d21ad7b272bc7b495a34ac3dc50c51 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 19 Jan 2026 14:20:02 +0000 Subject: [PATCH 105/211] Update tests for randomly generated device IDs Pin the device ID in tests by mocking the generation function. And test said generation function. --- .../tests/test_guest_registration_servlet.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index 81390efa29..ca72d17964 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -17,6 +17,7 @@ from twisted.web.server import Request from twisted.web.test.requesthelper import DummyRequest from synapse_guest_module import GuestModule +from synapse_guest_module.mas_admin_client import MasAdminClient from tests import ( SQLiteStore, create_module, @@ -88,6 +89,9 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): request.content = io.BytesIO(b'{"displayname":"My Name "}') if self.config_override is not None: + module.registration_servlet._mas_admin_client._generate_device_id = ( # type: ignore[method-assign,union-attr] + lambda: "MASDEVICE123" + ) set_async_return_value( module_api.http_client.post_urlencoded_get_json, {"access_token": "mas_admin_token"}, @@ -98,7 +102,7 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): {"data": {"id": "mas-user-id"}}, { "data": { - "id": "MASDEVICE", + "id": "MASDEVICE123", "attributes": {"access_token": "mas_access_token"}, } }, @@ -126,8 +130,16 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): response, { "accessToken": "mas_access_token", - "deviceId": "MASDEVICE", + "deviceId": "MASDEVICE123", "homeserverUrl": "https://matrix.local:1234/", # "userId" was already checked by self.assertRegex and was removed from the object }, ) + + +class MasAdminClientTest(aiounittest.AsyncTestCase): + async def test_generate_device_id_format(self) -> None: + device_id = MasAdminClient._generate_device_id() + + self.assertGreaterEqual(len(device_id), 10) + self.assertRegex(device_id, r"^[A-Za-z0-9-]+$") From f88a280b8b5cd3fa467a9a56a11c3e21e26b6f10 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 13:52:19 +0000 Subject: [PATCH 106/211] Correct typo in comment --- .../synapse/synapse_guest_module/guest_registration_servlet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 4eb6e0b607..9835ab7e10 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -63,8 +63,9 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname = displayname.strip() - # make sure the regex is unique + # Attempt up to 10 times to generate a localpart for _ in range(10): + # generate a random string as a suffix random_string = "".join( secrets.choice(string.ascii_lowercase + string.digits) for _ in range(32) From 514c6957f7ef15658a4179abd1cecbfbea22b051 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 14:02:00 +0000 Subject: [PATCH 107/211] Log MXID and MAS ID when registering guest user --- .../synapse_guest_module/guest_registration_servlet.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 9835ab7e10..fcf1469161 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -90,11 +90,15 @@ class GuestRegistrationServlet(DirectServeJsonResource): device_id, access_token, _, _ = await self._api.register_device(user_id) else: - logger.info("Registering MAS guest user with username '%s'", localpart) - mas_user_id = await self._mas_admin_client.create_user(localpart) + logger.info("Registering MAS guest user with localpart '%s'", localpart) + # This will be the MAS-specific user ID (i.e. "01KFNJEB720EAGR907PSXRXQ51") + mas_user_id = await self._mas_admin_client.create_user(localpart) + # This is the Matrix user ID (i.e. "@guest_abc123:matrix.org") user_id = self._api.get_qualified_user_id(localpart) + logger.info(f"Registered guest user: '{user_id}' (MAS ID: '{mas_user_id}')") + await self._api.set_displayname( UserID.from_string(user_id), displayname + self._config.display_name_suffix, From 2e71abdfda74d8250dad25de4ea9e69088408bbe Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 14:38:19 +0000 Subject: [PATCH 108/211] Add `user_id` field to `guest_module_mas_users` Currently unused, but may be useful in future. --- .../synapse/synapse_guest_module/guest_module.py | 1 + .../synapse_guest_module/guest_registration_servlet.py | 5 +++-- modules/restricted-guests/synapse/tests/__init__.py | 2 +- .../synapse/tests/test_guest_user_reaper.py | 10 +++++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 281cf9dd6b..7f53f5052e 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -218,6 +218,7 @@ class GuestModule: """ CREATE TABLE IF NOT EXISTS guest_module_mas_users ( mas_user_id TEXT PRIMARY KEY, + user_id TEXT, created_at BIGINT NOT NULL ) """, diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index fcf1469161..41257e5726 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -104,7 +104,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): displayname + self._config.display_name_suffix, ) - await self._store_mas_user(mas_user_id, int(time.time())) + await self._store_mas_user(mas_user_id, user_id, int(time.time())) # Determine how long to keep the access token valid for. # @@ -135,7 +135,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): return 500, {"msg": "Internal error: Could not find a free username"} - async def _store_mas_user(self, mas_user_id: str, created_at: int) -> None: + async def _store_mas_user(self, mas_user_id: str, user_id: str, created_at: int) -> None: if self._mas_tables_ready is not None: await self._mas_tables_ready.wait() @@ -145,6 +145,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): table="guest_module_mas_users", values={ "mas_user_id": mas_user_id, + "user_id": user_id, "created_at": created_at, }, ) diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index d79a60a891..b97dd1c45b 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -160,5 +160,5 @@ def _setup_db(conn: sqlite3.Connection) -> None: "CREATE TABLE users(name text, deactivated smallint, creation_ts bigint)" ) conn.execute( - "CREATE TABLE guest_module_mas_users(mas_user_id text, created_at bigint)" + "CREATE TABLE guest_module_mas_users(mas_user_id text, user_id text, created_at bigint)" ) diff --git a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py index 06815e1427..960e3f999f 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py +++ b/modules/restricted-guests/synapse/tests/test_guest_user_reaper.py @@ -136,10 +136,10 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): now = int(time.time()) store.conn.executemany( - "INSERT INTO guest_module_mas_users VALUES (?, ?)", + "INSERT INTO guest_module_mas_users VALUES (?, ?, ?)", [ - ["mas-old-1", 0], - ["mas-active", now], + ["mas-old-1", "@old-1:localhost", 0], + ["mas-active", "@active:localhost", now], ], ) @@ -161,6 +161,6 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): ) remaining_users = store.conn.execute( - "SELECT mas_user_id FROM guest_module_mas_users" + "SELECT mas_user_id, user_id FROM guest_module_mas_users" ).fetchall() - self.assertEqual(remaining_users, [("mas-active",)]) + self.assertEqual(remaining_users, [("mas-active", "@active:localhost")]) From e0304c627bf01ce908dca56e97f0b870202355f3 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 14:41:06 +0000 Subject: [PATCH 109/211] `created_at` -> `created_at_sec` --- .../synapse/synapse_guest_module/guest_module.py | 6 +++--- .../guest_registration_servlet.py | 11 +++++++++-- .../synapse/synapse_guest_module/guest_user_reaper.py | 2 +- modules/restricted-guests/synapse/tests/__init__.py | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 7f53f5052e..93092a9be2 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -219,15 +219,15 @@ class GuestModule: CREATE TABLE IF NOT EXISTS guest_module_mas_users ( mas_user_id TEXT PRIMARY KEY, user_id TEXT, - created_at BIGINT NOT NULL + created_at_sec BIGINT NOT NULL ) """, (), ) txn.execute( """ - CREATE INDEX IF NOT EXISTS guest_module_mas_users_created_at - ON guest_module_mas_users (created_at) + CREATE INDEX IF NOT EXISTS guest_module_mas_users_created_at_sec + ON guest_module_mas_users (created_at_sec) """, (), ) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 41257e5726..0d5dd51b7c 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -135,7 +135,14 @@ class GuestRegistrationServlet(DirectServeJsonResource): return 500, {"msg": "Internal error: Could not find a free username"} - async def _store_mas_user(self, mas_user_id: str, user_id: str, created_at: int) -> None: + async def _store_mas_user(self, mas_user_id: str, user_id: str, created_at_sec: int) -> None: + """Store details about the MAS user in the DB + + Args: + mas_user_id: The MAS user ID + user_id: The Matrix user ID + created_at_sec: The creation timestamp in seconds since the unix epoch + """ if self._mas_tables_ready is not None: await self._mas_tables_ready.wait() @@ -146,7 +153,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): values={ "mas_user_id": mas_user_id, "user_id": user_id, - "created_at": created_at, + "created_at_sec": created_at_sec, }, ) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py index dd2163b428..24fd5db2ce 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_user_reaper.py @@ -121,7 +121,7 @@ class GuestUserReaper: """ SELECT mas_user_id FROM guest_module_mas_users - WHERE created_at < ? + WHERE created_at_sec < ? """, (expire_ts_seconds,), ) diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index b97dd1c45b..f62fc2cbf4 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -160,5 +160,5 @@ def _setup_db(conn: sqlite3.Connection) -> None: "CREATE TABLE users(name text, deactivated smallint, creation_ts bigint)" ) conn.execute( - "CREATE TABLE guest_module_mas_users(mas_user_id text, user_id text, created_at bigint)" + "CREATE TABLE guest_module_mas_users(mas_user_id text, user_id text, created_at_sec bigint)" ) From a4173bb1dd72aee3c9b7f005243fae96ee252256 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 14:55:17 +0000 Subject: [PATCH 110/211] `expires_in` -> `expires_in_sec` + docstring --- .../guest_registration_servlet.py | 4 ++-- .../synapse_guest_module/mas_admin_client.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 0d5dd51b7c..3300204480 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -110,7 +110,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): # # If a user reaper is enabled, just have the token expire after # the configured period. - expires_in = ( + expires_in_sec = ( self._config.user_expiration_seconds if self._config.enable_user_reaper else 0 @@ -119,7 +119,7 @@ class GuestRegistrationServlet(DirectServeJsonResource): device_id, access_token, ) = await self._mas_admin_client.create_personal_session( - mas_user_id, expires_in + mas_user_id, expires_in_sec ) logger.debug("Registered user '%s'", user_id) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 2535790ad2..a010371204 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -50,15 +50,24 @@ class MasAdminClient: return mas_user_id async def create_personal_session( - self, mas_user_id: str, expires_in: int + self, mas_user_id: str, expires_in_sec: int ) -> tuple[str, str]: + """Creates a new personal session for the given MAS user. + + Args: + mas_user_id: The MAS user ID. + expires_in_sec: The session expiration time in seconds. + + Returns: + A tuple of (device_id, access_token). + """ token = await self.request_admin_token() url = self._build_admin_url("/api/admin/v1/personal-sessions") device_id = self._generate_device_id() request_body = { "actor_user_id": mas_user_id, - "expires_in": expires_in, + "expires_in": expires_in_sec, "scope": f"openid urn:matrix:client:api:* urn:matrix:client:device:{device_id}", "human_name": "guest session", } @@ -73,8 +82,6 @@ class MasAdminClient: attributes = data.get("attributes", {}) if isinstance(data, dict) else {} access_token = attributes.get("access_token") - print(response) - if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS session response missing `access_token` field") From ecd25faec824285a21c53f351a8c0fd1731a4bd8 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 15:16:39 +0000 Subject: [PATCH 111/211] Add docstring for `request_admin_token` --- .../synapse_guest_module/mas_admin_client.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index a010371204..60c03aa417 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -99,6 +99,17 @@ class MasAdminClient: ) async def request_admin_token(self) -> str: + """ + Uses the client credentials flow to request an admin access token + from MAS. + + Returns: + The admin access token. + + Raises: + ValueError: If the token response is invalid. + HttpResponseException: On a non-2xx HTTP response. + """ url = self._build_oauth_url("/oauth2/token") basic_auth = base64.b64encode( f"{self._config.client_id}:{self._client_secret}".encode("utf-8") @@ -165,6 +176,20 @@ class MasAdminClient: async def _post_urlencoded_get_json( self, url: str, data: Dict[str, str], headers: Dict[str, Any] ) -> Any: + """Helper to POST JSON data and get JSON response. + + Args: + url: The URL to POST to. + data: The form data to POST. + headers: Additional headers to include in the request. + + Returns: + The JSON response from the server. + + Raises: + HttpResponseException: On a non-2xx HTTP response. + ValueError: if the response was not JSON. + """ http_client = self._api.http_client post_urlencoded: Optional[Awaitable[Any]] = getattr( http_client, "post_urlencoded_get_json", None From 7abb63f1f205d477bb1af30199d0ad916f3fd8b9 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 15:18:55 +0000 Subject: [PATCH 112/211] Don't be cryptographically random! That's what you get for generating a random string function. --- .../synapse/synapse_guest_module/mas_admin_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 60c03aa417..b7166887f5 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -5,7 +5,7 @@ import base64 import logging -import secrets +import random import string from typing import Any, Awaitable, Dict, Optional @@ -171,7 +171,7 @@ class MasAdminClient: """ length = 16 alphabet = string.ascii_letters + string.digits + "-" - return "".join(secrets.choice(alphabet) for _ in range(length)) + return "".join(random.choices(alphabet, k=length)) async def _post_urlencoded_get_json( self, url: str, data: Dict[str, str], headers: Dict[str, Any] From c6dabf052b0a3bd2a4c112d34006f83dda3adc97 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 16:36:59 +0000 Subject: [PATCH 113/211] Remove fallback wrapper for `post_urlencoded_get_json` --- .../synapse_guest_module/mas_admin_client.py | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index b7166887f5..1c7f9d894c 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -7,7 +7,6 @@ import base64 import logging import random import string -from typing import Any, Awaitable, Dict, Optional from synapse.module_api import ModuleApi @@ -123,7 +122,7 @@ class MasAdminClient: "scope": "urn:mas:admin", } - response = await self._post_urlencoded_get_json(url, data, headers) + response = await self._api.http_client.post_urlencoded_get_json(url, data, headers) access_token = response.get("access_token") if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS token response missing access_token") @@ -173,35 +172,6 @@ class MasAdminClient: alphabet = string.ascii_letters + string.digits + "-" return "".join(random.choices(alphabet, k=length)) - async def _post_urlencoded_get_json( - self, url: str, data: Dict[str, str], headers: Dict[str, Any] - ) -> Any: - """Helper to POST JSON data and get JSON response. - - Args: - url: The URL to POST to. - data: The form data to POST. - headers: Additional headers to include in the request. - - Returns: - The JSON response from the server. - - Raises: - HttpResponseException: On a non-2xx HTTP response. - ValueError: if the response was not JSON. - """ - http_client = self._api.http_client - post_urlencoded: Optional[Awaitable[Any]] = getattr( - http_client, "post_urlencoded_get_json", None - ) - if post_urlencoded is not None and callable(post_urlencoded): - return await post_urlencoded(url, data, headers=headers) - - logger.debug("MAS client falling back to post_json_get_json for %s", url) - return await http_client.post_json_get_json( - uri=url, post_json=data, headers=headers - ) - def _build_admin_url(self, path: str) -> str: if not path.startswith("/"): path = "/" + path From 85ef24f9179282ff6e8e3a1882d119739fe70c50 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 16:49:12 +0000 Subject: [PATCH 114/211] Remove test runtime checks for AsyncMock vs. Mock Turns out they're always AsyncMock. --- .../synapse/tests/__init__.py | 18 ++--------- .../tests/test_guest_registration_servlet.py | 30 +++++++++---------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/modules/restricted-guests/synapse/tests/__init__.py b/modules/restricted-guests/synapse/tests/__init__.py index f62fc2cbf4..4f990b54f2 100644 --- a/modules/restricted-guests/synapse/tests/__init__.py +++ b/modules/restricted-guests/synapse/tests/__init__.py @@ -9,8 +9,8 @@ import sqlite3 from asyncio import Future -from typing import Any, Awaitable, Callable, Dict, List, Tuple, TypeVar -from unittest.mock import AsyncMock, Mock +from typing import Any, Awaitable, Callable, Dict, Tuple, TypeVar +from unittest.mock import Mock from synapse.http.client import SimpleHttpClient from synapse.module_api import ModuleApi @@ -81,20 +81,6 @@ def make_awaitable(result: TV) -> Awaitable[TV]: return future -def set_async_return_value(target: Any, value: Any) -> None: - if isinstance(target, AsyncMock): - target.return_value = value - else: - target.return_value = make_awaitable(value) - - -def set_async_side_effect(target: Any, values: List[Any]) -> None: - if isinstance(target, AsyncMock): - target.side_effect = values - else: - target.side_effect = [make_awaitable(value) for value in values] - - def get_qualified_user_id(username: str) -> str: return f"@{username}:matrix.local" diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index ca72d17964..f6008cbded 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -92,22 +92,20 @@ class GuestUserReaperTest(aiounittest.AsyncTestCase): module.registration_servlet._mas_admin_client._generate_device_id = ( # type: ignore[method-assign,union-attr] lambda: "MASDEVICE123" ) - set_async_return_value( - module_api.http_client.post_urlencoded_get_json, - {"access_token": "mas_admin_token"}, - ) - set_async_side_effect( - module_api.http_client.post_json_get_json, - [ - {"data": {"id": "mas-user-id"}}, - { - "data": { - "id": "MASDEVICE123", - "attributes": {"access_token": "mas_access_token"}, - } - }, - ], - ) + + # `make_awaitable` is not needed here as both methods are already `AsyncMock`. + module_api.http_client.post_urlencoded_get_json.return_value = { + "access_token": "mas_admin_token" + } + module_api.http_client.post_json_get_json.side_effect = [ + {"data": {"id": "mas-user-id"}}, + { + "data": { + "id": "MASDEVICE123", + "attributes": {"access_token": "mas_access_token"}, + } + }, + ] status, response = await module.registration_servlet._async_render_POST(request) From e1b42da345ac2094386913ae147cb297c4d3f20f Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Fri, 23 Jan 2026 17:05:56 +0000 Subject: [PATCH 115/211] lint --- .../synapse_guest_module/guest_registration_servlet.py | 10 +++++++--- .../synapse/synapse_guest_module/mas_admin_client.py | 10 ++++++---- .../synapse/tests/test_guest_registration_servlet.py | 9 +-------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py index 3300204480..d11e1ffb05 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_registration_servlet.py @@ -97,7 +97,9 @@ class GuestRegistrationServlet(DirectServeJsonResource): # This is the Matrix user ID (i.e. "@guest_abc123:matrix.org") user_id = self._api.get_qualified_user_id(localpart) - logger.info(f"Registered guest user: '{user_id}' (MAS ID: '{mas_user_id}')") + logger.info( + f"Registered guest user: '{user_id}' (MAS ID: '{mas_user_id}')" + ) await self._api.set_displayname( UserID.from_string(user_id), @@ -135,9 +137,11 @@ class GuestRegistrationServlet(DirectServeJsonResource): return 500, {"msg": "Internal error: Could not find a free username"} - async def _store_mas_user(self, mas_user_id: str, user_id: str, created_at_sec: int) -> None: + async def _store_mas_user( + self, mas_user_id: str, user_id: str, created_at_sec: int + ) -> None: """Store details about the MAS user in the DB - + Args: mas_user_id: The MAS user ID user_id: The Matrix user ID diff --git a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py index 1c7f9d894c..bde247e1ac 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/mas_admin_client.py @@ -52,11 +52,11 @@ class MasAdminClient: self, mas_user_id: str, expires_in_sec: int ) -> tuple[str, str]: """Creates a new personal session for the given MAS user. - + Args: mas_user_id: The MAS user ID. expires_in_sec: The session expiration time in seconds. - + Returns: A tuple of (device_id, access_token). """ @@ -104,7 +104,7 @@ class MasAdminClient: Returns: The admin access token. - + Raises: ValueError: If the token response is invalid. HttpResponseException: On a non-2xx HTTP response. @@ -122,7 +122,9 @@ class MasAdminClient: "scope": "urn:mas:admin", } - response = await self._api.http_client.post_urlencoded_get_json(url, data, headers) + response = await self._api.http_client.post_urlencoded_get_json( + url, data, headers + ) access_token = response.get("access_token") if not isinstance(access_token, str) or len(access_token) == 0: raise ValueError("MAS token response missing access_token") diff --git a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py index f6008cbded..b61c6400e0 100644 --- a/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py +++ b/modules/restricted-guests/synapse/tests/test_guest_registration_servlet.py @@ -18,14 +18,7 @@ from twisted.web.test.requesthelper import DummyRequest from synapse_guest_module import GuestModule from synapse_guest_module.mas_admin_client import MasAdminClient -from tests import ( - SQLiteStore, - create_module, - make_awaitable, - mas_config_override, - set_async_return_value, - set_async_side_effect, -) +from tests import SQLiteStore, create_module, make_awaitable, mas_config_override @parameterized_class( From ab8a307d5d4f70c57ea5873ddd914bb6d396d561 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Wed, 4 Feb 2026 16:31:19 +0000 Subject: [PATCH 116/211] Make `{mas_,}user_id` DB tables NOT NULL --- .../synapse/synapse_guest_module/guest_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py index 93092a9be2..1160ee6c93 100644 --- a/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py +++ b/modules/restricted-guests/synapse/synapse_guest_module/guest_module.py @@ -217,8 +217,8 @@ class GuestModule: txn.execute( """ CREATE TABLE IF NOT EXISTS guest_module_mas_users ( - mas_user_id TEXT PRIMARY KEY, - user_id TEXT, + mas_user_id TEXT PRIMARY KEY NOT NULL, + user_id TEXT NOT NULL, created_at_sec BIGINT NOT NULL ) """, From 7c62578252e900c56f8d1110589e12814a8d9ddd Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Mon, 19 Jan 2026 17:53:01 +0000 Subject: [PATCH 117/211] Create MAS container and tests for restricted guests testing Add a new container that both: - Spins up a MAS instance. - Loads the restricted guests module into Synapse, and configures it accordingly. Then extend restricted guests spec to include MAS tests. This requires setting up MAS' config to recognise the module as an automated client (so that it can request Admin perms to create/deactivate users). --- .../tests/restricted-guests.spec.ts | 301 +++++++++++++----- .../element-web/tests/services.ts | 30 +- 2 files changed, 251 insertions(+), 80 deletions(-) diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index f8f180a0cb..964cbdbd46 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -5,41 +5,115 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { StartedSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; -import { Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.ts"; +import { + MatrixAuthenticationServiceContainer, + type MasConfig, + type StartedMatrixAuthenticationServiceContainer, + type StartedSynapseContainer, + type SynapseConfig, +} from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; +import type { Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; +import type { Fixtures } from "@playwright/test"; -import { test as base, expect } from "../../../../playwright/element-web-test.ts"; -import { RestrictedGuestsSynapseContainer } from "./services.ts"; +import { test as base, expect } from "../../../../playwright/element-web-test"; +import { RestrictedGuestsSynapseContainer, RestrictedGuestsSynapseWithMasContainer } from "./services"; -const test = base.extend< +const MAS_CLIENT_ID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"; +const MAS_CLIENT_SECRET = "restricted-guests-secret"; +const MAS_SHARED_SECRET = "restricted-guests-shared-secret"; +const MAS_INTERNAL_URL = "http://mas:8080"; +const GUEST_HOMESERVER_NAME = "guest-homeserver"; +const GUEST_HOMESERVER_INTERNAL_URL = "http://guest-homeserver:8008"; + +const MAS_HTTP_LISTENERS: NonNullable["listeners"] = [ { - testRoomId: string; + name: "web", + resources: [ + { name: "discovery" }, + { name: "human" }, + { name: "oauth" }, + { name: "compat" }, + { name: "graphql" }, + { name: "assets" }, + { name: "adminapi" }, + ], + binds: [ + { + address: "[::]:8080", + }, + ], + proxy_protocol: false, }, { - guestHomeserver: StartedSynapseContainer; - bot: Credentials; - } ->({ + name: "internal", + resources: [ + { + name: "health", + }, + ], + binds: [ + { + address: "[::]:8081", + }, + ], + proxy_protocol: false, + }, +]; + +const MAS_CONFIG: Partial = { + http: { + listeners: MAS_HTTP_LISTENERS, + public_base: "", + }, + matrix: { + kind: "synapse", + homeserver: GUEST_HOMESERVER_NAME, + endpoint: GUEST_HOMESERVER_INTERNAL_URL, + secret: MAS_SHARED_SECRET, + }, + policy: { + data: { + admin_clients: [MAS_CLIENT_ID], + client_registration: { + allow_insecure_uris: true, + }, + }, + }, + clients: [ + { + client_id: MAS_CLIENT_ID, + client_auth_method: "client_secret_basic", + client_secret: MAS_CLIENT_SECRET, + }, + ], +}; + +const applySharedTestConfig = (testInstance: typeof base) => { + testInstance.use({ + displayName: "Tommy", + synapseConfig: { + allow_guest_access: true, + }, + labsFlags: ["feature_ask_to_join"], + }); +}; + +const sharedFixtures: Fixtures<{ testRoomId: string }, { bot: Credentials }, any, any> = { testRoomId: [ async ({ homeserver, bot }, use) => { - const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( - "POST", - "/v3/createRoom", - bot.accessToken, - { - name: "Test room", - preset: "public_chat", - topic: "All about happy hour", - initial_state: [ - { - // This is required to allow guests to join the room with this Synapse module - type: "m.room.join_rule", - state_key: "", - content: { join_rule: "knock" }, - }, - ], - }, - ); + const { room_id: roomId } = (await homeserver.csApi.request("POST", "/v3/createRoom", bot.accessToken, { + name: "Test room", + preset: "public_chat", + topic: "All about happy hour", + initial_state: [ + { + // This is required to allow guests to join the room with this Synapse module + type: "m.room.join_rule", + state_key: "", + content: { join_rule: "knock" }, + }, + ], + })) as { room_id: string }; await use(roomId); }, { scope: "test" }, @@ -51,13 +125,25 @@ const test = base.extend< }, { scope: "worker" }, ], +}; + +const test = base.extend< + { + testRoomId: string; + }, + { + guestHomeserver: StartedSynapseContainer; + bot: Credentials; + } +>({ + ...sharedFixtures, guestHomeserver: [ async ({ logger, synapseConfig, network }, use) => { const container = await new RestrictedGuestsSynapseContainer() .withConfig(synapseConfig) - .withConfig({ server_name: "guest-homeserver" }) + .withConfig({ server_name: GUEST_HOMESERVER_NAME }) .withNetwork(network) - .withNetworkAliases("guest-homeserver") + .withNetworkAliases(GUEST_HOMESERVER_NAME) .withLogConsumer(logger.getConsumer("guest_homeserver")) .start(); @@ -68,66 +154,123 @@ const test = base.extend< ], }); -test.use({ - displayName: "Tommy", - synapseConfig: { - allow_guest_access: true, +const masTest = base.extend< + { + testRoomId: string; }, - labsFlags: ["feature_ask_to_join"], + { + guestHomeserver: StartedSynapseContainer; + guestMas: StartedMatrixAuthenticationServiceContainer; + bot: Credentials; + } +>({ + ...sharedFixtures, + guestMas: [ + async ({ logger, network, postgres }, use) => { + const container = await new MatrixAuthenticationServiceContainer(postgres) + .withNetwork(network) + .withNetworkAliases("mas") + .withLogConsumer(logger.getConsumer("guest_mas")) + .withConfig(MAS_CONFIG) + .start(); + + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], + guestHomeserver: [ + async ({ logger, synapseConfig, network, guestMas }, use) => { + const container = await new RestrictedGuestsSynapseWithMasContainer({ + adminApiBaseUrl: MAS_INTERNAL_URL, + oauthBaseUrl: MAS_INTERNAL_URL, + clientId: MAS_CLIENT_ID, + clientSecret: MAS_CLIENT_SECRET, + }) + .withConfig(synapseConfig) + .withConfig({ + server_name: GUEST_HOMESERVER_NAME, + matrix_authentication_service: { + enabled: true, + endpoint: `${MAS_INTERNAL_URL}/`, + secret: MAS_SHARED_SECRET, + }, + } as Partial) + .withMatrixAuthenticationService(guestMas) + .withNetwork(network) + .withNetworkAliases(GUEST_HOMESERVER_NAME) + .withLogConsumer(logger.getConsumer("guest_homeserver")) + .start(); + + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], }); -test.describe("Restricted Guests", () => { - test.use({ - page: async ({ page, homeserver, guestHomeserver }, use) => { - await page.goto("/"); - await use(page); - }, - }); +type RestrictedGuestsTestInstance = typeof test; - test("should error if config is missing", async ({ page }) => { - await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); - await expect(page.getByText("Errors in module configuration")).toBeVisible(); - }); +const defineRestrictedGuestsTests = (testInstance: RestrictedGuestsTestInstance, suiteName: string) => { + applySharedTestConfig(testInstance); - test.describe("with config", () => { - test.beforeEach(({ config, guestHomeserver }) => { - config["io.element.element-web-modules.restricted-guests"] = { - guest_user_homeserver_url: guestHomeserver.baseUrl, - }; + testInstance.describe(suiteName, () => { + testInstance.use({ + page: async ({ page }, use) => { + await page.goto("/"); + await use(page); + }, }); - test( - "should show the default room preview bar for logged in users", - { tag: ["@screenshot"] }, - async ({ page, user, testRoomId }) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); + testInstance("should error if config is missing", async ({ page }) => { + await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + await expect(page.getByText("Errors in module configuration")).toBeVisible(); + }); - const button = page.getByRole("button", { name: "Join the discussion" }); - await expect(button).toBeVisible(); - }, - ); + testInstance.describe("with config", () => { + testInstance.beforeEach(({ config, guestHomeserver }) => { + config["io.element.element-web-modules.restricted-guests"] = { + guest_user_homeserver_url: guestHomeserver.baseUrl, + }; + }); - test( - "should show the module's room preview bar for guests", - { tag: ["@screenshot"] }, - async ({ page, testRoomId }) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); + testInstance( + "should show the default room preview bar for logged in users", + { tag: ["@screenshot"] }, + async ({ page, user, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); - const button = page.getByRole("button", { name: "Join", exact: true }); - await expect(button).toBeVisible(); - await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot("preview-bar.png"); + const button = page.getByRole("button", { name: "Join the discussion" }); + await expect(button).toBeVisible(); + }, + ); - await button.click(); - const dialog = page.getByRole("dialog"); - await expect(dialog).toMatchScreenshot("dialog.png"); + testInstance( + "should show the module's room preview bar for guests", + { tag: ["@screenshot"] }, + async ({ page, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); - await dialog.getByPlaceholder("Name").fill("Jim"); - await dialog.getByRole("button", { name: "Continue as guest" }).click(); + const button = page.getByRole("button", { name: "Join", exact: true }); + await expect(button).toBeVisible(); + await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot(`preview-bar.png`); - await expect(page.getByText("Ask to join?")).toBeVisible(); - }, - ); + await button.click(); + const dialog = page.getByRole("dialog"); + await expect(dialog).toMatchScreenshot(`dialog.png`); + + await dialog.getByPlaceholder("Name").fill("Jim"); + await dialog.getByRole("button", { name: "Continue as guest" }).click(); + + await expect(page.getByText("Ask to join?")).toBeVisible(); + }, + ); + }); }); -}); +}; + +// The screenshots between the two tests should be identical. +defineRestrictedGuestsTests(test, "Restricted Guests"); +defineRestrictedGuestsTests(masTest as RestrictedGuestsTestInstance, "Restricted Guests (MAS)"); diff --git a/modules/restricted-guests/element-web/tests/services.ts b/modules/restricted-guests/element-web/tests/services.ts index 6b56108b06..c0c9f4c33d 100644 --- a/modules/restricted-guests/element-web/tests/services.ts +++ b/modules/restricted-guests/element-web/tests/services.ts @@ -16,6 +16,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); // We use the SynapseContainer as a base to have all of its utilities for config setting export class RestrictedGuestsSynapseContainer extends SynapseContainer { + protected getModuleConfig(): Record { + return {}; + } + public override async start(): Promise { this.withCopyDirectoriesToContainer([ { @@ -27,9 +31,33 @@ export class RestrictedGuestsSynapseContainer extends SynapseContainer { }); this.config.modules.push({ module: "synapse_guest_module.GuestModule", - config: {}, + config: this.getModuleConfig(), }); return super.start(); } } + +export interface RestrictedGuestsMasModuleConfig { + adminApiBaseUrl: string; + oauthBaseUrl?: string; + clientId: string; + clientSecret: string; +} + +export class RestrictedGuestsSynapseWithMasContainer extends RestrictedGuestsSynapseContainer { + public constructor(private readonly masConfig: RestrictedGuestsMasModuleConfig) { + super(); + } + + protected override getModuleConfig(): Record { + return { + mas: { + admin_api_base_url: this.masConfig.adminApiBaseUrl, + oauth_base_url: this.masConfig.oauthBaseUrl ?? this.masConfig.adminApiBaseUrl, + client_id: this.masConfig.clientId, + client_secret: this.masConfig.clientSecret, + }, + }; + } +} From 1b3243cb0022087a39cb2caeabcef69d1fa1a273 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 20 Jan 2026 17:14:47 +0000 Subject: [PATCH 118/211] Disable local passwords in MAS Synapse Otherwise we'll get the following error upon Synapse startup: Password auth cannot be enabled while OAuth delegation is enabled. --- .../element-web/tests/restricted-guests.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index 964cbdbd46..31ef8fa8e1 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -195,6 +195,10 @@ const masTest = base.extend< endpoint: `${MAS_INTERNAL_URL}/`, secret: MAS_SHARED_SECRET, }, + // Must be disabled when using MAS. + password_config: { + enabled: false, + }, } as Partial) .withMatrixAuthenticationService(guestMas) .withNetwork(network) From 9a8ef99b8c2b29c11398f286d7ec26725f58d6b7 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 20 Jan 2026 17:17:20 +0000 Subject: [PATCH 119/211] Disable local registration when using MAS --- .../element-web/tests/restricted-guests.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index 31ef8fa8e1..6550f1f30d 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -199,6 +199,8 @@ const masTest = base.extend< password_config: { enabled: false, }, + // Must be disabled when using MAS. + enable_registration: false, } as Partial) .withMatrixAuthenticationService(guestMas) .withNetwork(network) From 1d9664ef1073de37437ec15e25f4f9fb87b0fd50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:05:52 +0000 Subject: [PATCH 120/211] Update npm non-major dependencies --- modules/banner/element-web/package.json | 2 +- modules/opendesk/element-web/package.json | 2 +- modules/restricted-guests/element-web/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/banner/element-web/package.json b/modules/banner/element-web/package.json index 3b57ac1d63..c78181b379 100644 --- a/modules/banner/element-web/package.json +++ b/modules/banner/element-web/package.json @@ -21,7 +21,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.24.0", + "vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-svgr": "^4.3.0" }, "dependencies": { diff --git a/modules/opendesk/element-web/package.json b/modules/opendesk/element-web/package.json index 57511e311b..653df09fa7 100644 --- a/modules/opendesk/element-web/package.json +++ b/modules/opendesk/element-web/package.json @@ -27,6 +27,6 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.24.0" + "vite-plugin-node-polyfills": "^0.25.0" } } diff --git a/modules/restricted-guests/element-web/package.json b/modules/restricted-guests/element-web/package.json index 00e82e31f0..8587318f64 100644 --- a/modules/restricted-guests/element-web/package.json +++ b/modules/restricted-guests/element-web/package.json @@ -21,7 +21,7 @@ "rollup-plugin-external-globals": "^0.13.0", "typescript": "^5.7.3", "vite": "^7.1.11", - "vite-plugin-node-polyfills": "^0.24.0" + "vite-plugin-node-polyfills": "^0.25.0" }, "dependencies": { "@vector-im/compound-design-tokens": "^6.0.0", From 5bc64a19da80ed501634acccd32d46e116a551df Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 2 Mar 2026 13:08:52 +0000 Subject: [PATCH 121/211] Update services.ts --- modules/restricted-guests/element-web/tests/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/restricted-guests/element-web/tests/services.ts b/modules/restricted-guests/element-web/tests/services.ts index c0c9f4c33d..8122e671b3 100644 --- a/modules/restricted-guests/element-web/tests/services.ts +++ b/modules/restricted-guests/element-web/tests/services.ts @@ -38,7 +38,7 @@ export class RestrictedGuestsSynapseContainer extends SynapseContainer { } } -export interface RestrictedGuestsMasModuleConfig { +interface RestrictedGuestsMasModuleConfig { adminApiBaseUrl: string; oauthBaseUrl?: string; clientId: string; From 5173170bf52da7f55336743d8fc6ae2dd6a9e984 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 3 Mar 2026 09:35:54 +0000 Subject: [PATCH 122/211] Adapt restricted guests module for Login UX --- .../element-web/src/AuthFooter.tsx | 59 +++++++++++++++++++ .../element-web/src/RegisterDialog.tsx | 13 ++-- .../element-web/src/RoomPreviewBar.tsx | 1 + .../element-web/src/config.ts | 3 + .../element-web/src/index.tsx | 24 ++++++-- .../element-web/src/translations.json | 8 +-- 6 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 modules/restricted-guests/element-web/src/AuthFooter.tsx diff --git a/modules/restricted-guests/element-web/src/AuthFooter.tsx b/modules/restricted-guests/element-web/src/AuthFooter.tsx new file mode 100644 index 0000000000..4b908ecb35 --- /dev/null +++ b/modules/restricted-guests/element-web/src/AuthFooter.tsx @@ -0,0 +1,59 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type FC } from "react"; +import { Button } from "@vector-im/compound-web"; +import { type AccountAuthInfo, type Api } from "@element-hq/element-web-module-api"; +import styled from "styled-components"; + +import { type ModuleConfig } from "./config.ts"; +import RegisterDialog from "./RegisterDialog.tsx"; + +interface Props { + api: Api; + config: ModuleConfig; + onLoggedIn(data: AccountAuthInfo): void; +} + +const Container = styled.aside` + margin: var(--cpd-space-3x) 0; + + button { + width: 100%; + } +`; + +const AuthFooter: FC = ({ api, config, onLoggedIn }) => { + const onTryJoin = async (): Promise => { + const { finished } = api.openDialog( + { + title: api.i18n.translate("register_dialog_title"), + }, + RegisterDialog, + { + api, + config, + }, + ); + + const { model: accountAuthInfo, ok } = await finished; + + if (ok && accountAuthInfo) { + onLoggedIn(accountAuthInfo); + } + }; + + return ( + + + + ); +}; + +export default AuthFooter; diff --git a/modules/restricted-guests/element-web/src/RegisterDialog.tsx b/modules/restricted-guests/element-web/src/RegisterDialog.tsx index 1c65708b03..1b445df251 100644 --- a/modules/restricted-guests/element-web/src/RegisterDialog.tsx +++ b/modules/restricted-guests/element-web/src/RegisterDialog.tsx @@ -15,6 +15,7 @@ import { type ModuleConfig } from "./config.ts"; interface RegisterDialogProps extends DialogProps { api: Api; config: ModuleConfig; + showLoginLink?: boolean; } const enum State { @@ -29,7 +30,7 @@ const StyledFormRoot = styled(Form.Root)` font-feature-settings: normal; `; -const RegisterDialog: FC = ({ api, config, onCancel, onSubmit }) => { +const RegisterDialog: FC = ({ api, config, onCancel, onSubmit, showLoginLink }) => { const [username, setUsername] = useState(""); const [state, setState] = useState(State.Idle); @@ -69,7 +70,7 @@ const RegisterDialog: FC = ({ api, config, onCancel, onSubm return ( - + {api.i18n.translate("register_dialog_register_username_label")} = ({ api, config, onCancel, onSubm {message} - - {api.i18n.translate("register_dialog_existing_account")} - + {showLoginLink && ( + + {api.i18n.translate("register_dialog_existing_account")} + + )} {api.i18n.translate("register_dialog_continue_label")} diff --git a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx index 75a2b9741e..bda49e57c5 100644 --- a/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx +++ b/modules/restricted-guests/element-web/src/RoomPreviewBar.tsx @@ -45,6 +45,7 @@ const RoomPreviewBar: FC = ({ api, config, roomId, roomAlia { api, config, + showLoginLink: true, }, ); diff --git a/modules/restricted-guests/element-web/src/config.ts b/modules/restricted-guests/element-web/src/config.ts index 07a86902c5..766a6571bb 100644 --- a/modules/restricted-guests/element-web/src/config.ts +++ b/modules/restricted-guests/element-web/src/config.ts @@ -40,5 +40,8 @@ export const CONFIG_KEY = "io.element.element-web-modules.restricted-guests"; declare module "@element-hq/element-web-module-api" { export interface Config { [CONFIG_KEY]: input; + sso_redirect_options?: { + immediate?: boolean; // incompatible option + }; } } diff --git a/modules/restricted-guests/element-web/src/index.tsx b/modules/restricted-guests/element-web/src/index.tsx index a28b5c6c5f..d11e4c0691 100644 --- a/modules/restricted-guests/element-web/src/index.tsx +++ b/modules/restricted-guests/element-web/src/index.tsx @@ -12,6 +12,7 @@ import Translations from "./translations.json"; import { ModuleConfig, CONFIG_KEY } from "./config"; import { name as ModuleName } from "../package.json"; import RoomPreviewBar from "./RoomPreviewBar.tsx"; +import AuthFooter from "./AuthFooter.tsx"; const GUEST_INVISIBLE_COMPONENTS = [ "UIComponent.sendInvites", @@ -41,14 +42,26 @@ class RestrictedGuestsModule implements Module { throw new Error(`Errors in module configuration for "${ModuleName}"`); } + const appConfig = this.api.config.get(); + if (appConfig.sso_redirect_options?.immediate) { + console.warn(`${ModuleName} found incompatible option 'sso_redirect_options.immediate', turning it off.`); + appConfig.sso_redirect_options.immediate = false; + } + + // Room preview bar customisations (for Matrix guest support) this.api.customComponents.registerRoomPreviewBar((props, OriginalComponent) => ( )); + this.api.customisations.registerShouldShowComponent(this.shouldShowComponent); - // TODO replace this with a more generic API - this.api._registerLegacyComponentVisibilityCustomisations(this); + // Login component customisations (for no guest support) + this.api.customComponents.registerLoginComponent((props, OriginalComponent) => ( + + + + )); } /** @@ -58,11 +71,12 @@ class RestrictedGuestsModule implements Module { * @returns true, if the user should see the component */ public readonly shouldShowComponent = (component: string): boolean => { - if (!this.config || !this.api.profile.value.userId?.startsWith(this.config.guest_user_prefix)) { - return true; + const profile = this.api.profile.value; + if (this.config && (profile.isGuest || profile.userId?.startsWith(this.config.guest_user_prefix))) { + return GUEST_INVISIBLE_COMPONENTS.includes(component); } - return GUEST_INVISIBLE_COMPONENTS.includes(component); + return true; }; } diff --git a/modules/restricted-guests/element-web/src/translations.json b/modules/restricted-guests/element-web/src/translations.json index bca1dc4a76..315a1e938d 100644 --- a/modules/restricted-guests/element-web/src/translations.json +++ b/modules/restricted-guests/element-web/src/translations.json @@ -4,8 +4,8 @@ "de": "Benutzername" }, "register_dialog_title": { - "en": "Request room access", - "de": "Raumbeitritt anfragen" + "en": "Request access", + "de": "Zugriff anfordern" }, "register_dialog_busy": { "en": "Creating your account...", @@ -32,7 +32,7 @@ "de": "Treten Sie dem Raum bei, um teilzunehmen" }, "join_cta": { - "en": "Join", - "de": "Verbinden" + "en": "Join as guest", + "de": "Als Gast beitreten" } } From adfd99ca7dabb27de54461390af08cdb426f6c13 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 3 Mar 2026 18:25:22 +0000 Subject: [PATCH 123/211] Tests galore --- .../tests/restricted-guests.spec.ts | 319 ++++++++++-------- .../dialog-linux.png | Bin 13444 -> 11810 bytes .../login-legacy-linux.png | Bin 0 -> 108178 bytes .../login-mas-linux.png | Bin 0 -> 65636 bytes .../preview-bar-linux.png | Bin 4055 -> 5179 bytes 5 files changed, 183 insertions(+), 136 deletions(-) create mode 100644 modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/login-legacy-linux.png create mode 100644 modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/login-mas-linux.png diff --git a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts index 6550f1f30d..341c023fe5 100644 --- a/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts +++ b/modules/restricted-guests/element-web/tests/restricted-guests.spec.ts @@ -10,20 +10,21 @@ import { type MasConfig, type StartedMatrixAuthenticationServiceContainer, type StartedSynapseContainer, - type SynapseConfig, + type SynapseContainer, } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; -import type { Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; -import type { Fixtures } from "@playwright/test"; +import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +import { type StartedNetwork } from "testcontainers"; +import { type Logger } from "@element-hq/element-web-playwright-common/lib/utils/logger.ts"; +import { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; -import { test as base, expect } from "../../../../playwright/element-web-test"; import { RestrictedGuestsSynapseContainer, RestrictedGuestsSynapseWithMasContainer } from "./services"; +import { test as subBase, expect } from "../../../../playwright/element-web-test"; const MAS_CLIENT_ID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"; const MAS_CLIENT_SECRET = "restricted-guests-secret"; const MAS_SHARED_SECRET = "restricted-guests-shared-secret"; -const MAS_INTERNAL_URL = "http://mas:8080"; +const MAS_INTERNAL_URL = "http://guest-mas:8080"; const GUEST_HOMESERVER_NAME = "guest-homeserver"; -const GUEST_HOMESERVER_INTERNAL_URL = "http://guest-homeserver:8008"; const MAS_HTTP_LISTENERS: NonNullable["listeners"] = [ { @@ -60,17 +61,11 @@ const MAS_HTTP_LISTENERS: NonNullable["listeners"] = [ }, ]; -const MAS_CONFIG: Partial = { +const BASE_MAS_CONFIG: Partial = { http: { listeners: MAS_HTTP_LISTENERS, public_base: "", }, - matrix: { - kind: "synapse", - homeserver: GUEST_HOMESERVER_NAME, - endpoint: GUEST_HOMESERVER_INTERNAL_URL, - secret: MAS_SHARED_SECRET, - }, policy: { data: { admin_clients: [MAS_CLIENT_ID], @@ -88,17 +83,43 @@ const MAS_CONFIG: Partial = { ], }; -const applySharedTestConfig = (testInstance: typeof base) => { - testInstance.use({ - displayName: "Tommy", - synapseConfig: { - allow_guest_access: true, - }, - labsFlags: ["feature_ask_to_join"], - }); -}; +declare module "@element-hq/element-web-module-api" { + export interface Config { + embedded_pages?: { + login_for_welcome?: boolean; + }; + } +} -const sharedFixtures: Fixtures<{ testRoomId: string }, { bot: Credentials }, any, any> = { +async function getMas( + name: string, + postgres: StartedPostgreSqlContainer, + network: StartedNetwork, + logger: Logger, + config: Partial, +): Promise { + const container = await new MatrixAuthenticationServiceContainer(postgres) + .withNetwork(network) + .withNetworkAliases(name) + .withLogConsumer(logger.getConsumer(name)) + .withConfig(config) + .start(); + return container; +} + +// We do some wacky things here in order to run the test suite against multiple homeserver configurations +const base = subBase.extend< + { + testRoomId: string; + }, + { + auth: "mas" | "legacy"; + + bot: Credentials; + guestMas?: StartedMatrixAuthenticationServiceContainer; + guestHomeserver: StartedSynapseContainer; + } +>({ testRoomId: [ async ({ homeserver, bot }, use) => { const { room_id: roomId } = (await homeserver.csApi.request("POST", "/v3/createRoom", bot.accessToken, { @@ -125,158 +146,184 @@ const sharedFixtures: Fixtures<{ testRoomId: string }, { bot: Credentials }, any }, { scope: "worker" }, ], -}; - -const test = base.extend< - { - testRoomId: string; - }, - { - guestHomeserver: StartedSynapseContainer; - bot: Credentials; - } ->({ - ...sharedFixtures, - guestHomeserver: [ - async ({ logger, synapseConfig, network }, use) => { - const container = await new RestrictedGuestsSynapseContainer() - .withConfig(synapseConfig) - .withConfig({ server_name: GUEST_HOMESERVER_NAME }) - .withNetwork(network) - .withNetworkAliases(GUEST_HOMESERVER_NAME) - .withLogConsumer(logger.getConsumer("guest_homeserver")) - .start(); + auth: ["mas", { scope: "worker" }], + // Optional MAS on the default homeserver, enabled only when we are testing the non-guest login UX + mas: [ + async ({ logger, network, postgres, auth, synapseConfig }, use) => { + if (auth !== "mas" || synapseConfig.allow_guest_access !== false) { + return use(undefined); + } + const container = await getMas("mas", postgres, network, logger, { + ...BASE_MAS_CONFIG, + matrix: { + kind: "synapse", + homeserver: "homeserver", + endpoint: "http://homeserver:8008", + secret: MAS_SHARED_SECRET, + }, + }); await use(container); await container.stop(); }, { scope: "worker" }, ], -}); - -const masTest = base.extend< - { - testRoomId: string; - }, - { - guestHomeserver: StartedSynapseContainer; - guestMas: StartedMatrixAuthenticationServiceContainer; - bot: Credentials; - } ->({ - ...sharedFixtures, + // Optional MAS on the module homeserver guestMas: [ - async ({ logger, network, postgres }, use) => { - const container = await new MatrixAuthenticationServiceContainer(postgres) + async ({ logger, network, auth }, use) => { + if (auth !== "mas") { + return use(undefined); + } + + // We need a separate postgres so it doesn't fight with the default MAS + const postgres = await new PostgreSqlContainer("postgres:13.3-alpine") .withNetwork(network) - .withNetworkAliases("mas") - .withLogConsumer(logger.getConsumer("guest_mas")) - .withConfig(MAS_CONFIG) + .withNetworkAliases("guest-mas-postgres") + .withLogConsumer(logger.getConsumer("guest-mas-postgres")) .start(); + const container = await getMas("guest-mas", postgres, network, logger, { + ...BASE_MAS_CONFIG, + matrix: { + kind: "synapse", + homeserver: GUEST_HOMESERVER_NAME, + endpoint: "http://guest-homeserver:8008", + secret: MAS_SHARED_SECRET, + }, + }); await use(container); await container.stop(); + await postgres.stop(); }, { scope: "worker" }, ], + // Module homeserver guestHomeserver: [ async ({ logger, synapseConfig, network, guestMas }, use) => { - const container = await new RestrictedGuestsSynapseWithMasContainer({ - adminApiBaseUrl: MAS_INTERNAL_URL, - oauthBaseUrl: MAS_INTERNAL_URL, - clientId: MAS_CLIENT_ID, - clientSecret: MAS_CLIENT_SECRET, - }) + let container: SynapseContainer; + if (guestMas) { + container = new RestrictedGuestsSynapseWithMasContainer({ + adminApiBaseUrl: MAS_INTERNAL_URL, + oauthBaseUrl: MAS_INTERNAL_URL, + clientId: MAS_CLIENT_ID, + clientSecret: MAS_CLIENT_SECRET, + }).withMatrixAuthenticationService(guestMas); + } else { + container = new RestrictedGuestsSynapseContainer(); + } + + const startedContainer = await container .withConfig(synapseConfig) .withConfig({ server_name: GUEST_HOMESERVER_NAME, - matrix_authentication_service: { - enabled: true, - endpoint: `${MAS_INTERNAL_URL}/`, - secret: MAS_SHARED_SECRET, - }, - // Must be disabled when using MAS. - password_config: { - enabled: false, - }, - // Must be disabled when using MAS. - enable_registration: false, - } as Partial) - .withMatrixAuthenticationService(guestMas) + }) .withNetwork(network) .withNetworkAliases(GUEST_HOMESERVER_NAME) .withLogConsumer(logger.getConsumer("guest_homeserver")) .start(); - await use(container); - await container.stop(); + await use(startedContainer); + await startedContainer.stop(); }, { scope: "worker" }, ], + displayName: "Tommy", + labsFlags: ["feature_ask_to_join"], + config: { + embedded_pages: { + login_for_welcome: true, + }, + }, + page: async ({ page }, use) => { + await page.goto("/"); + await use(page); + }, }); -type RestrictedGuestsTestInstance = typeof test; - -const defineRestrictedGuestsTests = (testInstance: RestrictedGuestsTestInstance, suiteName: string) => { - applySharedTestConfig(testInstance); - - testInstance.describe(suiteName, () => { - testInstance.use({ - page: async ({ page }, use) => { - await page.goto("/"); - await use(page); +for (const auth of ["mas", "legacy"] as const) { + for (const guestsEnabled of [true, false]) { + const test = base.extend({ + auth, + synapseConfig: { + allow_guest_access: guestsEnabled, }, }); - testInstance("should error if config is missing", async ({ page }) => { - await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); - await expect(page.getByText("Errors in module configuration")).toBeVisible(); - }); - - testInstance.describe("with config", () => { - testInstance.beforeEach(({ config, guestHomeserver }) => { - config["io.element.element-web-modules.restricted-guests"] = { - guest_user_homeserver_url: guestHomeserver.baseUrl, - }; + test.describe(`Restricted guests auth=${auth} guests=${guestsEnabled}`, () => { + test("should error if config is missing", async ({ page }) => { + await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); + await expect(page.getByText("Errors in module configuration")).toBeVisible(); }); - testInstance( - "should show the default room preview bar for logged in users", - { tag: ["@screenshot"] }, - async ({ page, user, testRoomId }) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); + test.describe("with config", () => { + test.beforeEach(({ config, guestHomeserver }) => { + config["io.element.element-web-modules.restricted-guests"] = { + guest_user_homeserver_url: guestHomeserver.baseUrl, + }; + }); - const button = page.getByRole("button", { name: "Join the discussion" }); - await expect(button).toBeVisible(); - }, - ); + if (guestsEnabled) { + // The screenshots between the two auth type tests for guests should be identical. + test( + "should show the default room preview bar for logged in users", + { tag: ["@screenshot"] }, + async ({ page, user, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); + const button = page.getByRole("button", { name: "Join the discussion" }); + await expect(button).toBeVisible(); + }, + ); - testInstance( - "should show the module's room preview bar for guests", - { tag: ["@screenshot"] }, - async ({ page, testRoomId }) => { - // Go to a room we are not a member of - await page.goto(`/#/room/${testRoomId}`); + test( + "should show the module's room preview bar for guests", + { tag: ["@screenshot"] }, + async ({ page, testRoomId }) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); - const button = page.getByRole("button", { name: "Join", exact: true }); - await expect(button).toBeVisible(); - await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot(`preview-bar.png`); + const button = page.getByRole("button", { name: "Join as guest", exact: true }); + await expect(button).toBeVisible(); + await expect(page.locator(".mx_RoomPreviewBar")).toMatchScreenshot(`preview-bar.png`); - await button.click(); - const dialog = page.getByRole("dialog"); - await expect(dialog).toMatchScreenshot(`dialog.png`); + await button.click(); + const dialog = page.getByRole("dialog"); + await expect(dialog).toMatchScreenshot(`dialog.png`); - await dialog.getByPlaceholder("Name").fill("Jim"); - await dialog.getByRole("button", { name: "Continue as guest" }).click(); + await dialog.getByPlaceholder("Name").fill("Jim"); + await dialog.getByRole("button", { name: "Continue as guest" }).click(); - await expect(page.getByText("Ask to join?")).toBeVisible(); - }, - ); + await expect(page.getByText("Ask to join?")).toBeVisible(); + }, + ); + } else { + test.use({ + page: async ({ page, testRoomId }, use) => { + // Go to a room we are not a member of + await page.goto(`/#/room/${testRoomId}`); + await use(page); + }, + user: async ({ pageWithCredentials, credentials }, use) => { + await use(credentials); + }, + }); + + test("should show the module login ux", { tag: ["@screenshot"] }, async ({ page }) => { + const button = page.getByRole("button", { name: "Join as guest", exact: true }); + await expect(button).toBeVisible(); + await expect(page.getByRole("main")).toMatchScreenshot(`login-${auth}.png`); + + await button.click(); + const dialog = page.getByRole("dialog"); + await expect(dialog).toMatchScreenshot(`dialog.png`); + + await dialog.getByPlaceholder("Name").fill("Jim"); + await dialog.getByRole("button", { name: "Continue as guest" }).click(); + + await expect(page.getByText("Join the discussion")).toBeVisible(); + }); + } + }); }); - }); -}; - -// The screenshots between the two tests should be identical. -defineRestrictedGuestsTests(test, "Restricted Guests"); -defineRestrictedGuestsTests(masTest as RestrictedGuestsTestInstance, "Restricted Guests (MAS)"); + } +} diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/dialog-linux.png index f053bea1d844e988dc8099ea411d7d0acf64d2d4..89cd54c942923977f2fb31d770ff2d2d7d9f8c9c 100644 GIT binary patch literal 11810 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 H48H#_Bao{5 literal 13444 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 DP%D7P diff --git a/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/login-legacy-linux.png b/modules/restricted-guests/element-web/tests/snapshots/restricted-guests.spec.ts/login-legacy-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..31d48fab8e7dd359f9adc70a933edce71d190c60 GIT binary patch literal 108178 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