From 3dde93bb69981dd5f43e461e1451ea5b91601b5f 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/156] Initial runtime Modules work --- packages/element-web-module-api/README.md | 16 + packages/element-web-module-api/package.json | 36 ++ .../src/@types/global.d.ts | 12 + packages/element-web-module-api/src/api.ts | 56 +++ packages/element-web-module-api/src/index.ts | 11 + .../src/legacy-customisations.ts | 204 +++++++++++ .../src/legacy-modules.ts | 20 + packages/element-web-module-api/src/loader.ts | 45 +++ packages/element-web-module-api/tsconfig.json | 8 + .../element-web-module-api/vite.config.ts | 34 ++ packages/element-web-module-api/yarn.lock | 344 ++++++++++++++++++ 11 files changed, 786 insertions(+) create mode 100644 packages/element-web-module-api/README.md create mode 100644 packages/element-web-module-api/package.json create mode 100644 packages/element-web-module-api/src/@types/global.d.ts create mode 100644 packages/element-web-module-api/src/api.ts create mode 100644 packages/element-web-module-api/src/index.ts create mode 100644 packages/element-web-module-api/src/legacy-customisations.ts create mode 100644 packages/element-web-module-api/src/legacy-modules.ts create mode 100644 packages/element-web-module-api/src/loader.ts create mode 100644 packages/element-web-module-api/tsconfig.json create mode 100644 packages/element-web-module-api/vite.config.ts create mode 100644 packages/element-web-module-api/yarn.lock diff --git a/packages/element-web-module-api/README.md b/packages/element-web-module-api/README.md new file mode 100644 index 0000000000..35572c1394 --- /dev/null +++ b/packages/element-web-module-api/README.md @@ -0,0 +1,16 @@ +# @element-hq/element-web-module-api + +API surface for extending Element Web in a safe & predictable way. + +This project is still in early development but aims to replace matrix-react-sdk-module-api and Element Web deprecated customisations. + +## 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/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json new file mode 100644 index 0000000000..8fb37e78f3 --- /dev/null +++ b/packages/element-web-module-api/package.json @@ -0,0 +1,36 @@ +{ + "name": "@element-hq/element-web-module-api", + "version": "0.1.0", + "description": "Module API surface for element-web", + "repository": "https://github.com/element-hq/element-web-modules", + "author": "element-hq", + "license": "SEE LICENSE IN README.md", + "type": "module", + "types": "./lib/index.d.ts", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/element-web-plugin-engine.js", + "require": "./lib/element-web-plugin-engine.umd.js" + } + }, + "devDependencies": { + "@types/node": "^22.10.7", + "@types/semver": "^7.5.8", + "semver": "^7.6.3", + "typescript": "^5.7.3", + "vite": "^6.0.11", + "vite-plugin-dts": "^4.5.0" + }, + "peerDependencies": { + "@matrix-org/react-sdk-module-api": "^2.5.0", + "@types/react": "*", + "@types/react-dom": "*", + "react": "^18" + }, + "peerDependenciesMeta": { + "@matrix-org/react-sdk-module-api": { + "optional": true + } + } +} diff --git a/packages/element-web-module-api/src/@types/global.d.ts b/packages/element-web-module-api/src/@types/global.d.ts new file mode 100644 index 0000000000..07eea17458 --- /dev/null +++ b/packages/element-web-module-api/src/@types/global.d.ts @@ -0,0 +1,12 @@ +/* +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. +*/ + +declare global { + var __VERSION__: string; // injected by vite +} + +export {}; diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts new file mode 100644 index 0000000000..f38b8e643c --- /dev/null +++ b/packages/element-web-module-api/src/api.ts @@ -0,0 +1,56 @@ +/* +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 { LegacyModuleApiExtension } from "./legacy-modules"; +import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; + +export interface Module { + load(): Promise; +} + +const moduleSignature: Record = { + load: "function", +}; + +export interface ModuleFactory { + readonly moduleApiVersion: string; + new (api: Api): Module; + readonly prototype: Module; +} + +const moduleFactorySignature: Record = { + moduleApiVersion: "string", + prototype: "object", +}; + +export interface ModuleExport { + default: ModuleFactory; +} + +const moduleExportSignature: Record = { + default: "object", +}; + +type Type = "function" | "string" | "number" | "boolean" | "object"; + +export function isInterface(obj: any, keys: Record): obj is T { + if (obj === null || typeof obj !== "object") return false; + for (const key in keys) { + if (typeof obj[key] !== keys[key]) return false; + } + return true; +} + +export function isModule(module: any): module is ModuleExport { + return ( + isInterface(module, moduleExportSignature) && + isInterface(module.default, moduleFactorySignature) && + isInterface(module.default.prototype, moduleSignature) + ); +} + +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension {} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts new file mode 100644 index 0000000000..c739e9d24c --- /dev/null +++ b/packages/element-web-module-api/src/index.ts @@ -0,0 +1,11 @@ +/* +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. +*/ + +export { ModuleLoader } from "./loader"; +export type { Api, Module, ModuleFactory } from "./api"; +export type * from "./legacy-modules"; +export type * from "./legacy-customisations"; diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts new file mode 100644 index 0000000000..e3ac489cf7 --- /dev/null +++ b/packages/element-web-module-api/src/legacy-customisations.ts @@ -0,0 +1,204 @@ +/* +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. +*/ + +/** + * The types here suck but these customisations are deprecated and will be removed soon. + */ + +export interface AliasCustomisations { + // E.g. prefer one of the aliases over another + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +export interface ChatExportCustomisations { + /** + * Force parameters in room chat export fields returned here are forced + * and not allowed to be edited in the chat export form + */ + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + // must be < 10**8 + // only used when range is 'LastNMessages' + // default is 100 + numberOfMessages?: number; + includeAttachments?: boolean; + // maximum size of exported archive + // must be > 0 and < 8000 + sizeMb?: number; + }; +} + +export interface ComponentVisibilityCustomisations { + /** + * 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: + | "UIComponent.sendInvites" + | "UIComponent.roomCreation" + | "UIComponent.spaceCreation" + | "UIComponent.exploreRooms" + | "UIComponent.addIntegrations" + | "UIComponent.filterContainer" + | "UIComponent.roomOptionsMenu", + ): boolean; +} + +export interface DirectoryCustomisations { + requireCanonicalAliasAccessToPublish?(): boolean; +} + +export interface LifecycleCustomisations { + onLoggedOutAndStorageCleared?(): void; +} + +export interface Media { + readonly isEncrypted: boolean; + readonly srcMxc: string; + readonly thumbnailMxc: string | null | undefined; + readonly hasThumbnail: boolean; + readonly srcHttp: string | null; + readonly thumbnailHttp: string | null; + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getSquareThumbnailHttp(dim: number): string | null; + downloadSource(): Promise; +} + +export interface MediaContructable { + new (prepared: { mxc: string; thumbnail?: any; file?: any }): Media; +} + +export interface MediaCustomisations { + readonly Media: MediaContructable; + mediaFromContent(content: Content, client?: Client): Media; + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +export interface RoomListCustomisations { + /** + * Determines if a room is visible in the room list or not. By default, + * all rooms are visible. Where special handling is performed by Element, + * those rooms will not be able to override their visibility in the room + * list - Element will make the decision without calling this function. + * + * This function should be as fast as possible to avoid slowing down the + * client. + * @param {Room} room The room to check the visibility of. + * @returns {boolean} True if the room should be visible, false otherwise. + */ + isRoomVisible?(room: Room): boolean; +} + +export interface UserIdentifierCustomisations { + /** + * Customise display of the user identifier + * hide userId for guests, display 3pid + * + * Set withDisplayName to true when user identifier will be displayed alongside user name + */ + getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; +} + +export interface WidgetPermissionsCustomisations { + /** + * Approves the widget for capabilities that it requested, if any can be + * approved. Typically this will be used to give certain widgets capabilities + * without having to prompt the user to approve them. This cannot reject + * capabilities that Element will be automatically granting, such as the + * ability for Jitsi widgets to stay on screen - those will be approved + * regardless. + * @param {Widget} widget The widget to approve capabilities for. + * @param {Set} requestedCapabilities The capabilities the widget requested. + * @returns {Set} Resolves to the capabilities that are approved for use + * by the widget. If none are approved, this should return an empty Set. + */ + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +export interface WidgetVariablesCustomisations { + /** + * Provides a partial set of the variables needed to render any widget. If + * variables are missing or not provided then they will be filled with the + * application-determined defaults. + * + * This will not be called until after isReady() resolves. + * @returns The variables. + */ + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; + /** + * Resolves to whether or not the customisation point is ready for variables + * to be provided. This will block widgets being rendered. + * If not provided, the app will assume that the customisation is always ready. + * @returns {Promise} Resolves when ready. + */ + isReady?(): Promise; +} + +export type LegacyCustomisations = (customisations: T) => void; + +export interface LegacyCustomisationsApiExtension { + /** + * @deprecated + */ + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + /** + * @deprecated + */ + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + /** + * @deprecated + */ + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + /** + * @deprecated + */ + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + /** + * @deprecated + */ + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + /** + * @deprecated + */ + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + /** + * @deprecated + */ + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + /** + * @deprecated + */ + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + /** + * @deprecated + */ + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< + WidgetPermissionsCustomisations + >; + /** + * @deprecated + */ + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/legacy-modules.ts new file mode 100644 index 0000000000..30a5755f21 --- /dev/null +++ b/packages/element-web-module-api/src/legacy-modules.ts @@ -0,0 +1,20 @@ +/* +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. +*/ + +// @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed +import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; + +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +export interface LegacyModuleApiExtension { + /** + * Register a legacy module based on @matrix-org/react-sdk-module-api + * @param LegacyModule the module class to register + * @deprecated provided only as a transition path for legacy modules + */ + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} diff --git a/packages/element-web-module-api/src/loader.ts b/packages/element-web-module-api/src/loader.ts new file mode 100644 index 0000000000..6cc04d5aec --- /dev/null +++ b/packages/element-web-module-api/src/loader.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 { satisfies } from "semver"; + +import { Api, isModule, Module, ModuleExport } from "./api"; + +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string) { + super(`Plugin version ${pluginVersion} is incompatible with engine version ${__VERSION__}`); + } +} + +export class ModuleLoader { + public constructor(private api: Api) {} + + #modules: Module[] = []; + public async load(moduleExport: ModuleExport): Promise { + if (this.#started) { + throw new Error("PluginEngine.start() has already been called"); + } + + if (!isModule(moduleExport)) { + throw new Error("Invalid plugin"); + } + if (!satisfies(__VERSION__, moduleExport.default.moduleApiVersion)) { + throw new ModuleIncompatibleError(moduleExport.default.moduleApiVersion); + } + this.#modules.push(new moduleExport.default(this.api)); + } + + #started = false; + public async start(): Promise { + if (this.#started) { + throw new Error("PluginEngine.start() has already been called"); + } + this.#started = true; + + await Promise.all(this.#modules.map((plugin) => plugin.load())); + } +} diff --git a/packages/element-web-module-api/tsconfig.json b/packages/element-web-module-api/tsconfig.json new file mode 100644 index 0000000000..6b52074a9f --- /dev/null +++ b/packages/element-web-module-api/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/packages/element-web-module-api/vite.config.ts b/packages/element-web-module-api/vite.config.ts new file mode 100644 index 0000000000..75670d9ccd --- /dev/null +++ b/packages/element-web-module-api/vite.config.ts @@ -0,0 +1,34 @@ +/* +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 dts from "vite-plugin-dts"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "element-web-plugin-engine", + fileName: "element-web-plugin-engine", + }, + outDir: "lib", + target: "esnext", + sourcemap: true, + }, + plugins: [ + dts({ + rollupTypes: true, + }), + ], + define: { + __VERSION__: JSON.stringify(process.env.npm_package_version), + }, +}); diff --git a/packages/element-web-module-api/yarn.lock b/packages/element-web-module-api/yarn.lock new file mode 100644 index 0000000000..bd4e3d6693 --- /dev/null +++ b/packages/element-web-module-api/yarn.lock @@ -0,0 +1,344 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@esbuild/aix-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" + integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + +"@esbuild/android-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" + integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + +"@esbuild/android-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" + integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + +"@esbuild/android-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" + integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + +"@esbuild/darwin-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" + integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + +"@esbuild/darwin-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" + integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + +"@esbuild/freebsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" + integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + +"@esbuild/freebsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" + integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + +"@esbuild/linux-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" + integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + +"@esbuild/linux-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" + integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + +"@esbuild/linux-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" + integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + +"@esbuild/linux-loong64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" + integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + +"@esbuild/linux-mips64el@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" + integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + +"@esbuild/linux-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" + integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + +"@esbuild/linux-riscv64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" + integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + +"@esbuild/linux-s390x@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" + integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + +"@esbuild/linux-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + +"@esbuild/netbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" + integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + +"@esbuild/netbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" + integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + +"@esbuild/openbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" + integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + +"@esbuild/openbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" + integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + +"@esbuild/sunos-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" + integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + +"@esbuild/win32-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" + integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + +"@esbuild/win32-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" + integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + +"@esbuild/win32-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" + integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + +"@rollup/rollup-android-arm-eabi@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz#d4dd60da0075a6ce9a6c76d71b8204f3e1822285" + integrity sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA== + +"@rollup/rollup-android-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz#25c4d33259a7a2ccd2f52a5ffcc0bb3ab3f0729d" + integrity sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g== + +"@rollup/rollup-darwin-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz#d137dff254b19163a6b52ac083a71cd055dae844" + integrity sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g== + +"@rollup/rollup-darwin-x64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz#58ff20b5dacb797d3adca19f02a21c532f9d55bf" + integrity sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ== + +"@rollup/rollup-freebsd-arm64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz#96ce1a241c591ec3e068f4af765d94eddb24e60c" + integrity sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew== + +"@rollup/rollup-freebsd-x64@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz#e59e7ede505be41f0b4311b0b943f8eb44938467" + integrity sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA== + +"@rollup/rollup-linux-arm-gnueabihf@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz#e455ca6e4ff35bd46d62201c153352e717000a7b" + integrity sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw== + +"@rollup/rollup-linux-arm-musleabihf@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz#bc1a93d807d19e70b1e343a5bfea43723bcd6327" + integrity sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg== + +"@rollup/rollup-linux-arm64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz#f38bf843f1dc3d5de680caf31084008846e3efae" + integrity sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA== + +"@rollup/rollup-linux-arm64-musl@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz#b3987a96c18b7287129cf735be2dbf83e94d9d05" + integrity sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g== + +"@rollup/rollup-linux-loongarch64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz#0f0324044e71c4f02e9f49e7ec4e347b655b34ee" + integrity sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz#809479f27f1fd5b4eecd2aa732132ad952d454ba" + integrity sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ== + +"@rollup/rollup-linux-riscv64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz#7bc75c4f22db04d3c972f83431739cfa41c6a36e" + integrity sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw== + +"@rollup/rollup-linux-s390x-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz#cfe8052345c55864d83ae343362cf1912480170e" + integrity sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ== + +"@rollup/rollup-linux-x64-gnu@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz#c6b048f1e25f3fea5b4bd246232f4d07a159c5a0" + integrity sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g== + +"@rollup/rollup-linux-x64-musl@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz#615273ac52d1a201f4de191cbd3389016a9d7d80" + integrity sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA== + +"@rollup/rollup-win32-arm64-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz#32ed85810c1b831c648eca999d68f01255b30691" + integrity sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw== + +"@rollup/rollup-win32-ia32-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz#d47effada68bcbfdccd30c4a788d42e4542ff4d3" + integrity sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ== + +"@rollup/rollup-win32-x64-msvc@4.31.0": + version "4.31.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz#7a2d89a82cf0388d60304964217dd7beac6de645" + integrity sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw== + +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/node@^22.10.7": + version "22.10.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7" + integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== + dependencies: + undici-types "~6.20.0" + +esbuild@^0.24.2: + version "0.24.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" + integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.2" + "@esbuild/android-arm" "0.24.2" + "@esbuild/android-arm64" "0.24.2" + "@esbuild/android-x64" "0.24.2" + "@esbuild/darwin-arm64" "0.24.2" + "@esbuild/darwin-x64" "0.24.2" + "@esbuild/freebsd-arm64" "0.24.2" + "@esbuild/freebsd-x64" "0.24.2" + "@esbuild/linux-arm" "0.24.2" + "@esbuild/linux-arm64" "0.24.2" + "@esbuild/linux-ia32" "0.24.2" + "@esbuild/linux-loong64" "0.24.2" + "@esbuild/linux-mips64el" "0.24.2" + "@esbuild/linux-ppc64" "0.24.2" + "@esbuild/linux-riscv64" "0.24.2" + "@esbuild/linux-s390x" "0.24.2" + "@esbuild/linux-x64" "0.24.2" + "@esbuild/netbsd-arm64" "0.24.2" + "@esbuild/netbsd-x64" "0.24.2" + "@esbuild/openbsd-arm64" "0.24.2" + "@esbuild/openbsd-x64" "0.24.2" + "@esbuild/sunos-x64" "0.24.2" + "@esbuild/win32-arm64" "0.24.2" + "@esbuild/win32-ia32" "0.24.2" + "@esbuild/win32-x64" "0.24.2" + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +postcss@^8.4.49: + version "8.5.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" + integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +rollup@^4.23.0: + version "4.31.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.31.0.tgz#b84af969a0292cb047dce2c0ec5413a9457597a4" + integrity sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.31.0" + "@rollup/rollup-android-arm64" "4.31.0" + "@rollup/rollup-darwin-arm64" "4.31.0" + "@rollup/rollup-darwin-x64" "4.31.0" + "@rollup/rollup-freebsd-arm64" "4.31.0" + "@rollup/rollup-freebsd-x64" "4.31.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.31.0" + "@rollup/rollup-linux-arm-musleabihf" "4.31.0" + "@rollup/rollup-linux-arm64-gnu" "4.31.0" + "@rollup/rollup-linux-arm64-musl" "4.31.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.31.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.31.0" + "@rollup/rollup-linux-riscv64-gnu" "4.31.0" + "@rollup/rollup-linux-s390x-gnu" "4.31.0" + "@rollup/rollup-linux-x64-gnu" "4.31.0" + "@rollup/rollup-linux-x64-musl" "4.31.0" + "@rollup/rollup-win32-arm64-msvc" "4.31.0" + "@rollup/rollup-win32-ia32-msvc" "4.31.0" + "@rollup/rollup-win32-x64-msvc" "4.31.0" + fsevents "~2.3.2" + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +typescript@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== + +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +vite@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.11.tgz#224497e93e940b34c3357c9ebf2ec20803091ed8" + integrity sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg== + dependencies: + esbuild "^0.24.2" + postcss "^8.4.49" + rollup "^4.23.0" + optionalDependencies: + fsevents "~2.3.3" From 5bc85e8ae4e3742d181f66a55616e0fa3d625e32 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 11:03:02 +0000 Subject: [PATCH 002/156] Iterate --- packages/element-web-module-api/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 8fb37e78f3..2e01680e89 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -14,6 +14,10 @@ "require": "./lib/element-web-plugin-engine.umd.js" } }, + "scripts": { + "lint:ts": "tsc --noEmit", + "lint:js": "eslint --max-warnings 0 src" + }, "devDependencies": { "@types/node": "^22.10.7", "@types/semver": "^7.5.8", From e71f9e327ee24b7e8e5b020c222a068138e969fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 11:27:53 +0000 Subject: [PATCH 003/156] Iterate --- packages/element-web-module-api/package.json | 3 +-- .../src/@types/global.d.ts | 1 + packages/element-web-module-api/src/api.ts | 4 ++-- .../src/legacy-customisations.ts | 16 ++++++++-------- .../element-web-module-api/src/legacy-modules.ts | 2 ++ packages/element-web-module-api/src/loader.ts | 5 +++-- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 2e01680e89..d817ca4936 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -15,8 +15,7 @@ } }, "scripts": { - "lint:ts": "tsc --noEmit", - "lint:js": "eslint --max-warnings 0 src" + "lint:ts": "tsc --noEmit" }, "devDependencies": { "@types/node": "^22.10.7", diff --git a/packages/element-web-module-api/src/@types/global.d.ts b/packages/element-web-module-api/src/@types/global.d.ts index 07eea17458..82ae781eb5 100644 --- a/packages/element-web-module-api/src/@types/global.d.ts +++ b/packages/element-web-module-api/src/@types/global.d.ts @@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details. */ declare global { + // eslint-disable-next-line no-var var __VERSION__: string; // injected by vite } diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index f38b8e643c..5d01f31b62 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -37,7 +37,7 @@ const moduleExportSignature: Record = { type Type = "function" | "string" | "number" | "boolean" | "object"; -export function isInterface(obj: any, keys: Record): obj is T { +export function isInterface(obj: unknown, keys: Record): obj is T { if (obj === null || typeof obj !== "object") return false; for (const key in keys) { if (typeof obj[key] !== keys[key]) return false; @@ -45,7 +45,7 @@ export function isInterface(obj: any, keys: Record): obj is T return true; } -export function isModule(module: any): module is ModuleExport { +export function isModule(module: unknown): module is ModuleExport { return ( isInterface(module, moduleExportSignature) && isInterface(module.default, moduleFactorySignature) && diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts index e3ac489cf7..753f8ff107 100644 --- a/packages/element-web-module-api/src/legacy-customisations.ts +++ b/packages/element-web-module-api/src/legacy-customisations.ts @@ -77,12 +77,12 @@ export interface Media { downloadSource(): Promise; } -export interface MediaContructable { - new (prepared: { mxc: string; thumbnail?: any; file?: any }): Media; +export interface MediaContructable { + new (prepared: { mxc: string; thumbnail?: Thumbnail; file?: File }): Media; } -export interface MediaCustomisations { - readonly Media: MediaContructable; +export interface MediaCustomisations { + readonly Media: MediaContructable; mediaFromContent(content: Content, client?: Client): Media; mediaFromMxc(mxc?: string, client?: Client): Media; } @@ -166,7 +166,7 @@ export interface LegacyCustomisationsApiExtension { /** * @deprecated */ - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; /** * @deprecated */ @@ -182,11 +182,11 @@ export interface LegacyCustomisationsApiExtension { /** * @deprecated */ - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; /** * @deprecated */ - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; /** * @deprecated */ @@ -195,7 +195,7 @@ export interface LegacyCustomisationsApiExtension { * @deprecated */ readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< - WidgetPermissionsCustomisations + WidgetPermissionsCustomisations >; /** * @deprecated diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/legacy-modules.ts index 30a5755f21..8e7cc82ca8 100644 --- a/packages/element-web-module-api/src/legacy-modules.ts +++ b/packages/element-web-module-api/src/legacy-modules.ts @@ -5,11 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +/* eslint-disable @typescript-eslint/naming-convention */ export interface LegacyModuleApiExtension { /** * Register a legacy module based on @matrix-org/react-sdk-module-api diff --git a/packages/element-web-module-api/src/loader.ts b/packages/element-web-module-api/src/loader.ts index 6cc04d5aec..8f19cbb627 100644 --- a/packages/element-web-module-api/src/loader.ts +++ b/packages/element-web-module-api/src/loader.ts @@ -10,7 +10,7 @@ import { satisfies } from "semver"; import { Api, isModule, Module, ModuleExport } from "./api"; export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string) { + public constructor(pluginVersion: string) { super(`Plugin version ${pluginVersion} is incompatible with engine version ${__VERSION__}`); } } @@ -30,7 +30,8 @@ export class ModuleLoader { if (!satisfies(__VERSION__, moduleExport.default.moduleApiVersion)) { throw new ModuleIncompatibleError(moduleExport.default.moduleApiVersion); } - this.#modules.push(new moduleExport.default(this.api)); + const { default: Module } = moduleExport; + this.#modules.push(new Module(this.api)); } #started = false; From 0e8315493e19a3876881f90a5df1dc96bea073eb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 11:31:38 +0000 Subject: [PATCH 004/156] Iterate --- packages/element-web-module-api/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index 5d01f31b62..73d1e23535 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -40,7 +40,7 @@ type Type = "function" | "string" | "number" | "boolean" | "object"; export function isInterface(obj: unknown, keys: Record): obj is T { if (obj === null || typeof obj !== "object") return false; for (const key in keys) { - if (typeof obj[key] !== keys[key]) return false; + if (typeof (obj as Record)[key] !== keys[key]) return false; } return true; } From 35b33ac893d7cc8ce3e4db30a3f70424d6e6f68c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 11:52:33 +0000 Subject: [PATCH 005/156] Iterate --- packages/element-web-module-api/package.json | 8 +++++++- packages/element-web-module-api/src/api.ts | 2 +- packages/element-web-module-api/src/index.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index d817ca4936..7898159820 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -18,22 +18,28 @@ "lint:ts": "tsc --noEmit" }, "devDependencies": { + "@matrix-org/react-sdk-module-api": "^2.5.0", "@types/node": "^22.10.7", "@types/semver": "^7.5.8", + "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", "typescript": "^5.7.3", "vite": "^6.0.11", "vite-plugin-dts": "^4.5.0" }, "peerDependencies": { - "@matrix-org/react-sdk-module-api": "^2.5.0", + "@matrix-org/react-sdk-module-api": "*", "@types/react": "*", "@types/react-dom": "*", + "matrix-web-i18n": "*", "react": "^18" }, "peerDependenciesMeta": { "@matrix-org/react-sdk-module-api": { "optional": true + }, + "matrix-web-i18n": { + "optional": true } } } diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index 73d1e23535..b016b796c9 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -37,7 +37,7 @@ const moduleExportSignature: Record = { type Type = "function" | "string" | "number" | "boolean" | "object"; -export function isInterface(obj: unknown, keys: Record): obj is T { +function isInterface(obj: unknown, keys: Record): obj is T { if (obj === null || typeof obj !== "object") return false; for (const key in keys) { if (typeof (obj as Record)[key] !== keys[key]) return false; diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index c739e9d24c..d105ffdb8a 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.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. */ -export { ModuleLoader } from "./loader"; +export { ModuleLoader, ModuleIncompatibleError } from "./loader"; export type { Api, Module, ModuleFactory } from "./api"; export type * from "./legacy-modules"; export type * from "./legacy-customisations"; From 505533a5f550b66976495372a5e621b8519011b4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Jan 2025 17:38:52 +0000 Subject: [PATCH 006/156] Iterate --- packages/element-web-module-api/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 7898159820..950f2b362e 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -15,10 +15,12 @@ } }, "scripts": { + "prepare": "vite build", "lint:ts": "tsc --noEmit" }, "devDependencies": { "@matrix-org/react-sdk-module-api": "^2.5.0", + "@types/react": "^18", "@types/node": "^22.10.7", "@types/semver": "^7.5.8", "matrix-web-i18n": "^3.3.0", From b0c81d53d922d94ac29388412805eeb067f739e1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 08:35:40 +0000 Subject: [PATCH 007/156] Iterate --- packages/element-web-module-api/src/api.ts | 17 ++++++++++++++++- packages/element-web-module-api/src/index.ts | 2 +- .../src/legacy-customisations.ts | 10 +++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index b016b796c9..06ddcaf26b 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -53,4 +53,19 @@ export function isModule(module: unknown): module is ModuleExport { ); } -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension {} +export interface Config { + // The branding name of the application + brand: string; + // Other config options are available but not specified in the types as that would make it difficult to change for element-web + // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md +} + +export interface ConfigApi { + get(): Config; + get(key: K): Config[K]; + get(key?: K): Config | Config[K]; +} + +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { + config: ConfigApi; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index d105ffdb8a..0b2bf9435c 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -6,6 +6,6 @@ Please see LICENSE files in the repository root for full details. */ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; -export type { Api, Module, ModuleFactory } from "./api"; +export type { Api, Module, ModuleFactory, Config, ConfigApi } from "./api"; export type * from "./legacy-modules"; export type * from "./legacy-customisations"; diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts index 753f8ff107..bc1d963cd4 100644 --- a/packages/element-web-module-api/src/legacy-customisations.ts +++ b/packages/element-web-module-api/src/legacy-customisations.ts @@ -77,12 +77,12 @@ export interface Media { downloadSource(): Promise; } -export interface MediaContructable { - new (prepared: { mxc: string; thumbnail?: Thumbnail; file?: File }): Media; +export interface MediaContructable { + new (prepared: PreparedMedia): Media; } -export interface MediaCustomisations { - readonly Media: MediaContructable; +export interface MediaCustomisations { + readonly Media: MediaContructable; mediaFromContent(content: Content, client?: Client): Media; mediaFromMxc(mxc?: string, client?: Client): Media; } @@ -182,7 +182,7 @@ export interface LegacyCustomisationsApiExtension { /** * @deprecated */ - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; /** * @deprecated */ From 3d94420f9f5f25f8c4cc5bf410cb5d7a5d9465cd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 09:02:46 +0000 Subject: [PATCH 008/156] Iterate --- packages/element-web-module-api/README.md | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/element-web-module-api/README.md b/packages/element-web-module-api/README.md index 35572c1394..b5cdac7d96 100644 --- a/packages/element-web-module-api/README.md +++ b/packages/element-web-module-api/README.md @@ -4,6 +4,54 @@ API surface for extending Element Web in a safe & predictable way. This project is still in early development but aims to replace matrix-react-sdk-module-api and Element Web deprecated customisations. +## Using the API + +Modules are loaded by Element Web at runtime via a dynamic ecmascript import, but can be bundled into a webapp for deployment convenience. + +The module's default export MUST be a class which accepts a single argument, an instance of `ModuleApi`. +This class must also bear a static property `moduleApiVersion` which is a semver range string +and a `load` method which is called when the module is to be loaded. + +```typescript +import type { Module, Api, ModuleFactory } from "@element-hq/element-web-module-api"; + +class ExampleModule implements Module { + public static readonly moduleApiVersion = "^0.1.0"; + + public constructor(private api: Api) {} + + public async load(): Promise { + // Your extension code goes here + } +} + +export default ExampleModule satisfies ModuleFactory; +``` + +### Accessing application configuration + +The `api` object passed to the module constructor provides access to the application configuration. +You can extend the Config types using declaration merging, though please ensure that you do not trust the types you specify, +and opt for runtime validation due to the dynamic nature of the configuration. + +```typescript +// ... +declare module "@element-hq/element-web-module-api" { + interface Config { + "this.is.my.config.key": string; + } +} + +class ExampleModule implements Module { + // ... + public async load(): Promise { + const configValue = this.api.config.get("this.is.my.config.key"); + // Your extension code goes here + } +} +// ... +``` + ## Copyright & License Copyright (c) 2025 New Vector Ltd From 3a5baec69958d37e882caa82e880ab44a04edf20 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 10:48:35 +0000 Subject: [PATCH 009/156] Iterate --- .../element-web-module-api/api-extractor.json | 454 ++++++++++++++++++ .../element-web-module-api.api.md | 224 +++++++++ packages/element-web-module-api/package.json | 12 +- packages/element-web-module-api/src/api.ts | 21 + .../src/legacy-customisations.ts | 93 +++- .../src/legacy-modules.ts | 12 +- packages/element-web-module-api/src/loader.ts | 8 + packages/element-web-module-api/tsconfig.json | 4 +- .../element-web-module-api/vite.config.ts | 6 +- 9 files changed, 803 insertions(+), 31 deletions(-) create mode 100644 packages/element-web-module-api/api-extractor.json create mode 100644 packages/element-web-module-api/element-web-module-api.api.md diff --git a/packages/element-web-module-api/api-extractor.json b/packages/element-web-module-api/api-extractor.json new file mode 100644 index 0000000000..e945efc023 --- /dev/null +++ b/packages/element-web-module-api/api-extractor.json @@ -0,0 +1,454 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/lib/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we might specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + * + * The "bundledPackages" elements may specify glob patterns using minimatch syntax. To ensure deterministic + * output, globs are expanded by matching explicitly declared top-level dependencies only. For example, + * the pattern below will NOT match "@my-company/example" unless it appears in a field such as "dependencies" + * or "devDependencies" of the project's package.json file: + * + * "bundledPackages": [ "@my-company/*" ], + */ + "bundledPackages": [], + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output + * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify + * "preserve". + * + * DEFAULT VALUE: "by-name" + */ + // "enumMemberOrder": "by-name", + + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The base filename for the API report files, to be combined with "reportFolder" or "reportTempFolder" + * to produce the full file path. The "reportFileName" should not include any path separators such as + * "\" or "/". The "reportFileName" should not include a file extension, since API Extractor will automatically + * append an appropriate file extension such as ".api.md". If the "reportVariants" setting is used, then the + * file extension includes the variant name, for example "my-report.public.api.md" or "my-report.beta.api.md". + * The "complete" variant always uses the simple extension "my-report.api.md". + * + * Previous versions of API Extractor required "reportFileName" to include the ".api.md" extension explicitly; + * for backwards compatibility, that is still accepted but will be discarded before applying the above rules. + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: "" + */ + // "reportFileName": "", + + /** + * To support different approval requirements for different API levels, multiple "variants" of the API report can + * be generated. The "reportVariants" setting specifies a list of variants to be generated. If omitted, + * by default only the "complete" variant will be generated, which includes all @internal, @alpha, @beta, + * and @public items. Other possible variants are "alpha" (@alpha + @beta + @public), "beta" (@beta + @public), + * and "public" (@public only). + * + * DEFAULT VALUE: [ "complete" ] + */ + // "reportVariants": ["public", "beta"], + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + "reportFolder": "/" + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/", + + /** + * Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json", + + /** + * Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false, + + /** + * The base URL where the project's source code can be viewed on a website such as GitHub or + * Azure DevOps. This URL path corresponds to the `` path on disk. + * + * This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. + * For example, if the `projectFolderUrl` is "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor" and an API + * item's file path is "api/ExtractorConfig.ts", the full URL file path would be + * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". + * + * This setting can be omitted if you don't need source code links in your API documentation reference. + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "projectFolderUrl": "http://github.com/path/to/your/projectFolder" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true, + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + "untrimmedFilePath": "/lib/.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + "alphaTrimmedFilePath": "/lib/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + "betaTrimmedFilePath": "/lib/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + "publicTrimmedFilePath": "/lib/-public.d.ts" + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md new file mode 100644 index 0000000000..e70f3295f5 --- /dev/null +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -0,0 +1,224 @@ +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ModuleApi } from "@matrix-org/react-sdk-module-api"; +import { RuntimeModule } from "@matrix-org/react-sdk-module-api"; + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { + // (undocumented) + config: ConfigApi; +} + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?( + component: + | "UIComponent.sendInvites" + | "UIComponent.roomCreation" + | "UIComponent.spaceCreation" + | "UIComponent.exploreRooms" + | "UIComponent.addIntegrations" + | "UIComponent.filterContainer" + | "UIComponent.roomOptionsMenu", + ): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< + WidgetPermissionsCustomisations + >; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier( + userId: string, + opts: { + roomId?: string; + withDisplayName?: boolean; + }, + ): string | null; +} + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 950f2b362e..c3b8093a61 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -6,22 +6,26 @@ "author": "element-hq", "license": "SEE LICENSE IN README.md", "type": "module", - "types": "./lib/index.d.ts", + "types": "./lib/element-web-module-api-alpha.d.ts", "exports": { ".": { - "types": "./lib/index.d.ts", + "types": "./lib/element-web-module-api-alpha.d.ts", "import": "./lib/element-web-plugin-engine.js", "require": "./lib/element-web-plugin-engine.umd.js" } }, + "files": [ + "lib" + ], "scripts": { - "prepare": "vite build", + "prepare": "vite build && api-extractor run", "lint:ts": "tsc --noEmit" }, "devDependencies": { "@matrix-org/react-sdk-module-api": "^2.5.0", - "@types/react": "^18", + "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", + "@types/react": "^18", "@types/semver": "^7.5.8", "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index 06ddcaf26b..1ab56aadd4 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -8,6 +8,10 @@ Please see LICENSE files in the repository root for full details. import { LegacyModuleApiExtension } from "./legacy-modules"; import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; +/** + * Module interface for modules to implement. + * @public + */ export interface Module { load(): Promise; } @@ -16,6 +20,10 @@ const moduleSignature: Record = { load: "function", }; +/** + * Module interface for modules to export as the default export. + * @public + */ export interface ModuleFactory { readonly moduleApiVersion: string; new (api: Api): Module; @@ -53,6 +61,11 @@ export function isModule(module: unknown): module is ModuleExport { ); } +/** + * The configuration for the application. + * Should be extended via declaration merging. + * @public + */ export interface Config { // The branding name of the application brand: string; @@ -60,12 +73,20 @@ export interface Config { // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md } +/** + * API for accessing the configuration. + * @public + */ export interface ConfigApi { get(): Config; get(key: K): Config[K]; get(key?: K): Config | Config[K]; } +/** + * The API for modules to interact with the application. + * @public + */ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { config: ConfigApi; } diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts index bc1d963cd4..6374789b5b 100644 --- a/packages/element-web-module-api/src/legacy-customisations.ts +++ b/packages/element-web-module-api/src/legacy-customisations.ts @@ -9,11 +9,19 @@ Please see LICENSE files in the repository root for full details. * The types here suck but these customisations are deprecated and will be removed soon. */ +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface AliasCustomisations { // E.g. prefer one of the aliases over another getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface ChatExportCustomisations { /** * Force parameters in room chat export fields returned here are forced @@ -33,6 +41,10 @@ export interface ChatExportCustomisations { }; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface ComponentVisibilityCustomisations { /** * Determines whether or not the active MatrixClient user should be able to use @@ -40,9 +52,8 @@ export interface ComponentVisibilityCustomisations { * 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. + * @param component - The component to check visibility for. + * @returns True (default) if the user is able to see the component, false otherwise. */ shouldShowComponent?( component: @@ -56,14 +67,26 @@ export interface ComponentVisibilityCustomisations { ): boolean; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface LifecycleCustomisations { onLoggedOutAndStorageCleared?(): void; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface Media { readonly isEncrypted: boolean; readonly srcMxc: string; @@ -77,16 +100,28 @@ export interface Media { downloadSource(): Promise; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface MediaContructable { new (prepared: PreparedMedia): Media; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface MediaCustomisations { readonly Media: MediaContructable; mediaFromContent(content: Content, client?: Client): Media; mediaFromMxc(mxc?: string, client?: Client): Media; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface RoomListCustomisations { /** * Determines if a room is visible in the room list or not. By default, @@ -96,12 +131,16 @@ export interface RoomListCustomisations { * * This function should be as fast as possible to avoid slowing down the * client. - * @param {Room} room The room to check the visibility of. - * @returns {boolean} True if the room should be visible, false otherwise. + * @param room - The room to check the visibility of. + * @returns True if the room should be visible, false otherwise. */ isRoomVisible?(room: Room): boolean; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface UserIdentifierCustomisations { /** * Customise display of the user identifier @@ -112,6 +151,10 @@ export interface UserIdentifierCustomisations { getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface WidgetPermissionsCustomisations { /** * Approves the widget for capabilities that it requested, if any can be @@ -120,14 +163,18 @@ export interface WidgetPermissionsCustomisations { * capabilities that Element will be automatically granting, such as the * ability for Jitsi widgets to stay on screen - those will be approved * regardless. - * @param {Widget} widget The widget to approve capabilities for. - * @param {Set} requestedCapabilities The capabilities the widget requested. - * @returns {Set} Resolves to the capabilities that are approved for use + * @param widget - The widget to approve capabilities for. + * @param requestedCapabilities - The capabilities the widget requested. + * @returns Resolves to the capabilities that are approved for use * by the widget. If none are approved, this should return an empty Set. */ preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface WidgetVariablesCustomisations { /** * Provides a partial set of the variables needed to render any widget. If @@ -151,54 +198,62 @@ export interface WidgetVariablesCustomisations { * Resolves to whether or not the customisation point is ready for variables * to be provided. This will block widgets being rendered. * If not provided, the app will assume that the customisation is always ready. - * @returns {Promise} Resolves when ready. + * @returns a promise which resolves when ready. */ isReady?(): Promise; } +/** + * @alpha + * @deprecated in favour of the new Module API + */ export type LegacyCustomisations = (customisations: T) => void; +/** + * @alpha + * @deprecated in favour of the new Module API + */ export interface LegacyCustomisationsApiExtension { /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyAliasCustomisations: LegacyCustomisations; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< WidgetPermissionsCustomisations >; /** - * @deprecated + * @deprecated in favour of the new Module API */ readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; } diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/legacy-modules.ts index 8e7cc82ca8..baaad96b01 100644 --- a/packages/element-web-module-api/src/legacy-modules.ts +++ b/packages/element-web-module-api/src/legacy-modules.ts @@ -9,13 +9,21 @@ Please see LICENSE files in the repository root for full details. // @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; +/** + * @alpha + * @deprecated in favour of the new module API + */ export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +/** + * @alpha + * @deprecated in favour of the new module API + */ /* eslint-disable @typescript-eslint/naming-convention */ export interface LegacyModuleApiExtension { /** - * Register a legacy module based on @matrix-org/react-sdk-module-api - * @param LegacyModule the module class to register + * Register a legacy module based on \@matrix-org/react-sdk-module-api + * @param LegacyModule - the module class to register * @deprecated provided only as a transition path for legacy modules */ _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; diff --git a/packages/element-web-module-api/src/loader.ts b/packages/element-web-module-api/src/loader.ts index 8f19cbb627..0b303a19df 100644 --- a/packages/element-web-module-api/src/loader.ts +++ b/packages/element-web-module-api/src/loader.ts @@ -9,12 +9,20 @@ import { satisfies } from "semver"; import { Api, isModule, Module, ModuleExport } from "./api"; +/** + * Error thrown when a module is incompatible with the engine version. + * @public + */ export class ModuleIncompatibleError extends Error { public constructor(pluginVersion: string) { super(`Plugin version ${pluginVersion} is incompatible with engine version ${__VERSION__}`); } } +/** + * A module loader for loading and starting modules. + * @public + */ export class ModuleLoader { public constructor(private api: Api) {} diff --git a/packages/element-web-module-api/tsconfig.json b/packages/element-web-module-api/tsconfig.json index 6b52074a9f..3dee636915 100644 --- a/packages/element-web-module-api/tsconfig.json +++ b/packages/element-web-module-api/tsconfig.json @@ -1,8 +1,10 @@ { + "$schema": "http://json.schemastore.org/tsconfig", "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "lib", - "jsx": "react-jsx" + "jsx": "react-jsx", + "declarationMap": true }, "include": ["src"] } diff --git a/packages/element-web-module-api/vite.config.ts b/packages/element-web-module-api/vite.config.ts index 75670d9ccd..0fee8e9834 100644 --- a/packages/element-web-module-api/vite.config.ts +++ b/packages/element-web-module-api/vite.config.ts @@ -23,11 +23,7 @@ export default defineConfig({ target: "esnext", sourcemap: true, }, - plugins: [ - dts({ - rollupTypes: true, - }), - ], + plugins: [dts()], define: { __VERSION__: JSON.stringify(process.env.npm_package_version), }, From 2edfce5e7a00f2286700a7fb79db9a63176f7e88 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 10:50:56 +0000 Subject: [PATCH 010/156] Iterate --- packages/element-web-module-api/src/loader.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/element-web-module-api/src/loader.ts b/packages/element-web-module-api/src/loader.ts index 0b303a19df..0cc2141ca2 100644 --- a/packages/element-web-module-api/src/loader.ts +++ b/packages/element-web-module-api/src/loader.ts @@ -24,11 +24,13 @@ export class ModuleIncompatibleError extends Error { * @public */ export class ModuleLoader { + private modules: Module[] = []; + private started = false; + public constructor(private api: Api) {} - #modules: Module[] = []; public async load(moduleExport: ModuleExport): Promise { - if (this.#started) { + if (this.started) { throw new Error("PluginEngine.start() has already been called"); } @@ -39,16 +41,15 @@ export class ModuleLoader { throw new ModuleIncompatibleError(moduleExport.default.moduleApiVersion); } const { default: Module } = moduleExport; - this.#modules.push(new Module(this.api)); + this.modules.push(new Module(this.api)); } - #started = false; public async start(): Promise { - if (this.#started) { + if (this.started) { throw new Error("PluginEngine.start() has already been called"); } - this.#started = true; + this.started = true; - await Promise.all(this.#modules.map((plugin) => plugin.load())); + await Promise.all(this.modules.map((plugin) => plugin.load())); } } From 776d07730a73c0a9bd7964da128e30cb0d819530 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Jan 2025 10:56:48 +0000 Subject: [PATCH 011/156] Prettier -_- --- .../element-web-module-api.api.md | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index e70f3295f5..e47e7428f8 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -3,8 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { ModuleApi } from "@matrix-org/react-sdk-module-api"; -import { RuntimeModule } from "@matrix-org/react-sdk-module-api"; + +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; // @alpha @deprecated (undocumented) export interface AliasCustomisations { @@ -34,16 +35,7 @@ export interface ChatExportCustomisations { // @alpha @deprecated (undocumented) export interface ComponentVisibilityCustomisations { - shouldShowComponent?( - component: - | "UIComponent.sendInvites" - | "UIComponent.roomCreation" - | "UIComponent.spaceCreation" - | "UIComponent.exploreRooms" - | "UIComponent.addIntegrations" - | "UIComponent.filterContainer" - | "UIComponent.roomOptionsMenu", - ): boolean; + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; } // @public @@ -90,9 +82,7 @@ export interface LegacyCustomisationsApiExtension { // @deprecated (undocumented) readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< - WidgetPermissionsCustomisations - >; + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; // @deprecated (undocumented) readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; } @@ -191,13 +181,10 @@ export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; // @alpha @deprecated (undocumented) export interface UserIdentifierCustomisations { - getDisplayUserIdentifier( - userId: string, - opts: { - roomId?: string; - withDisplayName?: boolean; - }, - ): string | null; + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; } // @alpha @deprecated (undocumented) @@ -221,4 +208,5 @@ export interface WidgetVariablesCustomisations { } // (No @packageDocumentation comment for this package) + ``` From 06fcf783a13d76a575e51e4630779d7067c367ff Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 30 Jan 2025 10:57:18 +0000 Subject: [PATCH 012/156] Fix isModule check not working for new class modules --- packages/element-web-module-api/src/api.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts index 1ab56aadd4..5534411375 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api.ts @@ -40,13 +40,13 @@ export interface ModuleExport { } const moduleExportSignature: Record = { - default: "object", + default: "function", }; type Type = "function" | "string" | "number" | "boolean" | "object"; -function isInterface(obj: unknown, keys: Record): obj is T { - if (obj === null || typeof obj !== "object") return false; +function isInterface(obj: unknown, type: "object" | "function", keys: Record): obj is T { + if (obj === null || typeof obj !== type) return false; for (const key in keys) { if (typeof (obj as Record)[key] !== keys[key]) return false; } @@ -55,9 +55,9 @@ function isInterface(obj: unknown, keys: Record): obj is T { export function isModule(module: unknown): module is ModuleExport { return ( - isInterface(module, moduleExportSignature) && - isInterface(module.default, moduleFactorySignature) && - isInterface(module.default.prototype, moduleSignature) + isInterface(module, "object", moduleExportSignature) && + isInterface(module.default, "function", moduleFactorySignature) && + isInterface(module.default.prototype, "object", moduleSignature) ); } From 0a2f311cc8a312c8b34cf8042fe5dc87a85e8b33 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 30 Jan 2025 10:58:23 +0000 Subject: [PATCH 013/156] Wire up tests & sonarcloud --- packages/element-web-module-api/package.json | 8 +++++-- .../element-web-module-api/src/api.test.ts | 24 +++++++++++++++++++ .../element-web-module-api/vite.config.ts | 18 ++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 packages/element-web-module-api/src/api.test.ts diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index c3b8093a61..2c7f17038a 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -19,7 +19,8 @@ ], "scripts": { "prepare": "vite build && api-extractor run", - "lint:ts": "tsc --noEmit" + "lint:ts": "tsc --noEmit", + "test": "vitest" }, "devDependencies": { "@matrix-org/react-sdk-module-api": "^2.5.0", @@ -27,11 +28,14 @@ "@types/node": "^22.10.7", "@types/react": "^18", "@types/semver": "^7.5.8", + "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", "typescript": "^5.7.3", "vite": "^6.0.11", - "vite-plugin-dts": "^4.5.0" + "vite-plugin-dts": "^4.5.0", + "vitest": "^3.0.4", + "vitest-sonar-reporter": "^2.0.0" }, "peerDependencies": { "@matrix-org/react-sdk-module-api": "*", diff --git a/packages/element-web-module-api/src/api.test.ts b/packages/element-web-module-api/src/api.test.ts new file mode 100644 index 0000000000..f68200f122 --- /dev/null +++ b/packages/element-web-module-api/src/api.test.ts @@ -0,0 +1,24 @@ +/* +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 { expect, test } from "vitest"; + +import { Api, isModule } from "./api.js"; + +const TestModule = { + default: class TestModule { + public static moduleApiVersion = "1.0.0"; + public constructor(private readonly api: Api) {} + public async load(): Promise { + console.log("TestModule loaded"); + } + }, +}; + +test("isModule correctly identifies valid modules", () => { + expect(isModule(TestModule)).toBe(true); +}); diff --git a/packages/element-web-module-api/vite.config.ts b/packages/element-web-module-api/vite.config.ts index 0fee8e9834..6b0d47a8a2 100644 --- a/packages/element-web-module-api/vite.config.ts +++ b/packages/element-web-module-api/vite.config.ts @@ -27,4 +27,22 @@ export default defineConfig({ define: { __VERSION__: JSON.stringify(process.env.npm_package_version), }, + test: { + coverage: { + provider: "v8", + include: ["src/**/*"], + reporter: "lcov", + }, + reporters: [ + [ + "vitest-sonar-reporter", + { + outputFile: "coverage/sonar-report.xml", + onWritePath(path: string): string { + return `packages/element-web-module-api/${path}`; + }, + }, + ], + ], + }, }); From 8d05c5329e44ececc62a0e771e448b94bd285e6d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 30 Jan 2025 15:12:15 +0000 Subject: [PATCH 014/156] Iterate --- .../element-web-module-api/src/api.test.ts | 7 +-- .../element-web-module-api/src/loader.spec.ts | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 packages/element-web-module-api/src/loader.spec.ts diff --git a/packages/element-web-module-api/src/api.test.ts b/packages/element-web-module-api/src/api.test.ts index f68200f122..62dd3973aa 100644 --- a/packages/element-web-module-api/src/api.test.ts +++ b/packages/element-web-module-api/src/api.test.ts @@ -7,15 +7,14 @@ Please see LICENSE files in the repository root for full details. import { expect, test } from "vitest"; -import { Api, isModule } from "./api.js"; +import { Api } from "."; +import { isModule } from "./api.js"; const TestModule = { default: class TestModule { public static moduleApiVersion = "1.0.0"; public constructor(private readonly api: Api) {} - public async load(): Promise { - console.log("TestModule loaded"); - } + public async load(): Promise {} }, }; diff --git a/packages/element-web-module-api/src/loader.spec.ts b/packages/element-web-module-api/src/loader.spec.ts new file mode 100644 index 0000000000..85270cce38 --- /dev/null +++ b/packages/element-web-module-api/src/loader.spec.ts @@ -0,0 +1,58 @@ +/* +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 { expect, test, describe, vi, beforeEach, afterEach } from "vitest"; + +import { Api, ModuleIncompatibleError, ModuleLoader } from "."; + +describe("ModuleIncompatibleError", () => { + test("should extend Error", () => { + expect(new ModuleIncompatibleError("1.0.0")).toBeInstanceOf(Error); + }); +}); + +describe("ModuleLoader", () => { + const mockApi = {} as Api; + + beforeEach(() => { + vi.stubGlobal("__VERSION__", "1.0.1"); + }); + + test("should load a module", async () => { + const TestModule = { + default: class TestModule { + public static moduleApiVersion = "^1.0.0"; + public constructor(private readonly api: Api) {} + public async load(): Promise {} + }, + }; + + const spy = vi.spyOn(TestModule.default.prototype, "load"); + + const loader = new ModuleLoader(mockApi); + await loader.load(TestModule); + await loader.start(); + expect(spy).toHaveBeenCalledWith(); + }); + + test("should fail to load an incompatible module", async () => { + const TestModule = { + default: class TestModule { + public static moduleApiVersion = "^2"; + public constructor(private readonly api: Api) {} + public async load(): Promise {} + }, + }; + + const spy = vi.spyOn(TestModule.default.prototype, "load"); + + const loader = new ModuleLoader(mockApi); + await expect(loader.load(TestModule)).rejects.toThrowError(ModuleIncompatibleError); + await loader.start(); + expect(spy).not.toHaveBeenCalledWith(); + }); +}); From af435b4f1a5bae2398403867c5e01dd6be22c0ca Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 30 Jan 2025 15:14:30 +0000 Subject: [PATCH 015/156] Delint --- packages/element-web-module-api/src/loader.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/loader.spec.ts b/packages/element-web-module-api/src/loader.spec.ts index 85270cce38..853b6d3eb6 100644 --- a/packages/element-web-module-api/src/loader.spec.ts +++ b/packages/element-web-module-api/src/loader.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 { expect, test, describe, vi, beforeEach, afterEach } from "vitest"; +import { expect, test, describe, vi, beforeEach } from "vitest"; import { Api, ModuleIncompatibleError, ModuleLoader } from "."; From dd521f5a21e8a35952934bcaa9e1d1c1cdfa39bd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 31 Jan 2025 09:17:08 +0000 Subject: [PATCH 016/156] v0.1.1 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 2c7f17038a..a0f2c27d72 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,6 +1,6 @@ { "name": "@element-hq/element-web-module-api", - "version": "0.1.0", + "version": "0.1.1", "description": "Module API surface for element-web", "repository": "https://github.com/element-hq/element-web-modules", "author": "element-hq", From 47be004a2c2dce89650447e6aa6719b2eacbc584 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 31 Jan 2025 09:40:51 +0000 Subject: [PATCH 017/156] Fix sonar-project.properties and be consistent with test naming --- .../element-web-module-api/src/{loader.spec.ts => loader.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/element-web-module-api/src/{loader.spec.ts => loader.test.ts} (100%) diff --git a/packages/element-web-module-api/src/loader.spec.ts b/packages/element-web-module-api/src/loader.test.ts similarity index 100% rename from packages/element-web-module-api/src/loader.spec.ts rename to packages/element-web-module-api/src/loader.test.ts From e1c29f6ff10b5cf0e5d4ef8e60a7dadad9479bd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:18:06 +0000 Subject: [PATCH 018/156] Bump vitest in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vitest` from 3.0.4 to 3.0.5 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v3.0.5/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index a0f2c27d72..7ff2a25cad 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -34,7 +34,7 @@ "typescript": "^5.7.3", "vite": "^6.0.11", "vite-plugin-dts": "^4.5.0", - "vitest": "^3.0.4", + "vitest": "^3.0.5", "vitest-sonar-reporter": "^2.0.0" }, "peerDependencies": { From 1744b18955dbbc2cfe672e859c645a06ee3ff05b Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 13 Feb 2025 11:37:53 -0500 Subject: [PATCH 019/156] Make "yarn test" always enable coverage --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index a0f2c27d72..836f01ae06 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -20,7 +20,7 @@ "scripts": { "prepare": "vite build && api-extractor run", "lint:ts": "tsc --noEmit", - "test": "vitest" + "test": "vitest --coverage" }, "devDependencies": { "@matrix-org/react-sdk-module-api": "^2.5.0", From d45edc8c4de3b4b47537092d8efc05d53d59076a Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 13 Feb 2025 16:04:03 -0500 Subject: [PATCH 020/156] Include Python in static analysis job Also refactor "yarn lint" of each module/package to run whatever lint task is appropriate for it --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 7b07c0c9d4..5114e602be 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -19,7 +19,7 @@ ], "scripts": { "prepare": "vite build && api-extractor run", - "lint:ts": "tsc --noEmit", + "lint": "tsc --noEmit", "test": "vitest --coverage" }, "devDependencies": { From 0d32376f8d05dcae808f91ffc39406b4010ed843 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 17 Feb 2025 09:49:12 +0000 Subject: [PATCH 021/156] Add docker bake environment with support for building an Element Web docker image with build & runtime module loader with support for building docker images with a module pre-baked in --- packages/element-web-module-api/Dockerfile | 19 ++++++++++ packages/element-web-module-api/README.md | 22 +++++++++++ .../17-fetch-element-modules.sh | 38 +++++++++++++++++++ .../18-load-element-modules.sh | 33 ++++++++++++++++ .../nginx-templates/default.conf.template | 24 ++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 packages/element-web-module-api/Dockerfile create mode 100755 packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh create mode 100755 packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh create mode 100644 packages/element-web-module-api/docker/nginx-templates/default.conf.template diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile new file mode 100644 index 0000000000..70f589b8c5 --- /dev/null +++ b/packages/element-web-module-api/Dockerfile @@ -0,0 +1,19 @@ +ARG ELEMENT_VERSION=latest +FROM vectorim/element-web:${ELEMENT_VERSION} +ARG ELEMENT_WEB_MODULES="" + +# Override default nginx config. Templates in `/etc/nginx/templates` are passed +# through `envsubst` by the nginx docker image entry point. +COPY /docker/nginx-templates/* /etc/nginx/templates/ +COPY /docker/docker-entrypoint.d/* /docker-entrypoint.d/ + +# Create directories before we gain privileges +RUN mkdir -p /tmp/element-web-config /tmp/element-web-modules + +# Escalate privileges to install jq and moreutils and run the fetch modules script +USER root +RUN apk add jq moreutils +RUN ELEMENT_WEB_MODULES=${ELEMENT_WEB_MODULES} /docker-entrypoint.d/17-fetch-element-modules.sh + +# Drop privileges back to nginx +USER nginx \ No newline at end of file diff --git a/packages/element-web-module-api/README.md b/packages/element-web-module-api/README.md index b5cdac7d96..d1f3c94fc5 100644 --- a/packages/element-web-module-api/README.md +++ b/packages/element-web-module-api/README.md @@ -4,6 +4,28 @@ API surface for extending Element Web in a safe & predictable way. This project is still in early development but aims to replace matrix-react-sdk-module-api and Element Web deprecated customisations. +## Using the Docker image + +The docker image specified by the Dockerfile in this directory can be used in one of four ways. + +You can specify `ELEMENT_WEB_MODULES` as a build-arg or as a runtime environment variable to fetch & load a module +from a remote URL at runtime or to bundle it into the docker image for easier deployment respectively. +The format this variable should take is a comma-delimited list of URLs followed by the optional tarball hash after a `#` character, e.g. +`ELEMENT_WEB_MODULES=https://example.com/module.tgz#abc123,https://example.com/another-module.tgz#`. + +You can also use it as a base image and add your desired modules into `/tmp/element-web-modules` each in their own directory. +Finally, you can bind mount modules into the `/tmp/element-web-modules` directory at runtime. +The default entrypoint will be index.js in that directory but can be overriden if a package.json file is found with a `main` directive. + +The container expects a config.json file to be bind mounted or copied into the `/app/config.json` path. +The container runs an nginx web server in rootless mode on port 8080. +If you wish to use docker in read-only mode, you should follow the [upstream instructions](https://hub.docker.com/_/nginx#:~:text=Running%20nginx%20in%20read%2Donly%20mode) +but additionally include the following directories: + +- /tmp/element-web-modules/ +- /tmp/element-web-config/ +- /etc/nginx/conf.d/ + ## Using the API Modules are loaded by Element Web at runtime via a dynamic ecmascript import, but can be bundled into a webapp for deployment convenience. diff --git a/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh b/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh new file mode 100755 index 0000000000..ff57e90d53 --- /dev/null +++ b/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Loads modules specified via env var ELEMENT_WEB_MODULES +# in a comma delimited format of `url#hash,url#hash,...` +# the URL should point to a gzipped tarball, e.g. https://registry.npmjs.org/level/-/level-9.0.0.tgz +# the hash should a sha256sum of the tgz/tar.gz archive, it can be omitted though +# Runs both during the build stage and the runtime entrypoint for added flexibility + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +if [ -z "${ELEMENT_WEB_MODULES}" ]; then + entrypoint_log "ELEMENT_WEB_MODULES is not set, skipping module loading" + exit 0 +fi + +for MODULE in ${ELEMENT_WEB_MODULES//,/ } +do + MODULE_URL=${MODULE%#*} + EXPECTED_HASH=${MODULE#*#} + + entrypoint_log "Fetching module from $MODULE_URL" + wget -O /tmp/element-web-modules/module.tar.gz "$MODULE_URL" + HASH=$(sha256sum /tmp/element-web-modules/module.tar.gz | awk '{ print $1 }') + if [ -n "$EXPECTED_HASH" ] && [ "$HASH" != "$EXPECTED_HASH" ]; then + echo "Hash mismatch for $MODULE_URL: expected $EXPECTED_HASH, got $HASH" + exit 1 + fi + + mkdir -p "/tmp/element-web-modules/$HASH" + tar xvf /tmp/element-web-modules/module.tar.gz -C "/tmp/element-web-modules/$HASH" --strip-components=1 + rm -Rf /tmp/element-web-modules/module.tar.gz +done \ No newline at end of file diff --git a/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh b/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh new file mode 100755 index 0000000000..fe08194de0 --- /dev/null +++ b/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Loads modules from `/tmp/element-web-modules` into config.json's `modules` field + +set -e + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +# If there are modules to be loaded +if [ -d "/tmp/element-web-modules" ]; then + cd /tmp/element-web-modules + + # Copy these config files as a base + cp /app/config*.json /tmp/element-web-config/ + + for MODULE in * + do + # If the module has a package.json, use its main field as the entrypoint + ENTRYPOINT="index.js" + if [ -f "/tmp/element-web-modules/$MODULE/package.json" ]; then + ENTRYPOINT=$(jq -r '.main' "/tmp/element-web-modules/$MODULE/package.json") + fi + + entrypoint_log "Loading module $MODULE with entrypoint $ENTRYPOINT" + + # Append the module to the config + jq ".modules += [\"/modules/$MODULE/$ENTRYPOINT\"]" /tmp/element-web-config/config.json | sponge /tmp/element-web-config/config.json + done +fi \ No newline at end of file diff --git a/packages/element-web-module-api/docker/nginx-templates/default.conf.template b/packages/element-web-module-api/docker/nginx-templates/default.conf.template new file mode 100644 index 0000000000..83813ddcd2 --- /dev/null +++ b/packages/element-web-module-api/docker/nginx-templates/default.conf.template @@ -0,0 +1,24 @@ +server { + listen 8080; + server_name localhost; + + root /app; + index index.html; + + location = /index.html { + add_header Cache-Control "no-cache"; + } + location = /version { + add_header Cache-Control "no-cache"; + } + # covers config.json and config.hostname.json requests as it is prefix. + location /config { + root /tmp/element-web-config; + add_header Cache-Control "no-cache"; + } + location /modules { + root /tmp/element-web-modules; + } + error_page 500 502 503 504 /50x.html; +} + From 3a729d048706022100c368c59980762333e9e87c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 25 Feb 2025 15:29:56 +0000 Subject: [PATCH 022/156] Stash docker & bake --- packages/element-web-module-api/Dockerfile | 32 +++++++++------- packages/element-web-module-api/README.md | 26 ++----------- .../17-fetch-element-modules.sh | 38 ------------------- .../18-load-element-modules.sh | 33 ---------------- .../nginx-templates/default.conf.template | 24 ------------ 5 files changed, 22 insertions(+), 131 deletions(-) delete mode 100755 packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh delete mode 100755 packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh delete mode 100644 packages/element-web-module-api/docker/nginx-templates/default.conf.template diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 70f589b8c5..a858dc6aed 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,19 +1,23 @@ ARG ELEMENT_VERSION=latest -FROM vectorim/element-web:${ELEMENT_VERSION} -ARG ELEMENT_WEB_MODULES="" -# Override default nginx config. Templates in `/etc/nginx/templates` are passed -# through `envsubst` by the nginx docker image entry point. -COPY /docker/nginx-templates/* /etc/nginx/templates/ -COPY /docker/docker-entrypoint.d/* /docker-entrypoint.d/ +FROM --platform=$BUILDPLATFORM node:lts-alpine AS builder -# Create directories before we gain privileges -RUN mkdir -p /tmp/element-web-config /tmp/element-web-modules +ARG BUILD_CONTEXT -# Escalate privileges to install jq and moreutils and run the fetch modules script -USER root -RUN apk add jq moreutils -RUN ELEMENT_WEB_MODULES=${ELEMENT_WEB_MODULES} /docker-entrypoint.d/17-fetch-element-modules.sh +RUN apk add --no-cache jq -# Drop privileges back to nginx -USER nginx \ No newline at end of file +WORKDIR /app +COPY package.json yarn.lock ./ +COPY ./$BUILD_CONTEXT/package.json ./$BUILD_CONTEXT/ +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} +ARG ELEMENT_VERSION=latest +ARG BUILD_CONTEXT + +COPY --from=builder /modules/* /tmp/element-web-modules/ \ No newline at end of file diff --git a/packages/element-web-module-api/README.md b/packages/element-web-module-api/README.md index d1f3c94fc5..93186ed438 100644 --- a/packages/element-web-module-api/README.md +++ b/packages/element-web-module-api/README.md @@ -4,28 +4,6 @@ API surface for extending Element Web in a safe & predictable way. This project is still in early development but aims to replace matrix-react-sdk-module-api and Element Web deprecated customisations. -## Using the Docker image - -The docker image specified by the Dockerfile in this directory can be used in one of four ways. - -You can specify `ELEMENT_WEB_MODULES` as a build-arg or as a runtime environment variable to fetch & load a module -from a remote URL at runtime or to bundle it into the docker image for easier deployment respectively. -The format this variable should take is a comma-delimited list of URLs followed by the optional tarball hash after a `#` character, e.g. -`ELEMENT_WEB_MODULES=https://example.com/module.tgz#abc123,https://example.com/another-module.tgz#`. - -You can also use it as a base image and add your desired modules into `/tmp/element-web-modules` each in their own directory. -Finally, you can bind mount modules into the `/tmp/element-web-modules` directory at runtime. -The default entrypoint will be index.js in that directory but can be overriden if a package.json file is found with a `main` directive. - -The container expects a config.json file to be bind mounted or copied into the `/app/config.json` path. -The container runs an nginx web server in rootless mode on port 8080. -If you wish to use docker in read-only mode, you should follow the [upstream instructions](https://hub.docker.com/_/nginx#:~:text=Running%20nginx%20in%20read%2Donly%20mode) -but additionally include the following directories: - -- /tmp/element-web-modules/ -- /tmp/element-web-config/ -- /etc/nginx/conf.d/ - ## Using the API Modules are loaded by Element Web at runtime via a dynamic ecmascript import, but can be bundled into a webapp for deployment convenience. @@ -74,6 +52,10 @@ class ExampleModule implements Module { // ... ``` +## Releases + +The API is versioned using semver, with the major version incremented for breaking changes. + ## Copyright & License Copyright (c) 2025 New Vector Ltd diff --git a/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh b/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh deleted file mode 100755 index ff57e90d53..0000000000 --- a/packages/element-web-module-api/docker/docker-entrypoint.d/17-fetch-element-modules.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -# Loads modules specified via env var ELEMENT_WEB_MODULES -# in a comma delimited format of `url#hash,url#hash,...` -# the URL should point to a gzipped tarball, e.g. https://registry.npmjs.org/level/-/level-9.0.0.tgz -# the hash should a sha256sum of the tgz/tar.gz archive, it can be omitted though -# Runs both during the build stage and the runtime entrypoint for added flexibility - -set -e - -entrypoint_log() { - if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "$@" - fi -} - -if [ -z "${ELEMENT_WEB_MODULES}" ]; then - entrypoint_log "ELEMENT_WEB_MODULES is not set, skipping module loading" - exit 0 -fi - -for MODULE in ${ELEMENT_WEB_MODULES//,/ } -do - MODULE_URL=${MODULE%#*} - EXPECTED_HASH=${MODULE#*#} - - entrypoint_log "Fetching module from $MODULE_URL" - wget -O /tmp/element-web-modules/module.tar.gz "$MODULE_URL" - HASH=$(sha256sum /tmp/element-web-modules/module.tar.gz | awk '{ print $1 }') - if [ -n "$EXPECTED_HASH" ] && [ "$HASH" != "$EXPECTED_HASH" ]; then - echo "Hash mismatch for $MODULE_URL: expected $EXPECTED_HASH, got $HASH" - exit 1 - fi - - mkdir -p "/tmp/element-web-modules/$HASH" - tar xvf /tmp/element-web-modules/module.tar.gz -C "/tmp/element-web-modules/$HASH" --strip-components=1 - rm -Rf /tmp/element-web-modules/module.tar.gz -done \ No newline at end of file diff --git a/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh b/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh deleted file mode 100755 index fe08194de0..0000000000 --- a/packages/element-web-module-api/docker/docker-entrypoint.d/18-load-element-modules.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -# Loads modules from `/tmp/element-web-modules` into config.json's `modules` field - -set -e - -entrypoint_log() { - if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "$@" - fi -} - -# If there are modules to be loaded -if [ -d "/tmp/element-web-modules" ]; then - cd /tmp/element-web-modules - - # Copy these config files as a base - cp /app/config*.json /tmp/element-web-config/ - - for MODULE in * - do - # If the module has a package.json, use its main field as the entrypoint - ENTRYPOINT="index.js" - if [ -f "/tmp/element-web-modules/$MODULE/package.json" ]; then - ENTRYPOINT=$(jq -r '.main' "/tmp/element-web-modules/$MODULE/package.json") - fi - - entrypoint_log "Loading module $MODULE with entrypoint $ENTRYPOINT" - - # Append the module to the config - jq ".modules += [\"/modules/$MODULE/$ENTRYPOINT\"]" /tmp/element-web-config/config.json | sponge /tmp/element-web-config/config.json - done -fi \ No newline at end of file diff --git a/packages/element-web-module-api/docker/nginx-templates/default.conf.template b/packages/element-web-module-api/docker/nginx-templates/default.conf.template deleted file mode 100644 index 83813ddcd2..0000000000 --- a/packages/element-web-module-api/docker/nginx-templates/default.conf.template +++ /dev/null @@ -1,24 +0,0 @@ -server { - listen 8080; - server_name localhost; - - root /app; - index index.html; - - location = /index.html { - add_header Cache-Control "no-cache"; - } - location = /version { - add_header Cache-Control "no-cache"; - } - # covers config.json and config.hostname.json requests as it is prefix. - location /config { - root /tmp/element-web-config; - add_header Cache-Control "no-cache"; - } - location /modules { - root /tmp/element-web-modules; - } - error_page 500 502 503 504 /50x.html; -} - From f004a93e152b9b18ad3e3990c4993d9a77ecd5b6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 3 Mar 2025 12:30:57 +0000 Subject: [PATCH 023/156] Iterate --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index a858dc6aed..918ef81283 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -20,4 +20,4 @@ FROM ghcr.io/element-hq/element-web:${ELEMENT_VERSION} ARG ELEMENT_VERSION=latest ARG BUILD_CONTEXT -COPY --from=builder /modules/* /tmp/element-web-modules/ \ No newline at end of file +COPY --from=builder /modules /tmp/element-web-modules/ \ No newline at end of file From ea906ed8a999886f704f2dfa2290e3c617ffb680 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 3 Mar 2025 13:09:10 +0000 Subject: [PATCH 024/156] Iterate --- packages/element-web-module-api/Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 918ef81283..93d2e32ab4 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -17,7 +17,5 @@ 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} -ARG ELEMENT_VERSION=latest -ARG BUILD_CONTEXT COPY --from=builder /modules /tmp/element-web-modules/ \ No newline at end of file From 2f1bc2ab786df0825aa5d4a8478d2d2306f2e454 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:32:18 +0000 Subject: [PATCH 025/156] Fix package.json bin & repository fields --- packages/element-web-module-api/package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 5114e602be..305b09a74c 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,8 +1,12 @@ { "name": "@element-hq/element-web-module-api", - "version": "0.1.1", + "version": "0.1.2", "description": "Module API surface for element-web", - "repository": "https://github.com/element-hq/element-web-modules", + "repository": { + "type": "git", + "url": "git+https://github.com/element-hq/element-web-modules.git", + "directory": "packages/element-web-module-api" + }, "author": "element-hq", "license": "SEE LICENSE IN README.md", "type": "module", From a9b1311cb39594b8bfc778f88caf1409b9172979 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:40:42 +0000 Subject: [PATCH 026/156] Who'd have thunk provenance doesn't behave as the docs describe --- packages/element-web-module-api/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 305b09a74c..4b2718e51a 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,10 +1,10 @@ { "name": "@element-hq/element-web-module-api", - "version": "0.1.2", + "version": "0.1.3", "description": "Module API surface for element-web", "repository": { "type": "git", - "url": "git+https://github.com/element-hq/element-web-modules.git", + "url": "https://github.com/element-hq/element-web-modules", "directory": "packages/element-web-module-api" }, "author": "element-hq", From 8cfa277cd8c7041abb6bab860e02495b3e6dd29a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:53:26 +0000 Subject: [PATCH 027/156] Specify node version --- packages/element-web-module-api/package.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 4b2718e51a..887049c6a7 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,15 +1,18 @@ { "name": "@element-hq/element-web-module-api", - "version": "0.1.3", + "type": "module", + "version": "0.1.4", "description": "Module API surface for element-web", "repository": { "type": "git", - "url": "https://github.com/element-hq/element-web-modules", + "url": "git+https://github.com/element-hq/element-web-modules.git", "directory": "packages/element-web-module-api" }, "author": "element-hq", "license": "SEE LICENSE IN README.md", - "type": "module", + "engines": { + "node": ">=20.0.0" + }, "types": "./lib/element-web-module-api-alpha.d.ts", "exports": { ".": { From 3825bd80cd8b91df9d962f29b41682a9a51d2b76 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:59:17 +0000 Subject: [PATCH 028/156] Fix repository URLs --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 887049c6a7..d1a27dc7c8 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -5,7 +5,7 @@ "description": "Module API surface for element-web", "repository": { "type": "git", - "url": "git+https://github.com/element-hq/element-web-modules.git", + "url": "git+https://github.com/element-hq/element-modules.git", "directory": "packages/element-web-module-api" }, "author": "element-hq", From 2954e393464b523c8123d41357c973a3e9a6588e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:59:43 +0000 Subject: [PATCH 029/156] Bump versions --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index d1a27dc7c8..1c6971eaca 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "0.1.4", + "version": "0.1.5", "description": "Module API surface for element-web", "repository": { "type": "git", From 6837bbab67ac98bc727e008b3bdc84e59d8f74bc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 14:06:02 +0000 Subject: [PATCH 030/156] Update EW modules docker path --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 93d2e32ab4..9e7d64a3e6 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -18,4 +18,4 @@ RUN cp -r ./$BUILD_CONTEXT/lib/ /modules/$(jq -r '"\(.name)-v\(.version)"' ./$BU FROM ghcr.io/element-hq/element-web:${ELEMENT_VERSION} -COPY --from=builder /modules /tmp/element-web-modules/ \ No newline at end of file +COPY --from=builder /modules /modules/ \ No newline at end of file From 46a5a594f5c5878f692ccf6ec900158e9360538b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 16:39:06 +0000 Subject: [PATCH 031/156] Update Dockerfile to consider all package.json files for frozen lockfile --- packages/element-web-module-api/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 93d2e32ab4..ec29b09bc5 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -8,7 +8,10 @@ RUN apk add --no-cache jq WORKDIR /app COPY package.json yarn.lock ./ -COPY ./$BUILD_CONTEXT/package.json ./$BUILD_CONTEXT/ +# 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 From f0686ce9a6446e835294be80be67ba591d71819e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 11 Apr 2025 09:26:32 +0100 Subject: [PATCH 032/156] Update testcontainers, tar-fs and remove duplicate script and stray lockfiles --- packages/element-web-module-api/yarn.lock | 344 ---------------------- 1 file changed, 344 deletions(-) delete mode 100644 packages/element-web-module-api/yarn.lock diff --git a/packages/element-web-module-api/yarn.lock b/packages/element-web-module-api/yarn.lock deleted file mode 100644 index bd4e3d6693..0000000000 --- a/packages/element-web-module-api/yarn.lock +++ /dev/null @@ -1,344 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@esbuild/aix-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" - integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== - -"@esbuild/android-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" - integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== - -"@esbuild/android-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" - integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== - -"@esbuild/android-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" - integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== - -"@esbuild/darwin-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" - integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== - -"@esbuild/darwin-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" - integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== - -"@esbuild/freebsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" - integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== - -"@esbuild/freebsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" - integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== - -"@esbuild/linux-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" - integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== - -"@esbuild/linux-arm@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" - integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== - -"@esbuild/linux-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" - integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== - -"@esbuild/linux-loong64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" - integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== - -"@esbuild/linux-mips64el@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" - integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== - -"@esbuild/linux-ppc64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" - integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== - -"@esbuild/linux-riscv64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" - integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== - -"@esbuild/linux-s390x@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" - integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== - -"@esbuild/linux-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" - integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== - -"@esbuild/netbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" - integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== - -"@esbuild/netbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" - integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== - -"@esbuild/openbsd-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" - integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== - -"@esbuild/openbsd-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" - integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== - -"@esbuild/sunos-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" - integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== - -"@esbuild/win32-arm64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" - integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== - -"@esbuild/win32-ia32@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" - integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== - -"@esbuild/win32-x64@0.24.2": - version "0.24.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" - integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== - -"@rollup/rollup-android-arm-eabi@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz#d4dd60da0075a6ce9a6c76d71b8204f3e1822285" - integrity sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA== - -"@rollup/rollup-android-arm64@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz#25c4d33259a7a2ccd2f52a5ffcc0bb3ab3f0729d" - integrity sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g== - -"@rollup/rollup-darwin-arm64@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz#d137dff254b19163a6b52ac083a71cd055dae844" - integrity sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g== - -"@rollup/rollup-darwin-x64@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz#58ff20b5dacb797d3adca19f02a21c532f9d55bf" - integrity sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ== - -"@rollup/rollup-freebsd-arm64@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz#96ce1a241c591ec3e068f4af765d94eddb24e60c" - integrity sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew== - -"@rollup/rollup-freebsd-x64@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz#e59e7ede505be41f0b4311b0b943f8eb44938467" - integrity sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA== - -"@rollup/rollup-linux-arm-gnueabihf@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz#e455ca6e4ff35bd46d62201c153352e717000a7b" - integrity sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw== - -"@rollup/rollup-linux-arm-musleabihf@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz#bc1a93d807d19e70b1e343a5bfea43723bcd6327" - integrity sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg== - -"@rollup/rollup-linux-arm64-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz#f38bf843f1dc3d5de680caf31084008846e3efae" - integrity sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA== - -"@rollup/rollup-linux-arm64-musl@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz#b3987a96c18b7287129cf735be2dbf83e94d9d05" - integrity sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g== - -"@rollup/rollup-linux-loongarch64-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz#0f0324044e71c4f02e9f49e7ec4e347b655b34ee" - integrity sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz#809479f27f1fd5b4eecd2aa732132ad952d454ba" - integrity sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ== - -"@rollup/rollup-linux-riscv64-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz#7bc75c4f22db04d3c972f83431739cfa41c6a36e" - integrity sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw== - -"@rollup/rollup-linux-s390x-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz#cfe8052345c55864d83ae343362cf1912480170e" - integrity sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ== - -"@rollup/rollup-linux-x64-gnu@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz#c6b048f1e25f3fea5b4bd246232f4d07a159c5a0" - integrity sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g== - -"@rollup/rollup-linux-x64-musl@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz#615273ac52d1a201f4de191cbd3389016a9d7d80" - integrity sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA== - -"@rollup/rollup-win32-arm64-msvc@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz#32ed85810c1b831c648eca999d68f01255b30691" - integrity sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw== - -"@rollup/rollup-win32-ia32-msvc@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz#d47effada68bcbfdccd30c4a788d42e4542ff4d3" - integrity sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ== - -"@rollup/rollup-win32-x64-msvc@4.31.0": - version "4.31.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz#7a2d89a82cf0388d60304964217dd7beac6de645" - integrity sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw== - -"@types/estree@1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" - integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== - -"@types/node@^22.10.7": - version "22.10.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7" - integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== - dependencies: - undici-types "~6.20.0" - -esbuild@^0.24.2: - version "0.24.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" - integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== - optionalDependencies: - "@esbuild/aix-ppc64" "0.24.2" - "@esbuild/android-arm" "0.24.2" - "@esbuild/android-arm64" "0.24.2" - "@esbuild/android-x64" "0.24.2" - "@esbuild/darwin-arm64" "0.24.2" - "@esbuild/darwin-x64" "0.24.2" - "@esbuild/freebsd-arm64" "0.24.2" - "@esbuild/freebsd-x64" "0.24.2" - "@esbuild/linux-arm" "0.24.2" - "@esbuild/linux-arm64" "0.24.2" - "@esbuild/linux-ia32" "0.24.2" - "@esbuild/linux-loong64" "0.24.2" - "@esbuild/linux-mips64el" "0.24.2" - "@esbuild/linux-ppc64" "0.24.2" - "@esbuild/linux-riscv64" "0.24.2" - "@esbuild/linux-s390x" "0.24.2" - "@esbuild/linux-x64" "0.24.2" - "@esbuild/netbsd-arm64" "0.24.2" - "@esbuild/netbsd-x64" "0.24.2" - "@esbuild/openbsd-arm64" "0.24.2" - "@esbuild/openbsd-x64" "0.24.2" - "@esbuild/sunos-x64" "0.24.2" - "@esbuild/win32-arm64" "0.24.2" - "@esbuild/win32-ia32" "0.24.2" - "@esbuild/win32-x64" "0.24.2" - -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -nanoid@^3.3.8: - version "3.3.8" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" - integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -postcss@^8.4.49: - version "8.5.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" - integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - -rollup@^4.23.0: - version "4.31.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.31.0.tgz#b84af969a0292cb047dce2c0ec5413a9457597a4" - integrity sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw== - dependencies: - "@types/estree" "1.0.6" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.31.0" - "@rollup/rollup-android-arm64" "4.31.0" - "@rollup/rollup-darwin-arm64" "4.31.0" - "@rollup/rollup-darwin-x64" "4.31.0" - "@rollup/rollup-freebsd-arm64" "4.31.0" - "@rollup/rollup-freebsd-x64" "4.31.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.31.0" - "@rollup/rollup-linux-arm-musleabihf" "4.31.0" - "@rollup/rollup-linux-arm64-gnu" "4.31.0" - "@rollup/rollup-linux-arm64-musl" "4.31.0" - "@rollup/rollup-linux-loongarch64-gnu" "4.31.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.31.0" - "@rollup/rollup-linux-riscv64-gnu" "4.31.0" - "@rollup/rollup-linux-s390x-gnu" "4.31.0" - "@rollup/rollup-linux-x64-gnu" "4.31.0" - "@rollup/rollup-linux-x64-musl" "4.31.0" - "@rollup/rollup-win32-arm64-msvc" "4.31.0" - "@rollup/rollup-win32-ia32-msvc" "4.31.0" - "@rollup/rollup-win32-x64-msvc" "4.31.0" - fsevents "~2.3.2" - -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -typescript@^5.7.3: - version "5.7.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" - integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== - -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - -vite@^6.0.11: - version "6.0.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.11.tgz#224497e93e940b34c3357c9ebf2ec20803091ed8" - integrity sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg== - dependencies: - esbuild "^0.24.2" - postcss "^8.4.49" - rollup "^4.23.0" - optionalDependencies: - fsevents "~2.3.3" From 737d306f53141fbf8a1156f2d9fc1cad2b4e7058 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 28 Apr 2025 12:43:17 +0100 Subject: [PATCH 033/156] Iterate --- packages/element-web-module-api/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 1c6971eaca..ad1df4a164 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -33,7 +33,7 @@ "@matrix-org/react-sdk-module-api": "^2.5.0", "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", - "@types/react": "^18", + "@types/react": "^19", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", @@ -49,7 +49,7 @@ "@types/react": "*", "@types/react-dom": "*", "matrix-web-i18n": "*", - "react": "^18" + "react": "^19" }, "peerDependenciesMeta": { "@matrix-org/react-sdk-module-api": { From 03e04dec08a5f747bf88e326bb23573b6e58c0d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:06:05 +0100 Subject: [PATCH 034/156] Create module element-web-module-banner --- .../element-web-module-api.api.md | 25 ++++++++- packages/element-web-module-api/package.json | 3 +- .../element-web-module-api/src/api/config.ts | 28 ++++++++++ .../element-web-module-api/src/api/i18n.ts | 52 +++++++++++++++++++ .../src/{api.test.ts => api/index.test.ts} | 3 +- .../src/{api.ts => api/index.ts} | 50 ++++++++++-------- .../src/{ => api}/legacy-customisations.ts | 0 .../src/{ => api}/legacy-modules.ts | 0 packages/element-web-module-api/src/index.ts | 8 +-- 9 files changed, 138 insertions(+), 31 deletions(-) create mode 100644 packages/element-web-module-api/src/api/config.ts create mode 100644 packages/element-web-module-api/src/api/i18n.ts rename packages/element-web-module-api/src/{api.test.ts => api/index.test.ts} (89%) rename packages/element-web-module-api/src/{api.ts => api/index.ts} (68%) rename packages/element-web-module-api/src/{ => api}/legacy-customisations.ts (100%) rename packages/element-web-module-api/src/{ => api}/legacy-modules.ts (100%) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index e47e7428f8..a4b9456cb6 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,6 +5,7 @@ ```ts import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; // @alpha @deprecated (undocumented) @@ -18,8 +19,10 @@ export interface AliasCustomisations { // // @public export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - // (undocumented) - config: ConfigApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + readonly i18n: I18nApi; + readonly rootNode: HTMLElement; } // @alpha @deprecated (undocumented) @@ -60,6 +63,13 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @public +export interface I18nApi { + get language(): string; + register(translations: Partial): void; + translate(key: keyof Translations, variables?: Variables): string; +} + // @alpha @deprecated (undocumented) export type LegacyCustomisations = (customisations: T) => void; @@ -179,6 +189,11 @@ export interface RoomListCustomisations { // @alpha @deprecated (undocumented) export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +// @public +export type Translations = Record; + // @alpha @deprecated (undocumented) export interface UserIdentifierCustomisations { getDisplayUserIdentifier(userId: string, opts: { @@ -187,6 +202,12 @@ export interface UserIdentifierCustomisations { }): string | null; } +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + // @alpha @deprecated (undocumented) export interface WidgetPermissionsCustomisations { preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index ad1df4a164..42b2d50772 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "0.1.5", + "version": "0.2.0", "description": "Module API surface for element-web", "repository": { "type": "git", @@ -34,6 +34,7 @@ "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", "@types/react": "^19", + "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", diff --git a/packages/element-web-module-api/src/api/config.ts b/packages/element-web-module-api/src/api/config.ts new file mode 100644 index 0000000000..5f708b4983 --- /dev/null +++ b/packages/element-web-module-api/src/api/config.ts @@ -0,0 +1,28 @@ +/* +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. +*/ + +/** + * The configuration for the application. + * Should be extended via declaration merging. + * @public + */ +export interface Config { + // The branding name of the application + brand: string; + // Other config options are available but not specified in the types as that would make it difficult to change for element-web + // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md +} + +/** + * API for accessing the configuration. + * @public + */ +export interface ConfigApi { + get(): Config; + get(key: K): Config[K]; + get(key?: K): Config | Config[K]; +} diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts new file mode 100644 index 0000000000..a93c247d4b --- /dev/null +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -0,0 +1,52 @@ +/* +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. +*/ + +/** + * The translations for the module. + * @public + */ +export type Translations = Record< + string, + { + [ietfLanguageTag: string]: string; + } +>; + +/** + * Variables to interpolate into a translation. + * @public + */ +export type Variables = { + /** + * The number of items to count for pluralised translations + */ + count?: number; + [key: string]: number | string | undefined; +}; + +/** + * The API for interacting with translations. + * @public + */ +export interface I18nApi { + /** + * Read the current language of the user in IETF Language Tag format + */ + get language(): string; + + /** + * Register translations for the module, may override app's existing translations + */ + register(translations: Partial): void; + + /** + * Perform a translation, with optional variables + * @param key - The key to translate + * @param variables - Optional variables to interpolate into the translation + */ + translate(key: keyof Translations, variables?: Variables): string; +} diff --git a/packages/element-web-module-api/src/api.test.ts b/packages/element-web-module-api/src/api/index.test.ts similarity index 89% rename from packages/element-web-module-api/src/api.test.ts rename to packages/element-web-module-api/src/api/index.test.ts index 62dd3973aa..25f34768a8 100644 --- a/packages/element-web-module-api/src/api.test.ts +++ b/packages/element-web-module-api/src/api/index.test.ts @@ -7,8 +7,7 @@ Please see LICENSE files in the repository root for full details. import { expect, test } from "vitest"; -import { Api } from "."; -import { isModule } from "./api.js"; +import { Api, isModule } from "."; const TestModule = { default: class TestModule { diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api/index.ts similarity index 68% rename from packages/element-web-module-api/src/api.ts rename to packages/element-web-module-api/src/api/index.ts index 5534411375..f20d9db300 100644 --- a/packages/element-web-module-api/src/api.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -5,8 +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 { Root } from "react-dom/client"; import { LegacyModuleApiExtension } from "./legacy-modules"; import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; +import { ConfigApi } from "./config"; +import { I18nApi } from "./i18n"; /** * Module interface for modules to implement. @@ -61,32 +64,33 @@ export function isModule(module: unknown): module is ModuleExport { ); } -/** - * The configuration for the application. - * Should be extended via declaration merging. - * @public - */ -export interface Config { - // The branding name of the application - brand: string; - // Other config options are available but not specified in the types as that would make it difficult to change for element-web - // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md -} - -/** - * API for accessing the configuration. - * @public - */ -export interface ConfigApi { - get(): Config; - get(key: K): Config[K]; - get(key?: K): Config | Config[K]; -} - /** * The API for modules to interact with the application. * @public */ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - config: ConfigApi; + /** + * The API to read config.json values. + * Keys should be scoped to the module in reverse domain name notation. + * @public + */ + readonly config: ConfigApi; + /** + * The internationalisation API. + * @public + */ + readonly i18n: I18nApi; + /** + * The root node the main application is rendered to. + * Intended for rendering sibling React trees. + * @public + */ + readonly rootNode: HTMLElement; + /** + * Create a ReactDOM root for rendering React components. + * Exposed to allow modules to avoid needing to bundle their own ReactDOM. + * @param element - the element to render use as the root. + * @public + */ + createRoot(element: Element): Root; } diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/api/legacy-customisations.ts similarity index 100% rename from packages/element-web-module-api/src/legacy-customisations.ts rename to packages/element-web-module-api/src/api/legacy-customisations.ts diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/api/legacy-modules.ts similarity index 100% rename from packages/element-web-module-api/src/legacy-modules.ts rename to packages/element-web-module-api/src/api/legacy-modules.ts diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 0b2bf9435c..b503c88188 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -6,6 +6,8 @@ Please see LICENSE files in the repository root for full details. */ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; -export type { Api, Module, ModuleFactory, Config, ConfigApi } from "./api"; -export type * from "./legacy-modules"; -export type * from "./legacy-customisations"; +export type { Api, Module, ModuleFactory } from "./api"; +export type { Config, ConfigApi } from "./api/config"; +export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type * from "./api/legacy-modules"; +export type * from "./api/legacy-customisations"; From 2b1fb75f44009efc8c9f298fdc3af140e6c0775f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:23:13 +0100 Subject: [PATCH 035/156] Standardise lint/type scripts between modules/packages --- packages/element-web-module-api/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index ad1df4a164..c738fe055d 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -26,7 +26,8 @@ ], "scripts": { "prepare": "vite build && api-extractor run", - "lint": "tsc --noEmit", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "vitest --coverage" }, "devDependencies": { From d7736db1afcf28257b41bef604e9a606045e83a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:40:26 +0100 Subject: [PATCH 036/156] Initial stable release of the Module API Primarily to get away from semver treating every update as breaking in the 0.x.y series. --- .../element-web-module-api.api.md | 25 +- packages/element-web-module-api/package.json | 6 +- .../element-web-module-api/src/api/config.ts | 28 ++ .../element-web-module-api/src/api/i18n.ts | 52 ++++ .../src/api/index.test.ts | 22 ++ .../element-web-module-api/src/api/index.ts | 96 +++++++ .../src/api/legacy-customisations.ts | 259 ++++++++++++++++++ .../src/api/legacy-modules.ts | 30 ++ packages/element-web-module-api/src/index.ts | 8 +- 9 files changed, 519 insertions(+), 7 deletions(-) create mode 100644 packages/element-web-module-api/src/api/config.ts create mode 100644 packages/element-web-module-api/src/api/i18n.ts create mode 100644 packages/element-web-module-api/src/api/index.test.ts create mode 100644 packages/element-web-module-api/src/api/index.ts create mode 100644 packages/element-web-module-api/src/api/legacy-customisations.ts create mode 100644 packages/element-web-module-api/src/api/legacy-modules.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index e47e7428f8..a4b9456cb6 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,6 +5,7 @@ ```ts import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; // @alpha @deprecated (undocumented) @@ -18,8 +19,10 @@ export interface AliasCustomisations { // // @public export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - // (undocumented) - config: ConfigApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + readonly i18n: I18nApi; + readonly rootNode: HTMLElement; } // @alpha @deprecated (undocumented) @@ -60,6 +63,13 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @public +export interface I18nApi { + get language(): string; + register(translations: Partial): void; + translate(key: keyof Translations, variables?: Variables): string; +} + // @alpha @deprecated (undocumented) export type LegacyCustomisations = (customisations: T) => void; @@ -179,6 +189,11 @@ export interface RoomListCustomisations { // @alpha @deprecated (undocumented) export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +// @public +export type Translations = Record; + // @alpha @deprecated (undocumented) export interface UserIdentifierCustomisations { getDisplayUserIdentifier(userId: string, opts: { @@ -187,6 +202,12 @@ export interface UserIdentifierCustomisations { }): string | null; } +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + // @alpha @deprecated (undocumented) export interface WidgetPermissionsCustomisations { preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index ad1df4a164..9c0e0cc364 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "0.1.5", + "version": "1.0.0", "description": "Module API surface for element-web", "repository": { "type": "git", @@ -26,7 +26,8 @@ ], "scripts": { "prepare": "vite build && api-extractor run", - "lint": "tsc --noEmit", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "vitest --coverage" }, "devDependencies": { @@ -34,6 +35,7 @@ "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", "@types/react": "^19", + "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", diff --git a/packages/element-web-module-api/src/api/config.ts b/packages/element-web-module-api/src/api/config.ts new file mode 100644 index 0000000000..5f708b4983 --- /dev/null +++ b/packages/element-web-module-api/src/api/config.ts @@ -0,0 +1,28 @@ +/* +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. +*/ + +/** + * The configuration for the application. + * Should be extended via declaration merging. + * @public + */ +export interface Config { + // The branding name of the application + brand: string; + // Other config options are available but not specified in the types as that would make it difficult to change for element-web + // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md +} + +/** + * API for accessing the configuration. + * @public + */ +export interface ConfigApi { + get(): Config; + get(key: K): Config[K]; + get(key?: K): Config | Config[K]; +} diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts new file mode 100644 index 0000000000..a93c247d4b --- /dev/null +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -0,0 +1,52 @@ +/* +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. +*/ + +/** + * The translations for the module. + * @public + */ +export type Translations = Record< + string, + { + [ietfLanguageTag: string]: string; + } +>; + +/** + * Variables to interpolate into a translation. + * @public + */ +export type Variables = { + /** + * The number of items to count for pluralised translations + */ + count?: number; + [key: string]: number | string | undefined; +}; + +/** + * The API for interacting with translations. + * @public + */ +export interface I18nApi { + /** + * Read the current language of the user in IETF Language Tag format + */ + get language(): string; + + /** + * Register translations for the module, may override app's existing translations + */ + register(translations: Partial): void; + + /** + * Perform a translation, with optional variables + * @param key - The key to translate + * @param variables - Optional variables to interpolate into the translation + */ + translate(key: keyof Translations, variables?: Variables): string; +} diff --git a/packages/element-web-module-api/src/api/index.test.ts b/packages/element-web-module-api/src/api/index.test.ts new file mode 100644 index 0000000000..25f34768a8 --- /dev/null +++ b/packages/element-web-module-api/src/api/index.test.ts @@ -0,0 +1,22 @@ +/* +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 { expect, test } from "vitest"; + +import { Api, isModule } from "."; + +const TestModule = { + default: class TestModule { + public static moduleApiVersion = "1.0.0"; + public constructor(private readonly api: Api) {} + public async load(): Promise {} + }, +}; + +test("isModule correctly identifies valid modules", () => { + expect(isModule(TestModule)).toBe(true); +}); diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts new file mode 100644 index 0000000000..f20d9db300 --- /dev/null +++ b/packages/element-web-module-api/src/api/index.ts @@ -0,0 +1,96 @@ +/* +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 { Root } from "react-dom/client"; +import { LegacyModuleApiExtension } from "./legacy-modules"; +import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; +import { ConfigApi } from "./config"; +import { I18nApi } from "./i18n"; + +/** + * Module interface for modules to implement. + * @public + */ +export interface Module { + load(): Promise; +} + +const moduleSignature: Record = { + load: "function", +}; + +/** + * Module interface for modules to export as the default export. + * @public + */ +export interface ModuleFactory { + readonly moduleApiVersion: string; + new (api: Api): Module; + readonly prototype: Module; +} + +const moduleFactorySignature: Record = { + moduleApiVersion: "string", + prototype: "object", +}; + +export interface ModuleExport { + default: ModuleFactory; +} + +const moduleExportSignature: Record = { + default: "function", +}; + +type Type = "function" | "string" | "number" | "boolean" | "object"; + +function isInterface(obj: unknown, type: "object" | "function", keys: Record): obj is T { + if (obj === null || typeof obj !== type) return false; + for (const key in keys) { + if (typeof (obj as Record)[key] !== keys[key]) return false; + } + return true; +} + +export function isModule(module: unknown): module is ModuleExport { + return ( + isInterface(module, "object", moduleExportSignature) && + isInterface(module.default, "function", moduleFactorySignature) && + isInterface(module.default.prototype, "object", moduleSignature) + ); +} + +/** + * The API for modules to interact with the application. + * @public + */ +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { + /** + * The API to read config.json values. + * Keys should be scoped to the module in reverse domain name notation. + * @public + */ + readonly config: ConfigApi; + /** + * The internationalisation API. + * @public + */ + readonly i18n: I18nApi; + /** + * The root node the main application is rendered to. + * Intended for rendering sibling React trees. + * @public + */ + readonly rootNode: HTMLElement; + /** + * Create a ReactDOM root for rendering React components. + * Exposed to allow modules to avoid needing to bundle their own ReactDOM. + * @param element - the element to render use as the root. + * @public + */ + createRoot(element: Element): Root; +} diff --git a/packages/element-web-module-api/src/api/legacy-customisations.ts b/packages/element-web-module-api/src/api/legacy-customisations.ts new file mode 100644 index 0000000000..6374789b5b --- /dev/null +++ b/packages/element-web-module-api/src/api/legacy-customisations.ts @@ -0,0 +1,259 @@ +/* +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. +*/ + +/** + * The types here suck but these customisations are deprecated and will be removed soon. + */ + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface AliasCustomisations { + // E.g. prefer one of the aliases over another + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface ChatExportCustomisations { + /** + * Force parameters in room chat export fields returned here are forced + * and not allowed to be edited in the chat export form + */ + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + // must be < 10**8 + // only used when range is 'LastNMessages' + // default is 100 + numberOfMessages?: number; + includeAttachments?: boolean; + // maximum size of exported archive + // must be > 0 and < 8000 + sizeMb?: number; + }; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface ComponentVisibilityCustomisations { + /** + * 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 component - The component to check visibility for. + * @returns True (default) if the user is able to see the component, false otherwise. + */ + shouldShowComponent?( + component: + | "UIComponent.sendInvites" + | "UIComponent.roomCreation" + | "UIComponent.spaceCreation" + | "UIComponent.exploreRooms" + | "UIComponent.addIntegrations" + | "UIComponent.filterContainer" + | "UIComponent.roomOptionsMenu", + ): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface DirectoryCustomisations { + requireCanonicalAliasAccessToPublish?(): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface LifecycleCustomisations { + onLoggedOutAndStorageCleared?(): void; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface Media { + readonly isEncrypted: boolean; + readonly srcMxc: string; + readonly thumbnailMxc: string | null | undefined; + readonly hasThumbnail: boolean; + readonly srcHttp: string | null; + readonly thumbnailHttp: string | null; + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getSquareThumbnailHttp(dim: number): string | null; + downloadSource(): Promise; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface MediaContructable { + new (prepared: PreparedMedia): Media; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface MediaCustomisations { + readonly Media: MediaContructable; + mediaFromContent(content: Content, client?: Client): Media; + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface RoomListCustomisations { + /** + * Determines if a room is visible in the room list or not. By default, + * all rooms are visible. Where special handling is performed by Element, + * those rooms will not be able to override their visibility in the room + * list - Element will make the decision without calling this function. + * + * This function should be as fast as possible to avoid slowing down the + * client. + * @param room - The room to check the visibility of. + * @returns True if the room should be visible, false otherwise. + */ + isRoomVisible?(room: Room): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface UserIdentifierCustomisations { + /** + * Customise display of the user identifier + * hide userId for guests, display 3pid + * + * Set withDisplayName to true when user identifier will be displayed alongside user name + */ + getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface WidgetPermissionsCustomisations { + /** + * Approves the widget for capabilities that it requested, if any can be + * approved. Typically this will be used to give certain widgets capabilities + * without having to prompt the user to approve them. This cannot reject + * capabilities that Element will be automatically granting, such as the + * ability for Jitsi widgets to stay on screen - those will be approved + * regardless. + * @param widget - The widget to approve capabilities for. + * @param requestedCapabilities - The capabilities the widget requested. + * @returns Resolves to the capabilities that are approved for use + * by the widget. If none are approved, this should return an empty Set. + */ + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface WidgetVariablesCustomisations { + /** + * Provides a partial set of the variables needed to render any widget. If + * variables are missing or not provided then they will be filled with the + * application-determined defaults. + * + * This will not be called until after isReady() resolves. + * @returns The variables. + */ + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; + /** + * Resolves to whether or not the customisation point is ready for variables + * to be provided. This will block widgets being rendered. + * If not provided, the app will assume that the customisation is always ready. + * @returns a promise which resolves when ready. + */ + isReady?(): Promise; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export type LegacyCustomisations = (customisations: T) => void; + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface LegacyCustomisationsApiExtension { + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< + WidgetPermissionsCustomisations + >; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} diff --git a/packages/element-web-module-api/src/api/legacy-modules.ts b/packages/element-web-module-api/src/api/legacy-modules.ts new file mode 100644 index 0000000000..baaad96b01 --- /dev/null +++ b/packages/element-web-module-api/src/api/legacy-modules.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. +*/ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed +import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; + +/** + * @alpha + * @deprecated in favour of the new module API + */ +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +/** + * @alpha + * @deprecated in favour of the new module API + */ +/* eslint-disable @typescript-eslint/naming-convention */ +export interface LegacyModuleApiExtension { + /** + * Register a legacy module based on \@matrix-org/react-sdk-module-api + * @param LegacyModule - the module class to register + * @deprecated provided only as a transition path for legacy modules + */ + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 0b2bf9435c..b503c88188 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -6,6 +6,8 @@ Please see LICENSE files in the repository root for full details. */ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; -export type { Api, Module, ModuleFactory, Config, ConfigApi } from "./api"; -export type * from "./legacy-modules"; -export type * from "./legacy-customisations"; +export type { Api, Module, ModuleFactory } from "./api"; +export type { Config, ConfigApi } from "./api/config"; +export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type * from "./api/legacy-modules"; +export type * from "./api/legacy-customisations"; From 5927495c8883f957d8f1e121a46e1254ef6b2668 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:45:18 +0100 Subject: [PATCH 037/156] Remove stray files --- .../element-web-module-api/src/api.test.ts | 23 ----- packages/element-web-module-api/src/api.ts | 92 ------------------- 2 files changed, 115 deletions(-) delete mode 100644 packages/element-web-module-api/src/api.test.ts delete mode 100644 packages/element-web-module-api/src/api.ts diff --git a/packages/element-web-module-api/src/api.test.ts b/packages/element-web-module-api/src/api.test.ts deleted file mode 100644 index 62dd3973aa..0000000000 --- a/packages/element-web-module-api/src/api.test.ts +++ /dev/null @@ -1,23 +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 { expect, test } from "vitest"; - -import { Api } from "."; -import { isModule } from "./api.js"; - -const TestModule = { - default: class TestModule { - public static moduleApiVersion = "1.0.0"; - public constructor(private readonly api: Api) {} - public async load(): Promise {} - }, -}; - -test("isModule correctly identifies valid modules", () => { - expect(isModule(TestModule)).toBe(true); -}); diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts deleted file mode 100644 index 5534411375..0000000000 --- a/packages/element-web-module-api/src/api.ts +++ /dev/null @@ -1,92 +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 { LegacyModuleApiExtension } from "./legacy-modules"; -import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; - -/** - * Module interface for modules to implement. - * @public - */ -export interface Module { - load(): Promise; -} - -const moduleSignature: Record = { - load: "function", -}; - -/** - * Module interface for modules to export as the default export. - * @public - */ -export interface ModuleFactory { - readonly moduleApiVersion: string; - new (api: Api): Module; - readonly prototype: Module; -} - -const moduleFactorySignature: Record = { - moduleApiVersion: "string", - prototype: "object", -}; - -export interface ModuleExport { - default: ModuleFactory; -} - -const moduleExportSignature: Record = { - default: "function", -}; - -type Type = "function" | "string" | "number" | "boolean" | "object"; - -function isInterface(obj: unknown, type: "object" | "function", keys: Record): obj is T { - if (obj === null || typeof obj !== type) return false; - for (const key in keys) { - if (typeof (obj as Record)[key] !== keys[key]) return false; - } - return true; -} - -export function isModule(module: unknown): module is ModuleExport { - return ( - isInterface(module, "object", moduleExportSignature) && - isInterface(module.default, "function", moduleFactorySignature) && - isInterface(module.default.prototype, "object", moduleSignature) - ); -} - -/** - * The configuration for the application. - * Should be extended via declaration merging. - * @public - */ -export interface Config { - // The branding name of the application - brand: string; - // Other config options are available but not specified in the types as that would make it difficult to change for element-web - // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md -} - -/** - * API for accessing the configuration. - * @public - */ -export interface ConfigApi { - get(): Config; - get(key: K): Config[K]; - get(key?: K): Config | Config[K]; -} - -/** - * The API for modules to interact with the application. - * @public - */ -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - config: ConfigApi; -} From d2c2437188bbb7f53eb682e133ad27ad83d7a4d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:48:17 +0100 Subject: [PATCH 038/156] Remove stray files --- .../src/legacy-customisations.ts | 259 ------------------ .../src/legacy-modules.ts | 30 -- 2 files changed, 289 deletions(-) delete mode 100644 packages/element-web-module-api/src/legacy-customisations.ts delete mode 100644 packages/element-web-module-api/src/legacy-modules.ts diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts deleted file mode 100644 index 6374789b5b..0000000000 --- a/packages/element-web-module-api/src/legacy-customisations.ts +++ /dev/null @@ -1,259 +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. -*/ - -/** - * The types here suck but these customisations are deprecated and will be removed soon. - */ - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface AliasCustomisations { - // E.g. prefer one of the aliases over another - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface ChatExportCustomisations { - /** - * Force parameters in room chat export fields returned here are forced - * and not allowed to be edited in the chat export form - */ - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - // must be < 10**8 - // only used when range is 'LastNMessages' - // default is 100 - numberOfMessages?: number; - includeAttachments?: boolean; - // maximum size of exported archive - // must be > 0 and < 8000 - sizeMb?: number; - }; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface ComponentVisibilityCustomisations { - /** - * 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 component - The component to check visibility for. - * @returns True (default) if the user is able to see the component, false otherwise. - */ - shouldShowComponent?( - component: - | "UIComponent.sendInvites" - | "UIComponent.roomCreation" - | "UIComponent.spaceCreation" - | "UIComponent.exploreRooms" - | "UIComponent.addIntegrations" - | "UIComponent.filterContainer" - | "UIComponent.roomOptionsMenu", - ): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface DirectoryCustomisations { - requireCanonicalAliasAccessToPublish?(): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface LifecycleCustomisations { - onLoggedOutAndStorageCleared?(): void; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface Media { - readonly isEncrypted: boolean; - readonly srcMxc: string; - readonly thumbnailMxc: string | null | undefined; - readonly hasThumbnail: boolean; - readonly srcHttp: string | null; - readonly thumbnailHttp: string | null; - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - getSquareThumbnailHttp(dim: number): string | null; - downloadSource(): Promise; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface MediaContructable { - new (prepared: PreparedMedia): Media; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface MediaCustomisations { - readonly Media: MediaContructable; - mediaFromContent(content: Content, client?: Client): Media; - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface RoomListCustomisations { - /** - * Determines if a room is visible in the room list or not. By default, - * all rooms are visible. Where special handling is performed by Element, - * those rooms will not be able to override their visibility in the room - * list - Element will make the decision without calling this function. - * - * This function should be as fast as possible to avoid slowing down the - * client. - * @param room - The room to check the visibility of. - * @returns True if the room should be visible, false otherwise. - */ - isRoomVisible?(room: Room): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface UserIdentifierCustomisations { - /** - * Customise display of the user identifier - * hide userId for guests, display 3pid - * - * Set withDisplayName to true when user identifier will be displayed alongside user name - */ - getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface WidgetPermissionsCustomisations { - /** - * Approves the widget for capabilities that it requested, if any can be - * approved. Typically this will be used to give certain widgets capabilities - * without having to prompt the user to approve them. This cannot reject - * capabilities that Element will be automatically granting, such as the - * ability for Jitsi widgets to stay on screen - those will be approved - * regardless. - * @param widget - The widget to approve capabilities for. - * @param requestedCapabilities - The capabilities the widget requested. - * @returns Resolves to the capabilities that are approved for use - * by the widget. If none are approved, this should return an empty Set. - */ - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface WidgetVariablesCustomisations { - /** - * Provides a partial set of the variables needed to render any widget. If - * variables are missing or not provided then they will be filled with the - * application-determined defaults. - * - * This will not be called until after isReady() resolves. - * @returns The variables. - */ - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; - /** - * Resolves to whether or not the customisation point is ready for variables - * to be provided. This will block widgets being rendered. - * If not provided, the app will assume that the customisation is always ready. - * @returns a promise which resolves when ready. - */ - isReady?(): Promise; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export type LegacyCustomisations = (customisations: T) => void; - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface LegacyCustomisationsApiExtension { - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< - WidgetPermissionsCustomisations - >; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/legacy-modules.ts deleted file mode 100644 index baaad96b01..0000000000 --- a/packages/element-web-module-api/src/legacy-modules.ts +++ /dev/null @@ -1,30 +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. -*/ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed -import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; - -/** - * @alpha - * @deprecated in favour of the new module API - */ -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -/** - * @alpha - * @deprecated in favour of the new module API - */ -/* eslint-disable @typescript-eslint/naming-convention */ -export interface LegacyModuleApiExtension { - /** - * Register a legacy module based on \@matrix-org/react-sdk-module-api - * @param LegacyModule - the module class to register - * @deprecated provided only as a transition path for legacy modules - */ - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} From c9ea5f8f4ffca79d1a180d80e2ce7cafbc443d76 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 16 May 2025 15:49:07 +0100 Subject: [PATCH 039/156] Custom component concept piece --- .../element-web-module-api.api.md | 491 +++++++++--------- .../src/api/custom-components.ts | 86 +++ .../element-web-module-api/src/api/index.ts | 3 + packages/element-web-module-api/src/index.ts | 2 + 4 files changed, 349 insertions(+), 233 deletions(-) create mode 100644 packages/element-web-module-api/src/api/custom-components.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index a4b9456cb6..1e41a6da51 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -1,233 +1,258 @@ -## API Report File for "@element-hq/element-web-module-api" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { Root } from 'react-dom/client'; -import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; - -// @alpha @deprecated (undocumented) -export interface AliasCustomisations { - // (undocumented) - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha -// -// @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - readonly config: ConfigApi; - createRoot(element: Element): Root; - readonly i18n: I18nApi; - readonly rootNode: HTMLElement; -} - -// @alpha @deprecated (undocumented) -export interface ChatExportCustomisations { - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - numberOfMessages?: number; - includeAttachments?: boolean; - sizeMb?: number; - }; -} - -// @alpha @deprecated (undocumented) -export interface ComponentVisibilityCustomisations { - shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; -} - -// @public -export interface Config { - // (undocumented) - brand: string; -} - -// @public -export interface ConfigApi { - // (undocumented) - get(): Config; - // (undocumented) - get(key: K): Config[K]; - // (undocumented) - get(key?: K): Config | Config[K]; -} - -// @alpha @deprecated (undocumented) -export interface DirectoryCustomisations { - // (undocumented) - requireCanonicalAliasAccessToPublish?(): boolean; -} - -// @public -export interface I18nApi { - get language(): string; - register(translations: Partial): void; - translate(key: keyof Translations, variables?: Variables): string; -} - -// @alpha @deprecated (undocumented) -export type LegacyCustomisations = (customisations: T) => void; - -// @alpha @deprecated (undocumented) -export interface LegacyCustomisationsApiExtension { - // @deprecated (undocumented) - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} - -// @alpha @deprecated (undocumented) -export interface LegacyModuleApiExtension { - // @deprecated - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} - -// @alpha @deprecated (undocumented) -export interface LifecycleCustomisations { - // (undocumented) - onLoggedOutAndStorageCleared?(): void; -} - -// @alpha @deprecated (undocumented) -export interface Media { - // (undocumented) - downloadSource(): Promise; - // (undocumented) - getSquareThumbnailHttp(dim: number): string | null; - // (undocumented) - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - readonly hasThumbnail: boolean; - // (undocumented) - readonly isEncrypted: boolean; - // (undocumented) - readonly srcHttp: string | null; - // (undocumented) - readonly srcMxc: string; - // (undocumented) - readonly thumbnailHttp: string | null; - // (undocumented) - readonly thumbnailMxc: string | null | undefined; -} - -// @alpha @deprecated (undocumented) -export interface MediaContructable { - // (undocumented) - new (prepared: PreparedMedia): Media; -} - -// @alpha @deprecated (undocumented) -export interface MediaCustomisations { - // (undocumented) - readonly Media: MediaContructable; - // (undocumented) - mediaFromContent(content: Content, client?: Client): Media; - // (undocumented) - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -// @public -export interface Module { - // (undocumented) - load(): Promise; -} - -// @public -export interface ModuleFactory { - // (undocumented) - new (api: Api): Module; - // (undocumented) - readonly moduleApiVersion: string; - // (undocumented) - readonly prototype: Module; -} - -// @public -export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string); -} - -// @public -export class ModuleLoader { - constructor(api: Api); - // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts - // - // (undocumented) - load(moduleExport: ModuleExport): Promise; - // (undocumented) - start(): Promise; -} - -// @alpha @deprecated (undocumented) -export interface RoomListCustomisations { - isRoomVisible?(room: Room): boolean; -} - -// @alpha @deprecated (undocumented) -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -// @public -export type Translations = Record; - -// @alpha @deprecated (undocumented) -export interface UserIdentifierCustomisations { - getDisplayUserIdentifier(userId: string, opts: { - roomId?: string; - withDisplayName?: boolean; - }): string | null; -} - -// @public -export type Variables = { - count?: number; - [key: string]: number | string | undefined; -}; - -// @alpha @deprecated (undocumented) -export interface WidgetPermissionsCustomisations { - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -// @alpha @deprecated (undocumented) -export interface WidgetVariablesCustomisations { - isReady?(): Promise; - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; -} - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { JSX } from 'react'; +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { Root } from 'react-dom/client'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { + readonly config: ConfigApi; + createRoot(element: Element): Root; + // (undocumented) + readonly customComponents: CustomComponentsApi; + readonly i18n: I18nApi; + readonly rootNode: HTMLElement; +} + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @public +export type CustomComponentProps = { + [CustomComponentTarget.TextualEvent]: { + mxEvent: any; + stripReply: boolean; + }; +}; + +// @public +export type CustomComponentRenderFunction = (props: CustomComponentProps[T], originalComponent: JSX.Element) => JSX.Element | null; + +// @public +export interface CustomComponentsApi { + register(target: T, renderer: CustomComponentRenderFunction): void; +} + +// @public +export enum CustomComponentTarget { + // (undocumented) + TextualEvent = "TextualEvent" +} + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @public +export interface I18nApi { + get language(): string; + register(translations: Partial): void; + translate(key: keyof Translations, variables?: Variables): string; +} + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @public +export type Translations = Record; + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; +} + +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts new file mode 100644 index 0000000000..582a281ce1 --- /dev/null +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -0,0 +1,86 @@ +import { ComponentType, JSX, MouseEventHandler, PropsWithChildren } from "react"; + +/** + * Target of the renderer function. + * @public + */ +export enum CustomComponentTarget { + /** + * TODO: We should make this more descriptive. + * The renderer for text events in the timeline, such as "m.room.message" + */ + TextualBody = "TextualBody", + MessageContextMenu = "MessageContextMenu", +} + +export interface ContextMenuItem { + label: string; + iconClassName: string; + onClick: ( + e: React.MouseEvent | React.KeyboardEvent | React.FormEvent, + ) => void | Promise; +} + +/** + * Properties for the render component. + * @public + */ +export type CustomComponentProps = { + [CustomComponentTarget.TextualBody]: { + /** + * The Matrix event information. + * TODO: Should this just use the types from matrix-js-sdk? + */ + mxEvent: any; + /** + * Words to highlight on + */ + highlights?: string[]; + /** + * Should previews be shown for this event + */ + showUrlPreview?: boolean; + /** + * Is this event being rendered to a static export + */ + forExport?: boolean; + }; + [CustomComponentTarget.MessageContextMenu]: { + /** + * The Matrix event information. + * TODO: Should this just use the types from matrix-js-sdk? + */ + mxEvent: any; + /** + * Close the menu + */ + closeMenu: () => void; + }; +}; +/** + * Render function. Returning null skips this function and passes it onto the next registered renderer. + * @public + */ +export type CustomComponentRenderFunction = ( + props: CustomComponentProps[T], + originalComponent: JSX.Element, +) => JSX.Element | null; + +/** + * The API for creating custom components. + * @public + */ +export interface CustomComponentsApi { + /** + * Register a renderer for a component type. + * @param target - The target type of component + * @param renderer - The render method. + */ + register(target: T, renderer: CustomComponentRenderFunction): void; + + /** + * Register a context menu section with a given set of items. + * @param items - A set of items to render. + */ + buildContextMenuBlock(items: ContextMenuItem[]): JSX.Element; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index f20d9db300..35dd52d967 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -10,6 +10,7 @@ import { LegacyModuleApiExtension } from "./legacy-modules"; import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; import { ConfigApi } from "./config"; import { I18nApi } from "./i18n"; +import { CustomComponentsApi, CustomComponentTarget } from "./custom-components"; /** * Module interface for modules to implement. @@ -86,6 +87,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx * @public */ readonly rootNode: HTMLElement; + + readonly customComponents: CustomComponentsApi; /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index b503c88188..6640814d1f 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -9,5 +9,7 @@ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; export type { Api, Module, ModuleFactory } from "./api"; export type { Config, ConfigApi } from "./api/config"; export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type * from "./api/custom-components"; +export { CustomComponentTarget } from "./api/custom-components"; export type * from "./api/legacy-modules"; export type * from "./api/legacy-customisations"; From b5a7bd9eacffbafdc574899267bba73d6e7cff47 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 19 May 2025 09:16:27 +0100 Subject: [PATCH 040/156] Document and types' --- .../element-web-module-api.api.md | 29 +++++++-- packages/element-web-module-api/package.json | 4 +- .../src/api/custom-components.ts | 59 ++++++++++++++----- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 1e41a6da51..c86bf9cc60 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -6,6 +6,7 @@ import { JSX } from 'react'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { RoomEvent } from 'matrix-js-sdk'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -60,26 +61,42 @@ export interface ConfigApi { get(key?: K): Config | Config[K]; } +// @public (undocumented) +export interface ContextMenuItem { + iconClassName: string; + label: string; + onClick: (e: React.MouseEvent | React.KeyboardEvent | React.FormEvent) => void | Promise; +} + // @public export type CustomComponentProps = { - [CustomComponentTarget.TextualEvent]: { - mxEvent: any; - stripReply: boolean; + [CustomComponentTarget.TextualBody]: { + mxEvent: RoomEvent; + highlights?: string[]; + showUrlPreview?: boolean; + forExport?: boolean; + }; + [CustomComponentTarget.MessageContextMenu]: { + mxEvent: RoomEvent; + closeMenu: () => void; }; }; // @public -export type CustomComponentRenderFunction = (props: CustomComponentProps[T], originalComponent: JSX.Element) => JSX.Element | null; +export type CustomComponentRenderFunction = ( +props: CustomComponentProps[T], +originalComponent: JSX.Element) => JSX.Element | null; // @public export interface CustomComponentsApi { + buildContextMenuBlock(items: ContextMenuItem[]): JSX.Element; register(target: T, renderer: CustomComponentRenderFunction): void; } // @public export enum CustomComponentTarget { - // (undocumented) - TextualEvent = "TextualEvent" + MessageContextMenu = "MessageContextMenu", + TextualBody = "TextualBody" } // @alpha @deprecated (undocumented) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 9c0e0cc364..cd329b8788 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -44,13 +44,15 @@ "vite": "^6.0.11", "vite-plugin-dts": "^4.5.0", "vitest": "^3.0.5", - "vitest-sonar-reporter": "^2.0.0" + "vitest-sonar-reporter": "^2.0.0", + "matrix-js-sdk": "^37.5.0" }, "peerDependencies": { "@matrix-org/react-sdk-module-api": "*", "@types/react": "*", "@types/react-dom": "*", "matrix-web-i18n": "*", + "matrix-js-sdk": "*", "react": "^19" }, "peerDependenciesMeta": { diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 582a281ce1..f3cd115582 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -1,21 +1,38 @@ -import { ComponentType, JSX, MouseEventHandler, PropsWithChildren } from "react"; +import type { JSX } from "react"; +import type { RoomEvent } from "matrix-js-sdk"; /** - * Target of the renderer function. + * Targets in Element for custom components. * @public */ export enum CustomComponentTarget { /** - * TODO: We should make this more descriptive. - * The renderer for text events in the timeline, such as "m.room.message" + * Component that renders "m.room.message" events in the room timeline. */ TextualBody = "TextualBody", + /** + * "Options" Context menu for a timeline event. + * Use `buildContextMenuBlock` to build a section to be used by this component. + * @see buildContextMenuBlock + */ MessageContextMenu = "MessageContextMenu", } export interface ContextMenuItem { + /** + * The human readable label for an event. + * TODO: Should this be i18n-d + */ label: string; + /** + * The icon to use for this item. + * https://github.com/vector-im/riot-web/blob/efc6149a8b3362c01b93f52e76e5c4ae8cbcb65c/res/css/views/context_menus/_MessageContextMenu.pcss#L10 + */ iconClassName: string; + /** + * Handler for click events on the context menu item. + * Does NOT close the menu after execution. + */ onClick: ( e: React.MouseEvent | React.KeyboardEvent | React.FormEvent, ) => void | Promise; @@ -28,12 +45,12 @@ export interface ContextMenuItem { export type CustomComponentProps = { [CustomComponentTarget.TextualBody]: { /** - * The Matrix event information. - * TODO: Should this just use the types from matrix-js-sdk? + * The Matrix event for this textual body. */ - mxEvent: any; + mxEvent: RoomEvent; /** - * Words to highlight on + * Words to highlight on (e.g. from search results).\ + * May be undefined if the client does not need to highlight */ highlights?: string[]; /** @@ -47,12 +64,11 @@ export type CustomComponentProps = { }; [CustomComponentTarget.MessageContextMenu]: { /** - * The Matrix event information. - * TODO: Should this just use the types from matrix-js-sdk? + * The Matrix event which this context menu targets. */ - mxEvent: any; + mxEvent: RoomEvent; /** - * Close the menu + * Function that will close the menu. */ closeMenu: () => void; }; @@ -62,24 +78,37 @@ export type CustomComponentProps = { * @public */ export type CustomComponentRenderFunction = ( + /** + * Properties from the given target to be used for rendering. + */ props: CustomComponentProps[T], + /** + * The original component. + */ originalComponent: JSX.Element, ) => JSX.Element | null; /** - * The API for creating custom components. + * API for inserting custom components into Element. * @public */ export interface CustomComponentsApi { /** * Register a renderer for a component type. - * @param target - The target type of component + * The render function should either return a rendered component, or `null` if the + * component should not be overidden. + * + * Multiple render function may be registered for a single target, however the first + * non-null result will be used. If all results are null, or no registrations exist + * for a target then the original component is used. + * + * @param target - The target location for the component. * @param renderer - The render method. */ register(target: T, renderer: CustomComponentRenderFunction): void; /** - * Register a context menu section with a given set of items. + * Generate a context menu section for a given set of items. * @param items - A set of items to render. */ buildContextMenuBlock(items: ContextMenuItem[]): JSX.Element; From 50fc7bec66d94b96a0f7b74dc81d068827ee5f12 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 19 May 2025 11:56:53 +0100 Subject: [PATCH 041/156] lint --- packages/element-web-module-api/src/api/custom-components.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index f3cd115582..2d1f269409 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -18,6 +18,9 @@ export enum CustomComponentTarget { MessageContextMenu = "MessageContextMenu", } +/** + * @public + */ export interface ContextMenuItem { /** * The human readable label for an event. @@ -49,7 +52,7 @@ export type CustomComponentProps = { */ mxEvent: RoomEvent; /** - * Words to highlight on (e.g. from search results).\ + * Words to highlight on (e.g. from search results). * May be undefined if the client does not need to highlight */ highlights?: string[]; From e972c05ccc3667ef3292f0ebc6f3f9389b3577da Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 20 May 2025 12:11:07 +0100 Subject: [PATCH 042/156] Remove context menu --- .../element-web-module-api.api.md | 19 ++----- .../src/api/custom-components.ts | 53 ++----------------- 2 files changed, 7 insertions(+), 65 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index c86bf9cc60..f9731b1371 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,8 +5,8 @@ ```ts import { JSX } from 'react'; +import { MatrixEvent } from 'matrix-js-sdk'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { RoomEvent } from 'matrix-js-sdk'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -61,41 +61,28 @@ export interface ConfigApi { get(key?: K): Config | Config[K]; } -// @public (undocumented) -export interface ContextMenuItem { - iconClassName: string; - label: string; - onClick: (e: React.MouseEvent | React.KeyboardEvent | React.FormEvent) => void | Promise; -} - // @public export type CustomComponentProps = { [CustomComponentTarget.TextualBody]: { - mxEvent: RoomEvent; + mxEvent: MatrixEvent; highlights?: string[]; showUrlPreview?: boolean; forExport?: boolean; }; - [CustomComponentTarget.MessageContextMenu]: { - mxEvent: RoomEvent; - closeMenu: () => void; - }; }; // @public export type CustomComponentRenderFunction = ( props: CustomComponentProps[T], -originalComponent: JSX.Element) => JSX.Element | null; +originalComponent: () => JSX.Element) => JSX.Element | null; // @public export interface CustomComponentsApi { - buildContextMenuBlock(items: ContextMenuItem[]): JSX.Element; register(target: T, renderer: CustomComponentRenderFunction): void; } // @public export enum CustomComponentTarget { - MessageContextMenu = "MessageContextMenu", TextualBody = "TextualBody" } diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 2d1f269409..244757c6e1 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -1,5 +1,5 @@ import type { JSX } from "react"; -import type { RoomEvent } from "matrix-js-sdk"; +import type { MatrixEvent } from "matrix-js-sdk"; /** * Targets in Element for custom components. @@ -10,35 +10,6 @@ export enum CustomComponentTarget { * Component that renders "m.room.message" events in the room timeline. */ TextualBody = "TextualBody", - /** - * "Options" Context menu for a timeline event. - * Use `buildContextMenuBlock` to build a section to be used by this component. - * @see buildContextMenuBlock - */ - MessageContextMenu = "MessageContextMenu", -} - -/** - * @public - */ -export interface ContextMenuItem { - /** - * The human readable label for an event. - * TODO: Should this be i18n-d - */ - label: string; - /** - * The icon to use for this item. - * https://github.com/vector-im/riot-web/blob/efc6149a8b3362c01b93f52e76e5c4ae8cbcb65c/res/css/views/context_menus/_MessageContextMenu.pcss#L10 - */ - iconClassName: string; - /** - * Handler for click events on the context menu item. - * Does NOT close the menu after execution. - */ - onClick: ( - e: React.MouseEvent | React.KeyboardEvent | React.FormEvent, - ) => void | Promise; } /** @@ -50,7 +21,7 @@ export type CustomComponentProps = { /** * The Matrix event for this textual body. */ - mxEvent: RoomEvent; + mxEvent: MatrixEvent; /** * Words to highlight on (e.g. from search results). * May be undefined if the client does not need to highlight @@ -65,16 +36,6 @@ export type CustomComponentProps = { */ forExport?: boolean; }; - [CustomComponentTarget.MessageContextMenu]: { - /** - * The Matrix event which this context menu targets. - */ - mxEvent: RoomEvent; - /** - * Function that will close the menu. - */ - closeMenu: () => void; - }; }; /** * Render function. Returning null skips this function and passes it onto the next registered renderer. @@ -86,9 +47,9 @@ export type CustomComponentRenderFunction = ( */ props: CustomComponentProps[T], /** - * The original component. + * Render function for the original component. */ - originalComponent: JSX.Element, + originalComponent: () => JSX.Element, ) => JSX.Element | null; /** @@ -109,10 +70,4 @@ export interface CustomComponentsApi { * @param renderer - The render method. */ register(target: T, renderer: CustomComponentRenderFunction): void; - - /** - * Generate a context menu section for a given set of items. - * @param items - A set of items to render. - */ - buildContextMenuBlock(items: ContextMenuItem[]): JSX.Element; } From 908771235f48139581d887f903f8d829926876fb Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 20 May 2025 12:14:20 +0100 Subject: [PATCH 043/156] Tidyup --- .../element-web-module-api/src/api/custom-components.ts | 7 +++++++ packages/element-web-module-api/src/api/index.ts | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 244757c6e1..1c1c68b91b 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -1,3 +1,10 @@ +/* +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 type { MatrixEvent } from "matrix-js-sdk"; diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 35dd52d967..feb97cbe51 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -10,7 +10,7 @@ import { LegacyModuleApiExtension } from "./legacy-modules"; import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; import { ConfigApi } from "./config"; import { I18nApi } from "./i18n"; -import { CustomComponentsApi, CustomComponentTarget } from "./custom-components"; +import { CustomComponentsApi } from "./custom-components"; /** * Module interface for modules to implement. @@ -88,6 +88,10 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx */ readonly rootNode: HTMLElement; + /** + * The custom components API. + * @public + */ readonly customComponents: CustomComponentsApi; /** * Create a ReactDOM root for rendering React components. From ba5ce17574a55f0432075472f33e80a8ed9db582 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 20 May 2025 13:00:47 +0100 Subject: [PATCH 044/156] docs fix --- packages/element-web-module-api/element-web-module-api.api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index f9731b1371..18cfa96c1b 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -23,7 +23,6 @@ export interface AliasCustomisations { export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { readonly config: ConfigApi; createRoot(element: Element): Root; - // (undocumented) readonly customComponents: CustomComponentsApi; readonly i18n: I18nApi; readonly rootNode: HTMLElement; From b480633c151f6e1c14bf7d04cb4bde364366cbc0 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 045/156] 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] --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 9c0e0cc364..f23e2e679b 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -41,7 +41,7 @@ "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", "typescript": "^5.7.3", - "vite": "^6.0.11", + "vite": "^6.1.6", "vite-plugin-dts": "^4.5.0", "vitest": "^3.0.5", "vitest-sonar-reporter": "^2.0.0" From 930a5eb8b70499d5c91d8c757a64c31de564bc09 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 30 May 2025 14:51:28 +0100 Subject: [PATCH 046/156] Update module API --- .../element-web-module-api.api.md | 38 +++++----- .../src/api/custom-components.ts | 76 ++++++++----------- .../element-web-module-api/src/api/index.ts | 2 +- packages/element-web-module-api/src/index.ts | 1 - 4 files changed, 51 insertions(+), 66 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 18cfa96c1b..2914fc8603 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -61,29 +61,25 @@ export interface ConfigApi { } // @public -export type CustomComponentProps = { - [CustomComponentTarget.TextualBody]: { - mxEvent: MatrixEvent; - highlights?: string[]; - showUrlPreview?: boolean; - forExport?: boolean; - }; +export interface CustomComponentsApi { + // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @beta + registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void; +} + +// @alpha +export type CustomMessageComponentProps = { + mxEvent: MatrixEvent; + highlights?: string[]; + showUrlPreview?: boolean; + forExport?: boolean; }; -// @public -export type CustomComponentRenderFunction = ( -props: CustomComponentProps[T], -originalComponent: () => JSX.Element) => JSX.Element | null; - -// @public -export interface CustomComponentsApi { - register(target: T, renderer: CustomComponentRenderFunction): void; -} - -// @public -export enum CustomComponentTarget { - TextualBody = "TextualBody" -} +// Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "CustomMessageComponentProps" which is marked as @alpha +// +// @beta +export type CustomMessageRenderFunction = ( +props: CustomMessageComponentProps, +originalComponent?: () => React.JSX.Element) => JSX.Element | null; // @alpha @deprecated (undocumented) export interface DirectoryCustomisations { diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 1c1c68b91b..3cae10b61c 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -9,54 +9,42 @@ import type { JSX } from "react"; import type { MatrixEvent } from "matrix-js-sdk"; /** - * Targets in Element for custom components. - * @public + * Properties for all message components. + * @alpha Subject to change. */ -export enum CustomComponentTarget { +export type CustomMessageComponentProps = { /** - * Component that renders "m.room.message" events in the room timeline. + * The Matrix event for this textual body. */ - TextualBody = "TextualBody", -} + mxEvent: MatrixEvent; + /** + * Words to highlight on (e.g. from search results). + * May be undefined if the client does not need to highlight + */ + highlights?: string[]; + /** + * Should previews be shown for this event + */ + showUrlPreview?: boolean; + /** + * Is this event being rendered to a static export + */ + forExport?: boolean; +}; /** - * Properties for the render component. - * @public + * Function used to render a message component. + * @beta Unlikely to change */ -export type CustomComponentProps = { - [CustomComponentTarget.TextualBody]: { - /** - * The Matrix event for this textual body. - */ - mxEvent: MatrixEvent; - /** - * Words to highlight on (e.g. from search results). - * May be undefined if the client does not need to highlight - */ - highlights?: string[]; - /** - * Should previews be shown for this event - */ - showUrlPreview?: boolean; - /** - * Is this event being rendered to a static export - */ - forExport?: boolean; - }; -}; -/** - * Render function. Returning null skips this function and passes it onto the next registered renderer. - * @public - */ -export type CustomComponentRenderFunction = ( +export type CustomMessageRenderFunction = ( /** - * Properties from the given target to be used for rendering. + * Properties for the message to be renderered. */ - props: CustomComponentProps[T], + props: CustomMessageComponentProps, /** - * Render function for the original component. + * Render function for the original component. This may be omitted if the message would not normally be rendered. */ - originalComponent: () => JSX.Element, + originalComponent?: () => React.JSX.Element, ) => JSX.Element | null; /** @@ -65,16 +53,18 @@ export type CustomComponentRenderFunction = ( */ export interface CustomComponentsApi { /** - * Register a renderer for a component type. + * Register a renderer for a message type in the timeline. + * * The render function should either return a rendered component, or `null` if the - * component should not be overidden. + * component should not be overidden (for instance, to passthrough to another module or allow + * the application complete control) * * Multiple render function may be registered for a single target, however the first * non-null result will be used. If all results are null, or no registrations exist * for a target then the original component is used. * - * @param target - The target location for the component. - * @param renderer - The render method. + * @param eventType - The event type this renderer is for. Use a RegExp instance if you want to target multiple types. + * @param renderer - The render function. */ - register(target: T, renderer: CustomComponentRenderFunction): void; + registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void; } diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index feb97cbe51..3e9d4d072f 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -89,7 +89,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx readonly rootNode: HTMLElement; /** - * The custom components API. + * The custom message component API. * @public */ readonly customComponents: CustomComponentsApi; diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 6640814d1f..4fbf15e9ba 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -10,6 +10,5 @@ export type { Api, Module, ModuleFactory } from "./api"; export type { Config, ConfigApi } from "./api/config"; export type { I18nApi, Variables, Translations } from "./api/i18n"; export type * from "./api/custom-components"; -export { CustomComponentTarget } from "./api/custom-components"; export type * from "./api/legacy-modules"; export type * from "./api/legacy-customisations"; From 546861811c048a794898ca16c06b831e66605c8a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 3 Jun 2025 13:41:57 +0100 Subject: [PATCH 047/156] fixup --- packages/element-web-module-api/element-web-module-api.api.md | 2 +- packages/element-web-module-api/src/api/custom-components.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 2914fc8603..f7160e5289 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,7 +5,7 @@ ```ts import { JSX } from 'react'; -import { MatrixEvent } from 'matrix-js-sdk'; +import { MatrixEvent } from 'matrix-js-sdk/lib/matrix'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 3cae10b61c..4f008acafe 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { JSX } from "react"; -import type { MatrixEvent } from "matrix-js-sdk"; +import type { MatrixEvent } from "matrix-js-sdk/lib/matrix"; /** * Properties for all message components. From e2ae6f2288560d2f60859b1b6d9f420760472197 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 5 Jun 2025 09:58:51 +0100 Subject: [PATCH 048/156] Specify options for the original component. --- .../element-web-module-api.api.md | 9 +++++++-- packages/element-web-module-api/package.json | 6 +++--- .../src/api/custom-components.ts | 17 ++++++++++++----- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index f7160e5289..90cd4df08f 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -70,16 +70,16 @@ export interface CustomComponentsApi { export type CustomMessageComponentProps = { mxEvent: MatrixEvent; highlights?: string[]; - showUrlPreview?: boolean; forExport?: boolean; }; // Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "CustomMessageComponentProps" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "OriginalComponentProps" which is marked as @alpha // // @beta export type CustomMessageRenderFunction = ( props: CustomMessageComponentProps, -originalComponent?: () => React.JSX.Element) => JSX.Element | null; +originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JSX.Element | null; // @alpha @deprecated (undocumented) export interface DirectoryCustomisations { @@ -205,6 +205,11 @@ export class ModuleLoader { start(): Promise; } +// @alpha +export type OriginalComponentProps = { + showUrlPreview?: boolean; +}; + // @alpha @deprecated (undocumented) export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index f2fed95dfb..94d1d4913d 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -38,21 +38,21 @@ "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", + "matrix-js-sdk": "^37.5.0", "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", "typescript": "^5.7.3", "vite": "^6.1.6", "vite-plugin-dts": "^4.5.0", "vitest": "^3.0.5", - "vitest-sonar-reporter": "^2.0.0", - "matrix-js-sdk": "^37.5.0" + "vitest-sonar-reporter": "^2.0.0" }, "peerDependencies": { "@matrix-org/react-sdk-module-api": "*", "@types/react": "*", "@types/react-dom": "*", - "matrix-web-i18n": "*", "matrix-js-sdk": "*", + "matrix-web-i18n": "*", "react": "^19" }, "peerDependenciesMeta": { diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 4f008acafe..5ce0f8fefd 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -22,16 +22,23 @@ export type CustomMessageComponentProps = { * May be undefined if the client does not need to highlight */ highlights?: string[]; - /** - * Should previews be shown for this event - */ - showUrlPreview?: boolean; /** * Is this event being rendered to a static export */ forExport?: boolean; }; +/** + * Properties to alter the render function of the original component. + * @alpha Subject to change. + */ +export type OriginalComponentProps = { + /** + * Should previews be shown for this event. + */ + showUrlPreview?: boolean; +}; + /** * Function used to render a message component. * @beta Unlikely to change @@ -44,7 +51,7 @@ export type CustomMessageRenderFunction = ( /** * Render function for the original component. This may be omitted if the message would not normally be rendered. */ - originalComponent?: () => React.JSX.Element, + originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element, ) => JSX.Element | null; /** From c6a612382630dbb38d037e0f3c41a5baaafba03a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 5 Jun 2025 10:01:40 +0100 Subject: [PATCH 049/156] Remove unused props --- .../src/api/custom-components.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 5ce0f8fefd..f9f4128c91 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -17,15 +17,6 @@ export type CustomMessageComponentProps = { * The Matrix event for this textual body. */ mxEvent: MatrixEvent; - /** - * Words to highlight on (e.g. from search results). - * May be undefined if the client does not need to highlight - */ - highlights?: string[]; - /** - * Is this event being rendered to a static export - */ - forExport?: boolean; }; /** @@ -35,6 +26,7 @@ export type CustomMessageComponentProps = { export type OriginalComponentProps = { /** * Should previews be shown for this event. + * This may be overriden by user preferences. */ showUrlPreview?: boolean; }; From 60687b27a6f6967e6ae5c92ad77448c5a50e4bfb Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 5 Jun 2025 16:10:35 +0100 Subject: [PATCH 050/156] Add an example --- .../element-web-module-api/src/api/custom-components.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index f9f4128c91..01d589341e 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -64,6 +64,15 @@ export interface CustomComponentsApi { * * @param eventType - The event type this renderer is for. Use a RegExp instance if you want to target multiple types. * @param renderer - The render function. + * @example + * ``` + * customComponents.registerMessageRenderer("m.room.message", (props, originalComponent) => { + * return ; + * }); + * customComponents.registerMessageRenderer(/m\.room\.(topic|name)/, (props, originalComponent) => { + * return ; + * }); + * ``` */ registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void; } From 8cf426f0e77dbc857a695715c19791b333a1e063 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 5 Jun 2025 16:57:48 +0100 Subject: [PATCH 051/156] tighten example --- .../element-web-module-api/element-web-module-api.api.md | 2 -- .../element-web-module-api/src/api/custom-components.ts | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 90cd4df08f..49ce6f22cf 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -69,8 +69,6 @@ export interface CustomComponentsApi { // @alpha export type CustomMessageComponentProps = { mxEvent: MatrixEvent; - highlights?: string[]; - forExport?: boolean; }; // Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "CustomMessageComponentProps" which is marked as @alpha diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 01d589341e..87266de5ad 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -70,7 +70,11 @@ export interface CustomComponentsApi { * return ; * }); * customComponents.registerMessageRenderer(/m\.room\.(topic|name)/, (props, originalComponent) => { - * return ; + * if (props.mxEvent.isState()) { + * return ; + * } + * // Passthrough. + * return null; * }); * ``` */ From 240045f4337e6b6a7ead4b6687612b4dd144afd2 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Jun 2025 15:41:53 +0100 Subject: [PATCH 052/156] Add filter function / hints --- .../element-web-module-api.api.md | 17 ++++--- .../src/api/custom-components.ts | 50 ++++++++++++------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 49ce6f22cf..93b565b681 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -62,8 +62,9 @@ export interface ConfigApi { // @public export interface CustomComponentsApi { - // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @beta - registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void; + // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @alpha + // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderHints" which is marked as @alpha + registerMessageRenderer(eventType: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; } // @alpha @@ -71,13 +72,15 @@ export type CustomMessageComponentProps = { mxEvent: MatrixEvent; }; -// Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "CustomMessageComponentProps" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "CustomMessageRenderFunction" is marked as @beta, but its signature references "OriginalComponentProps" which is marked as @alpha -// -// @beta +// @alpha export type CustomMessageRenderFunction = ( props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JSX.Element | null; +originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JSX.Element; + +// @alpha +export type CustomMessageRenderHints = { + allowEditingEvent?: boolean; +}; // @alpha @deprecated (undocumented) export interface DirectoryCustomisations { diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 87266de5ad..794786538a 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -15,6 +15,7 @@ import type { MatrixEvent } from "matrix-js-sdk/lib/matrix"; export type CustomMessageComponentProps = { /** * The Matrix event for this textual body. + * @alpha */ mxEvent: MatrixEvent; }; @@ -31,9 +32,23 @@ export type OriginalComponentProps = { showUrlPreview?: boolean; }; +/** + * Hints to specify to Element when rendering events. + * @alpha Subject to change. + */ +export type CustomMessageRenderHints = { + /** + * Should the event be allowed to be edited in the client. This should + * be set to false if you override the render function, as the module + * API has no way to display message editing at the moment. + * Default is true. + */ + allowEditingEvent?: boolean; +}; + /** * Function used to render a message component. - * @beta Unlikely to change + * @alpha Unlikely to change */ export type CustomMessageRenderFunction = ( /** @@ -44,7 +59,7 @@ export type CustomMessageRenderFunction = ( * Render function for the original component. This may be omitted if the message would not normally be rendered. */ originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element, -) => JSX.Element | null; +) => JSX.Element; /** * API for inserting custom components into Element. @@ -54,29 +69,30 @@ export interface CustomComponentsApi { /** * Register a renderer for a message type in the timeline. * - * The render function should either return a rendered component, or `null` if the - * component should not be overidden (for instance, to passthrough to another module or allow - * the application complete control) + * The render function should return a rendered component. * - * Multiple render function may be registered for a single target, however the first - * non-null result will be used. If all results are null, or no registrations exist - * for a target then the original component is used. + * Multiple render function may be registered for a single event type, however the first matching + * result will be used. If no events match or are registered then the originalComponent is rendered. * - * @param eventType - The event type this renderer is for. Use a RegExp instance if you want to target multiple types. + * @param eventTypeOrFilter - The event type this renderer is for. Use a function for more complex filtering. * @param renderer - The render function. + * @param hints - Hints that alter the way the tile is handled. * @example * ``` * customComponents.registerMessageRenderer("m.room.message", (props, originalComponent) => { * return ; * }); - * customComponents.registerMessageRenderer(/m\.room\.(topic|name)/, (props, originalComponent) => { - * if (props.mxEvent.isState()) { - * return ; - * } - * // Passthrough. - * return null; - * }); + * customComponents.registerMessageRenderer( + * (mxEvent) => mxEvent.getType().matches(/m\.room\.(topic|name)/) && mxEvent.isState(), + * (props, originalComponent) => { + * return ; + * } + * ); * ``` */ - registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void; + registerMessageRenderer( + eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), + renderer: CustomMessageRenderFunction, + hints?: CustomMessageRenderHints, + ): void; } From cea6abfca5252b2e700e30296c71383e9c1b47c9 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 12 Jun 2025 10:58:45 +0100 Subject: [PATCH 053/156] Update API docs --- packages/element-web-module-api/element-web-module-api.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 93b565b681..754a4d2eec 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -64,7 +64,7 @@ export interface ConfigApi { export interface CustomComponentsApi { // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @alpha // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderHints" which is marked as @alpha - registerMessageRenderer(eventType: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; } // @alpha From 4e8c99dfd1ef39e62ede6911651b909c17efda72 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 16 Jun 2025 12:05:28 +0100 Subject: [PATCH 054/156] 1.1.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 94d1d4913d..ed0b67418a 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.0.0", + "version": "1.1.0", "description": "Module API surface for element-web", "repository": { "type": "git", From ead847ec6a50d2f46207288b54fd85ad5858eaf3 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 13:16:01 +0100 Subject: [PATCH 055/156] Use MatrixEvent provided by us. --- .../src/api/custom-components.ts | 2 +- packages/element-web-module-api/src/index.ts | 1 + .../src/models/event.ts | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 packages/element-web-module-api/src/models/event.ts diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 794786538a..0c8a26712d 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { JSX } from "react"; -import type { MatrixEvent } from "matrix-js-sdk/lib/matrix"; +import type { MatrixEvent } from "../models/event"; /** * Properties for all message components. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 4fbf15e9ba..0a7337ec21 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -9,6 +9,7 @@ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; export type { Api, Module, ModuleFactory } from "./api"; export type { Config, ConfigApi } from "./api/config"; export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type * from "./models/event"; export type * from "./api/custom-components"; export type * from "./api/legacy-modules"; export type * from "./api/legacy-customisations"; diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/element-web-module-api/src/models/event.ts new file mode 100644 index 0000000000..d8c1cfad86 --- /dev/null +++ b/packages/element-web-module-api/src/models/event.ts @@ -0,0 +1,24 @@ +import type { EventStatus, IEventRelation, Membership } from "matrix-js-sdk"; + +/** + * Representation of a Matrix event. + * @beta + */ +export interface MatrixEvent { + // Properties provided by the spec + eventId: string; + roomId?: string; + sender: string; + content: Record; + unsigned: Record; + type: string; + stateKey?: string; + originServerTs: number; + redacts?: string; + + // Properties provided by the SDK + membership?: Membership; + status: EventStatus; + relation?: IEventRelation | null; + age?: number; +} From 89102aa9038d472b940bd2ec04b9f7ec7471dd7b Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 13:27:50 +0100 Subject: [PATCH 056/156] fixup types --- packages/element-web-module-api/src/models/event.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/element-web-module-api/src/models/event.ts index d8c1cfad86..28b9267529 100644 --- a/packages/element-web-module-api/src/models/event.ts +++ b/packages/element-web-module-api/src/models/event.ts @@ -5,7 +5,6 @@ import type { EventStatus, IEventRelation, Membership } from "matrix-js-sdk"; * @beta */ export interface MatrixEvent { - // Properties provided by the spec eventId: string; roomId?: string; sender: string; @@ -15,8 +14,6 @@ export interface MatrixEvent { stateKey?: string; originServerTs: number; redacts?: string; - - // Properties provided by the SDK membership?: Membership; status: EventStatus; relation?: IEventRelation | null; From 56d0c5656a08a077ce34dec0fdf11867f5360775 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 13:30:47 +0100 Subject: [PATCH 057/156] API update\ --- .../element-web-module-api.api.md | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 754a4d2eec..3a8474d949 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -4,8 +4,10 @@ ```ts +import { EventStatus } from 'matrix-js-sdk'; +import { IEventRelation } from 'matrix-js-sdk'; import { JSX } from 'react'; -import { MatrixEvent } from 'matrix-js-sdk/lib/matrix'; +import { Membership } from 'matrix-js-sdk'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -62,6 +64,7 @@ export interface ConfigApi { // @public export interface CustomComponentsApi { + // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "MatrixEvent" which is marked as @beta // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @alpha // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderHints" which is marked as @alpha registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; @@ -134,6 +137,36 @@ export interface LifecycleCustomisations { onLoggedOutAndStorageCleared?(): void; } +// @beta +export interface MatrixEvent { + // (undocumented) + age?: number; + // (undocumented) + content: Record; + // (undocumented) + eventId: string; + // (undocumented) + membership?: Membership; + // (undocumented) + originServerTs: number; + // (undocumented) + redacts?: string; + // (undocumented) + relation?: IEventRelation | null; + // (undocumented) + roomId?: string; + // (undocumented) + sender: string; + // (undocumented) + stateKey?: string; + // (undocumented) + status: EventStatus; + // (undocumented) + type: string; + // (undocumented) + unsigned: Record; +} + // @alpha @deprecated (undocumented) export interface Media { // (undocumented) From b38f0f2dd20b5d6bedb9199b66a415c5d0ec5700 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 13:33:41 +0100 Subject: [PATCH 058/156] Drop js-sdk --- .../element-web-module-api/element-web-module-api.api.md | 9 --------- packages/element-web-module-api/package.json | 2 -- packages/element-web-module-api/src/models/event.ts | 5 ----- 3 files changed, 16 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 3a8474d949..b2bda29ab8 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -4,10 +4,7 @@ ```ts -import { EventStatus } from 'matrix-js-sdk'; -import { IEventRelation } from 'matrix-js-sdk'; import { JSX } from 'react'; -import { Membership } from 'matrix-js-sdk'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -146,22 +143,16 @@ export interface MatrixEvent { // (undocumented) eventId: string; // (undocumented) - membership?: Membership; - // (undocumented) originServerTs: number; // (undocumented) redacts?: string; // (undocumented) - relation?: IEventRelation | null; - // (undocumented) roomId?: string; // (undocumented) sender: string; // (undocumented) stateKey?: string; // (undocumented) - status: EventStatus; - // (undocumented) type: string; // (undocumented) unsigned: Record; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index ed0b67418a..b9432f9e01 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -38,7 +38,6 @@ "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", - "matrix-js-sdk": "^37.5.0", "matrix-web-i18n": "^3.3.0", "semver": "^7.6.3", "typescript": "^5.7.3", @@ -51,7 +50,6 @@ "@matrix-org/react-sdk-module-api": "*", "@types/react": "*", "@types/react-dom": "*", - "matrix-js-sdk": "*", "matrix-web-i18n": "*", "react": "^19" }, diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/element-web-module-api/src/models/event.ts index 28b9267529..54c787362a 100644 --- a/packages/element-web-module-api/src/models/event.ts +++ b/packages/element-web-module-api/src/models/event.ts @@ -1,5 +1,3 @@ -import type { EventStatus, IEventRelation, Membership } from "matrix-js-sdk"; - /** * Representation of a Matrix event. * @beta @@ -14,8 +12,5 @@ export interface MatrixEvent { stateKey?: string; originServerTs: number; redacts?: string; - membership?: Membership; - status: EventStatus; - relation?: IEventRelation | null; age?: number; } From c49ac2677bb80d5bda47f66e9c536c4ce85f51d1 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 13:42:54 +0100 Subject: [PATCH 059/156] copyright --- packages/element-web-module-api/src/models/event.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/element-web-module-api/src/models/event.ts index 54c787362a..709fb9e18c 100644 --- a/packages/element-web-module-api/src/models/event.ts +++ b/packages/element-web-module-api/src/models/event.ts @@ -1,3 +1,10 @@ +/* +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. +*/ + /** * Representation of a Matrix event. * @beta From e2a89888c29029d9c6d2b34aab553d7acafa35b7 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 14:02:41 +0100 Subject: [PATCH 060/156] Mark MatrixEvent as @alpha and document. --- .../src/models/event.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/element-web-module-api/src/models/event.ts index 709fb9e18c..b75606090e 100644 --- a/packages/element-web-module-api/src/models/event.ts +++ b/packages/element-web-module-api/src/models/event.ts @@ -6,18 +6,43 @@ Please see LICENSE files in the repository root for full details. */ /** - * Representation of a Matrix event. - * @beta + * Representation of a Matrix event, as specified by the client server specification. + * @alpha Subject to change. + * @see https://spec.matrix.org/v1.14/client-server-api/#room-event-format */ export interface MatrixEvent { + /** + * The event ID of this event. + */ eventId: string; - roomId?: string; + /** + * The room ID which contains this event. + */ + roomId: string; + /** + * The Matrix ID of the user who sent this event. + */ sender: string; + /** + * The content of the event. + * If the event was encrypted, this is the decrypted content. + */ content: Record; + /** + * Contains optional extra information about the event. + */ unsigned: Record; + /** + * The type of the event. + */ type: string; + /** + * The state key of the event. + * If this key is set, including `""` then the event is a state event. + */ stateKey?: string; + /** + * Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent. + */ originServerTs: number; - redacts?: string; - age?: number; } From 0d4a45d4472a493f25bf46f73afe49032797f3e2 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 14:03:10 +0100 Subject: [PATCH 061/156] Update custom components API to have @alpha stability. --- .../element-web-module-api.api.md | 22 ++++--------------- .../src/api/custom-components.ts | 4 ++-- .../element-web-module-api/src/api/index.ts | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index b2bda29ab8..6d6403735e 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -22,6 +22,7 @@ export interface AliasCustomisations { export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { readonly config: ConfigApi; createRoot(element: Element): Root; + // @alpha readonly customComponents: CustomComponentsApi; readonly i18n: I18nApi; readonly rootNode: HTMLElement; @@ -59,11 +60,8 @@ export interface ConfigApi { get(key?: K): Config | Config[K]; } -// @public +// @alpha export interface CustomComponentsApi { - // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "MatrixEvent" which is marked as @beta - // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderFunction" which is marked as @alpha - // Warning: (ae-incompatible-release-tags) The symbol "registerMessageRenderer" is marked as @public, but its signature references "CustomMessageRenderHints" which is marked as @alpha registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; } @@ -134,27 +132,15 @@ export interface LifecycleCustomisations { onLoggedOutAndStorageCleared?(): void; } -// @beta +// @alpha export interface MatrixEvent { - // (undocumented) - age?: number; - // (undocumented) content: Record; - // (undocumented) eventId: string; - // (undocumented) originServerTs: number; - // (undocumented) - redacts?: string; - // (undocumented) - roomId?: string; - // (undocumented) + roomId: string; sender: string; - // (undocumented) stateKey?: string; - // (undocumented) type: string; - // (undocumented) unsigned: Record; } diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 0c8a26712d..becc0f1992 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -48,7 +48,7 @@ export type CustomMessageRenderHints = { /** * Function used to render a message component. - * @alpha Unlikely to change + * @alpha Subject to change. */ export type CustomMessageRenderFunction = ( /** @@ -63,7 +63,7 @@ export type CustomMessageRenderFunction = ( /** * API for inserting custom components into Element. - * @public + * @alpha Subject to change. */ export interface CustomComponentsApi { /** diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 3e9d4d072f..399d807a14 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -90,7 +90,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx /** * The custom message component API. - * @public + * @alpha */ readonly customComponents: CustomComponentsApi; /** From fbd7c21ecc678976f07ccc5ac254a75bcd287432 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 17 Jun 2025 15:28:27 +0100 Subject: [PATCH 062/156] element-web-module-api 1.2.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index b9432f9e01..2e08d65adc 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.1.0", + "version": "1.2.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 9763807c42e1ca386c9504d776c7b24f14458297 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 23 Jun 2025 09:32:26 +0100 Subject: [PATCH 063/156] Extend the Module API in prep of restricted-guests module --- .../element-web-module-api.api.md | 76 ++++++++++++++++++- .../element-web-module-api/src/api/auth.ts | 45 +++++++++++ .../src/api/custom-components.ts | 46 ++++++++++- .../element-web-module-api/src/api/dialog.ts | 68 +++++++++++++++++ .../element-web-module-api/src/api/index.ts | 18 ++++- .../src/api/navigation.ts | 19 +++++ .../element-web-module-api/src/api/profile.ts | 34 +++++++++ .../src/api/watchable.ts | 38 ++++++++++ packages/element-web-module-api/src/index.ts | 4 + 9 files changed, 342 insertions(+), 6 deletions(-) create mode 100644 packages/element-web-module-api/src/api/auth.ts create mode 100644 packages/element-web-module-api/src/api/dialog.ts create mode 100644 packages/element-web-module-api/src/api/navigation.ts create mode 100644 packages/element-web-module-api/src/api/profile.ts create mode 100644 packages/element-web-module-api/src/api/watchable.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 6d6403735e..d0df58efb2 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -4,11 +4,26 @@ ```ts +import { ComponentType } from 'react'; import { JSX } from 'react'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; +// @public +export interface AccountAuthApiExtension { + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} + +// @public +export interface AccountAuthInfo { + accessToken: string; + deviceId: string; + homeserverUrl: string; + refreshToken?: string; + userId: string; +} + // @alpha @deprecated (undocumented) export interface AliasCustomisations { // (undocumented) @@ -19,12 +34,13 @@ export interface AliasCustomisations { // Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha // // @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { readonly config: ConfigApi; createRoot(element: Element): Root; // @alpha readonly customComponents: CustomComponentsApi; readonly i18n: I18nApi; + readonly navigation: NavigationApi; readonly rootNode: HTMLElement; } @@ -63,6 +79,7 @@ export interface ConfigApi { // @alpha export interface CustomComponentsApi { registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; } // @alpha @@ -73,13 +90,49 @@ export type CustomMessageComponentProps = { // @alpha export type CustomMessageRenderFunction = ( props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JSX.Element; +originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; // @alpha export type CustomMessageRenderHints = { allowEditingEvent?: boolean; }; +// @alpha +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +// @alpha +export type CustomRoomPreviewBarRenderFunction = ( +props: CustomRoomPreviewBarComponentProps, +originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; + +// @public +export interface DialogApiExtension { + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props?: P): DialogHandle; +} + +// @public +export type DialogHandle = { + finished: Promise<{ + ok: boolean; + model: M; + }>; + close(): void; +}; + +// @public +export interface DialogOptions { + title: string; +} + +// @public +export type DialogProps = { + onSubmit(model: M): void; + cancel(): void; +}; + // @alpha @deprecated (undocumented) export interface DirectoryCustomisations { // (undocumented) @@ -216,11 +269,28 @@ export class ModuleLoader { start(): Promise; } +// @public +export interface NavigationApi { + toMatrixToLink(link: string, join?: boolean): Promise; +} + // @alpha -export type OriginalComponentProps = { +export type OriginalMessageComponentProps = { showUrlPreview?: boolean; }; +// @public +export interface Profile { + displayName?: string; + userId?: string; +} + +// @public +export interface ProfileApiExtension { + // Warning: (ae-forgotten-export) The symbol "Watchable" needs to be exported by the entry point index.d.ts + readonly profile: Watchable; +} + // @alpha @deprecated (undocumented) export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; diff --git a/packages/element-web-module-api/src/api/auth.ts b/packages/element-web-module-api/src/api/auth.ts new file mode 100644 index 0000000000..5ecdcd3c25 --- /dev/null +++ b/packages/element-web-module-api/src/api/auth.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. +*/ + +/** + * Interface for account authentication information, used for overwriting the current account's authentication state. + * @public + */ +export interface AccountAuthInfo { + /** + * The user ID. + */ + userId: string; + /** + * The device ID. + */ + deviceId: string; + /** + * The access token belonging to this device ID and user ID. + */ + accessToken: string; + /** + * The refresh token belonging to this device ID and user ID. + */ + refreshToken?: string; + /** + * The homeserver URL where the credentials are valid. + */ + homeserverUrl: string; +} + +/** + * Methods to manage authentication in the application. + * @public + */ +export interface AccountAuthApiExtension { + /** + * Overwrite the current account's authentication state with the provided account information. + * @param accountInfo - The account authentication information to overwrite the current state with. + */ + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index becc0f1992..b97728dc05 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -24,7 +24,7 @@ export type CustomMessageComponentProps = { * Properties to alter the render function of the original component. * @alpha Subject to change. */ -export type OriginalComponentProps = { +export type OriginalMessageComponentProps = { /** * Should previews be shown for this event. * This may be overriden by user preferences. @@ -58,7 +58,31 @@ export type CustomMessageRenderFunction = ( /** * Render function for the original component. This may be omitted if the message would not normally be rendered. */ - originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element, + originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element, +) => JSX.Element; + +/** + * Properties for all message components. + * @alpha Subject to change. + */ +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +/** + * Function used to render a room preview bar component. + * @alpha Unlikely to change + */ +export type CustomRoomPreviewBarRenderFunction = ( + /** + * Properties for the room preview bar to be rendered. + */ + props: CustomRoomPreviewBarComponentProps, + /** + * Render function for the original component. + */ + originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element, ) => JSX.Element; /** @@ -95,4 +119,22 @@ export interface CustomComponentsApi { renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints, ): void; + + /** + * Register a renderer for the room preview bar. + * + * The render function should return a rendered component. + * + * @param renderer - The render function for the room preview bar. + * @example + * ``` + * customComponents.registerRoomPreviewBar((props, OriginalComponent) => { + * if (props.roomId === "!some_special_room_id:server") { + * return ; + * } + * return ; + * }); + * ``` + */ + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; } diff --git a/packages/element-web-module-api/src/api/dialog.ts b/packages/element-web-module-api/src/api/dialog.ts new file mode 100644 index 0000000000..a11a25d432 --- /dev/null +++ b/packages/element-web-module-api/src/api/dialog.ts @@ -0,0 +1,68 @@ +/* +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 { ComponentType } from "react"; + +/** + * Options for {@link Api#openDialog}. + * @public + */ +export interface DialogOptions { + /** + * The title of the dialog. + */ + title: string; +} + +/** + * Handle returned by {@link Api#openDialog}. + * @public + */ +export type DialogHandle = { + /** + * Promise that resolves when the dialog is finished. + */ + finished: Promise<{ ok: boolean; model: M }>; + /** + * Method to close the dialog. + */ + close(): void; +}; + +/** + * Props passed to the dialog body component. + * @public + */ +export type DialogProps = { + /** + * Callback to submit the dialog. + * @param model - The model to submit with the dialog. This is typically the data collected. + */ + onSubmit(model: M): void; + /** + * Cancel the dialog programmatically. + */ + cancel(): void; +}; + +/** + * Methods to manage dialogs in the application. + * @public + */ +export interface DialogApiExtension { + /** + * Open a dialog with the given options and body component and return a handle to it. + * @param initialOptions - The initial options for the dialog, such as title and action label. + * @param dialog - The body component to render in the dialog. This component should accept props of type `P`. + * @param props - Additional props to pass to the body + */ + openDialog( + initialOptions: DialogOptions, + dialog: ComponentType

>, + props?: P, + ): DialogHandle; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 399d807a14..8a59260773 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -11,6 +11,10 @@ import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; import { ConfigApi } from "./config"; import { I18nApi } from "./i18n"; import { CustomComponentsApi } from "./custom-components"; +import { NavigationApi } from "./navigation.ts"; +import { DialogApiExtension } from "./dialog.ts"; +import { AccountAuthApiExtension } from "./auth.ts"; +import { ProfileApiExtension } from "./profile.ts"; /** * Module interface for modules to implement. @@ -69,7 +73,12 @@ export function isModule(module: unknown): module is ModuleExport { * The API for modules to interact with the application. * @public */ -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { +export interface Api + extends LegacyModuleApiExtension, + LegacyCustomisationsApiExtension, + DialogApiExtension, + AccountAuthApiExtension, + ProfileApiExtension { /** * The API to read config.json values. * Keys should be scoped to the module in reverse domain name notation. @@ -93,6 +102,13 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx * @alpha */ readonly customComponents: CustomComponentsApi; + + /** + * API to navigate the application. + * @public + */ + readonly navigation: NavigationApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts new file mode 100644 index 0000000000..a3b073274c --- /dev/null +++ b/packages/element-web-module-api/src/api/navigation.ts @@ -0,0 +1,19 @@ +/* +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. +*/ + +/** + * API methods to navigate the application. + * @public + */ +export interface NavigationApi { + /** + * Navigate to a permalink, optionally causing a join if the user is not already a member of the room/space. + * @param link - The permalink to navigate to, e.g. `https://matrix.to/#/!roomId:example.com`. + * @param join - If true, the user will be made to attempt to join the room/space if they are not already a member. + */ + toMatrixToLink(link: string, join?: boolean): Promise; +} diff --git a/packages/element-web-module-api/src/api/profile.ts b/packages/element-web-module-api/src/api/profile.ts new file mode 100644 index 0000000000..097a28d86f --- /dev/null +++ b/packages/element-web-module-api/src/api/profile.ts @@ -0,0 +1,34 @@ +/* +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 { Watchable } from "./watchable.ts"; + +/** + * The profile of the user currently logged in. + * @public + */ +export interface Profile { + /** + * The user ID of the logged-in user, if undefined then no user is logged in. + */ + userId?: string; + /** + * The display name of the logged-in user. + */ + displayName?: string; +} + +/** + * API extensions for modules to access the profile of the logged-in user. + * @public + */ +export interface ProfileApiExtension { + /** + * The profile of the user currently logged in. + */ + readonly profile: Watchable; +} diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/element-web-module-api/src/api/watchable.ts new file mode 100644 index 0000000000..7f74f775c1 --- /dev/null +++ b/packages/element-web-module-api/src/api/watchable.ts @@ -0,0 +1,38 @@ +/* +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. +*/ + +type WatchFn = (value: T) => void; + +/** + * Utility class to wrap a value and allow listeners to be notified when the value changes. + */ +export class Watchable { + private readonly listeners = new Set>(); + + public constructor(private currentValue: T) {} + + public get value(): T { + return this.currentValue; + } + + public set value(value: T) { + if (this.currentValue !== value) { + this.currentValue = value; + for (const listener of this.listeners) { + listener(this.currentValue); + } + } + } + + public watch(listener: (value: T) => void): void { + this.listeners.add(listener); + } + + public unwatch(listener: (value: T) => void): void { + this.listeners.delete(listener); + } +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 0a7337ec21..952480135f 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -13,3 +13,7 @@ export type * from "./models/event"; export type * from "./api/custom-components"; export type * from "./api/legacy-modules"; export type * from "./api/legacy-customisations"; +export type * from "./api/auth"; +export type * from "./api/dialog"; +export type * from "./api/profile"; +export type * from "./api/navigation"; From 20098f132b913e7824a37fa015c6e9fc4061fca2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 24 Jun 2025 09:50:02 +0100 Subject: [PATCH 064/156] Iterate --- .../element-web-module-api.api.md | 19 ++++-- packages/element-web-module-api/package.json | 2 +- .../element-web-module-api/src/api/dialog.ts | 8 +-- .../src/api/watchable.test.ts | 58 +++++++++++++++++++ .../src/api/watchable.ts | 29 ++++++++-- packages/element-web-module-api/src/index.ts | 1 + 6 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 packages/element-web-module-api/src/api/watchable.test.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index d0df58efb2..51eed63c63 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -110,14 +110,14 @@ originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) = // @public export interface DialogApiExtension { - openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props?: P): DialogHandle; + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; } // @public export type DialogHandle = { finished: Promise<{ ok: boolean; - model: M; + model: M | null; }>; close(): void; }; @@ -130,7 +130,7 @@ export interface DialogOptions { // @public export type DialogProps = { onSubmit(model: M): void; - cancel(): void; + onCancel(): void; }; // @alpha @deprecated (undocumented) @@ -287,7 +287,6 @@ export interface Profile { // @public export interface ProfileApiExtension { - // Warning: (ae-forgotten-export) The symbol "Watchable" needs to be exported by the entry point index.d.ts readonly profile: Watchable; } @@ -318,6 +317,18 @@ export type Variables = { [key: string]: number | string | undefined; }; +// @public +export class Watchable { + constructor(currentValue: T); + // (undocumented) + unwatch(listener: (value: T) => void): void; + // (undocumented) + get value(): T; + set value(value: T); + // (undocumented) + watch(listener: (value: T) => void): void; +} + // @alpha @deprecated (undocumented) export interface WidgetPermissionsCustomisations { preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 2e08d65adc..1f8466cbba 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -43,7 +43,7 @@ "typescript": "^5.7.3", "vite": "^6.1.6", "vite-plugin-dts": "^4.5.0", - "vitest": "^3.0.5", + "vitest": "^3.2.4", "vitest-sonar-reporter": "^2.0.0" }, "peerDependencies": { diff --git a/packages/element-web-module-api/src/api/dialog.ts b/packages/element-web-module-api/src/api/dialog.ts index a11a25d432..2361d6dace 100644 --- a/packages/element-web-module-api/src/api/dialog.ts +++ b/packages/element-web-module-api/src/api/dialog.ts @@ -26,7 +26,7 @@ export type DialogHandle = { /** * Promise that resolves when the dialog is finished. */ - finished: Promise<{ ok: boolean; model: M }>; + finished: Promise<{ ok: boolean; model: M | null }>; /** * Method to close the dialog. */ @@ -46,7 +46,7 @@ export type DialogProps = { /** * Cancel the dialog programmatically. */ - cancel(): void; + onCancel(): void; }; /** @@ -60,9 +60,9 @@ export interface DialogApiExtension { * @param dialog - The body component to render in the dialog. This component should accept props of type `P`. * @param props - Additional props to pass to the body */ - openDialog( + openDialog( initialOptions: DialogOptions, dialog: ComponentType

>, - props?: P, + props: P, ): DialogHandle; } diff --git a/packages/element-web-module-api/src/api/watchable.test.ts b/packages/element-web-module-api/src/api/watchable.test.ts new file mode 100644 index 0000000000..c045286e2c --- /dev/null +++ b/packages/element-web-module-api/src/api/watchable.test.ts @@ -0,0 +1,58 @@ +/* +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 { expect, test, vitest } from "vitest"; + +import { Watchable } from "./watchable"; + +test("initial value is set correctly", () => { + const watchable = new Watchable(42); + expect(watchable.value).toBe(42); +}); + +test("value can be updated", () => { + const watchable = new Watchable(100); + watchable.value = 200; + expect(watchable.value).toBe(200); +}); + +test("watchers are notified on value change", () => { + const watchable = new Watchable(1); + const listener = vitest.fn(); + + watchable.watch(listener); + watchable.value = 2; // This should trigger the listener + expect(listener).toHaveBeenCalledExactlyOnceWith(2); + + watchable.unwatch(listener); // Clean up after the test +}); + +test("watchers are not notified if value does not change", () => { + const watchable = new Watchable(10); + const listener = vitest.fn(); + + watchable.watch(listener); + watchable.value = 10; // This should not trigger the listener + expect(listener).not.toHaveBeenCalled(); + + watchable.unwatch(listener); // Clean up after the test +}); + +test("when value is an object, shallow comparison works", () => { + const watchable = new Watchable({ a: 1, b: 2 }); + const listener = vitest.fn(); + watchable.watch(listener); + + // Update with a new object that has the same properties + watchable.value = { a: 3, b: 2 }; // This should trigger the listener + expect(listener).toHaveBeenCalledExactlyOnceWith({ a: 3, b: 2 }); + listener.mockClear(); + watchable.value = { a: 3, b: 2 }; // This should not trigger the listener again + expect(listener).not.toHaveBeenCalled(); + + watchable.unwatch(listener); // Clean up after the test +}); diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/element-web-module-api/src/api/watchable.ts index 7f74f775c1..4bd8d74239 100644 --- a/packages/element-web-module-api/src/api/watchable.ts +++ b/packages/element-web-module-api/src/api/watchable.ts @@ -7,8 +7,21 @@ Please see LICENSE files in the repository root for full details. type WatchFn = (value: T) => void; +function shallowCompare(obj1: T, obj2: T): boolean { + return ( + Object.keys(obj1).length === Object.keys(obj2).length && + Object.keys(obj1).every((key) => obj1[key as keyof T] === obj2[key as keyof T]) + ); +} + +function isObject(value: unknown): value is object { + return value !== null && typeof value === "object"; +} + /** * Utility class to wrap a value and allow listeners to be notified when the value changes. + * If T is an object, it will use a shallow comparison to determine if the value has changed. + * @public */ export class Watchable { private readonly listeners = new Set>(); @@ -20,11 +33,17 @@ export class Watchable { } public set value(value: T) { - if (this.currentValue !== value) { - this.currentValue = value; - for (const listener of this.listeners) { - listener(this.currentValue); - } + // If the value hasn't changed, do nothing. + if (value === this.currentValue) { + return; + } + if (isObject(value) && isObject(this.currentValue) && shallowCompare(this.currentValue as object, value)) { + return; + } + + this.currentValue = value; + for (const listener of this.listeners) { + listener(this.currentValue); } } diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 952480135f..196db8d9fc 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -17,3 +17,4 @@ export type * from "./api/auth"; export type * from "./api/dialog"; export type * from "./api/profile"; export type * from "./api/navigation"; +export * from "./api/watchable"; From 24cc2455f97ac24ce9df130c5b252206b1afa63d Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 3 Jul 2025 12:35:55 +0100 Subject: [PATCH 065/156] Add message hint to prevent media from being downloaded. --- .../element-web-module-api/element-web-module-api.api.md | 1 + .../element-web-module-api/src/api/custom-components.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 6d6403735e..ce5f5e8ea5 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -78,6 +78,7 @@ originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JS // @alpha export type CustomMessageRenderHints = { allowEditingEvent?: boolean; + allowDownloadingMedia?: boolean | Promise; }; // @alpha @deprecated (undocumented) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index becc0f1992..67d7a6bef9 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -44,6 +44,12 @@ export type CustomMessageRenderHints = { * Default is true. */ allowEditingEvent?: boolean; + /** + * If an event contains media, this function will be called to check + * if the media can be prompted to be downloaded as a file. + * Default is true. + */ + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; }; /** From cfb8c685d4fe5f926783bba8f475d78f583de0b9 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 3 Jul 2025 13:06:26 +0100 Subject: [PATCH 066/156] update sig --- packages/element-web-module-api/element-web-module-api.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index ce5f5e8ea5..81fd045f8f 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -78,7 +78,7 @@ originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element) => JS // @alpha export type CustomMessageRenderHints = { allowEditingEvent?: boolean; - allowDownloadingMedia?: boolean | Promise; + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; }; // @alpha @deprecated (undocumented) From 0b5681bf5bfa83bc5e3e13569bbeb169a033eddf Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 3 Jul 2025 14:08:10 +0100 Subject: [PATCH 067/156] Update custom-components.ts --- packages/element-web-module-api/src/api/custom-components.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index 67d7a6bef9..ba477057cf 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -47,7 +47,7 @@ export type CustomMessageRenderHints = { /** * If an event contains media, this function will be called to check * if the media can be prompted to be downloaded as a file. - * Default is true. + * If this function is not supplied, media downloads are allowed. */ allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; }; From 0b82316dbe09c02940f5c53ef9af8cf1645387ab Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 4 Jul 2025 15:46:04 +0100 Subject: [PATCH 068/156] element-web-module-api 1.3.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 2e08d65adc..30c6f69d99 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.2.0", + "version": "1.3.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 9c2ca140bb818b6a13b6bd812e6313b71cc271fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Jul 2025 16:27:09 +0100 Subject: [PATCH 069/156] Iterate --- .../element-web-module-api.api.md | 4 ++++ packages/element-web-module-api/package.json | 1 + .../element-web-module-api/src/api/profile.ts | 4 ++++ .../src/api/watchable.ts | 20 +++++++++++++++++++ .../element-web-module-api/vite.config.ts | 12 ++++++++++- 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index a93fdbbfb3..56fa2bb06b 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -283,6 +283,7 @@ export type OriginalMessageComponentProps = { // @public export interface Profile { displayName?: string; + isGuest?: boolean; userId?: string; } @@ -312,6 +313,9 @@ export interface UserIdentifierCustomisations { }): string | null; } +// @public +export function useWatchable(watchable: Watchable): T; + // @public export type Variables = { count?: number; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index a3c6a78a47..c98d0803f3 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -39,6 +39,7 @@ "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", + "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", "vite": "^6.1.6", diff --git a/packages/element-web-module-api/src/api/profile.ts b/packages/element-web-module-api/src/api/profile.ts index 097a28d86f..5a09537587 100644 --- a/packages/element-web-module-api/src/api/profile.ts +++ b/packages/element-web-module-api/src/api/profile.ts @@ -12,6 +12,10 @@ import { Watchable } from "./watchable.ts"; * @public */ export interface Profile { + /** + * Indicates whether the user is a guest user. + */ + isGuest?: boolean; /** * The user ID of the logged-in user, if undefined then no user is logged in. */ diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/element-web-module-api/src/api/watchable.ts index 4bd8d74239..2f82a9b732 100644 --- a/packages/element-web-module-api/src/api/watchable.ts +++ b/packages/element-web-module-api/src/api/watchable.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 { useEffect, useState } from "react"; + type WatchFn = (value: T) => void; function shallowCompare(obj1: T, obj2: T): boolean { @@ -55,3 +57,21 @@ export class Watchable { this.listeners.delete(listener); } } + +/** + * A React hook to use an updated Watchable value. + * @param watchable - The Watchable instance to watch. + * @returns The live value of the Watchable. + * @public + */ +export function useWatchable(watchable: Watchable): T { + const [value, setValue] = useState(watchable.value); + useEffect(() => { + setValue(watchable.value); + watchable.watch(setValue); + return (): void => { + watchable.unwatch(setValue); + }; + }, [watchable]); + return value; +} diff --git a/packages/element-web-module-api/vite.config.ts b/packages/element-web-module-api/vite.config.ts index 6b0d47a8a2..01d3eebba6 100644 --- a/packages/element-web-module-api/vite.config.ts +++ b/packages/element-web-module-api/vite.config.ts @@ -9,6 +9,7 @@ import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { defineConfig } from "vite"; import dts from "vite-plugin-dts"; +import externalGlobals from "rollup-plugin-external-globals"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -23,9 +24,18 @@ export default defineConfig({ target: "esnext", sourcemap: true, }, - plugins: [dts()], + plugins: [ + dts(), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), + ], define: { __VERSION__: JSON.stringify(process.env.npm_package_version), + // 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" } }, }, test: { coverage: { From a3c31d82d63d653b27ecafee8d4177f5245214e5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 28 Jul 2025 15:58:30 +0100 Subject: [PATCH 070/156] Bump module-api to v1.4.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index c98d0803f3..709c2fd01f 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.3.0", + "version": "1.4.0", "description": "Module API surface for element-web", "repository": { "type": "git", From f397f0bb7d04f65b88aa52f506c6b23dbd99aace Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 29 Jul 2025 10:29:32 +0100 Subject: [PATCH 071/156] Fix cjs export --- packages/element-web-module-api/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 709c2fd01f..1829215efe 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.4.0", + "version": "1.4.1", "description": "Module API surface for element-web", "repository": { "type": "git", @@ -18,7 +18,7 @@ ".": { "types": "./lib/element-web-module-api-alpha.d.ts", "import": "./lib/element-web-plugin-engine.js", - "require": "./lib/element-web-plugin-engine.umd.js" + "require": "./lib/element-web-plugin-engine.umd.cjs" } }, "files": [ From ac81502c0acca6a5f92a7fbe82931ed485fe016e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 01:49:15 +0000 Subject: [PATCH 072/156] 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] --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 1829215efe..9d04514a8b 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -42,7 +42,7 @@ "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", - "vite": "^6.1.6", + "vite": "^6.3.6", "vite-plugin-dts": "^4.5.0", "vitest": "^3.2.4", "vitest-sonar-reporter": "^2.0.0" From 85ae19a093ba1211520d0276196d2712aedb4f54 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 073/156] Update dependency vite to v7 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 9d04514a8b..7f4c20eef8 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -42,7 +42,7 @@ "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", - "vite": "^6.3.6", + "vite": "^7.0.0", "vite-plugin-dts": "^4.5.0", "vitest": "^3.2.4", "vitest-sonar-reporter": "^2.0.0" From 2cd86f7c3d0b3c0ade2c9a3918ef8d8d385e1d39 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Sep 2025 10:58:41 +0100 Subject: [PATCH 074/156] Add some experimental module interfaces ...with somewhat placeholder names as they're the best I can think of right now: * 'Extras': To add new bits of UI to places (specifically the space panel) * 'Builtins': For modules to render components that are part of Element Web * 'Navigation': For modules to add paths to the URL router --- .../src/api/builtins.ts | 19 +++++++++++++++++++ .../element-web-module-api/src/api/extras.ts | 18 ++++++++++++++++++ .../element-web-module-api/src/api/index.ts | 6 ++++++ .../src/api/navigation.ts | 6 ++++++ packages/element-web-module-api/src/index.ts | 2 ++ 5 files changed, 51 insertions(+) create mode 100644 packages/element-web-module-api/src/api/builtins.ts create mode 100644 packages/element-web-module-api/src/api/extras.ts diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts new file mode 100644 index 0000000000..31280366e0 --- /dev/null +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -0,0 +1,19 @@ +/* +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. +*/ + +export interface RoomViewProps { + roomId?: string; +} + +/** + * Exposes components that are part of Element Web to allow modules to render them + * as part of their custom components (because they can't import the components from + * Element Web since it would cause a dependency cycle) + */ +export interface BuiltinsApi { + getRoomViewComponent(): React.ComponentType; +} diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts new file mode 100644 index 0000000000..c4ad313b5f --- /dev/null +++ b/packages/element-web-module-api/src/api/extras.ts @@ -0,0 +1,18 @@ +/* +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 { JSX } from "react"; + +interface SpacePanelItemProps { + isPanelCollapsed: boolean; +} + +export type SpacePanelItemRenderFunction = (props: SpacePanelItemProps) => JSX.Element; + +export interface ExtrasApi { + addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 8a59260773..9ad4fc614f 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -15,6 +15,8 @@ import { NavigationApi } from "./navigation.ts"; import { DialogApiExtension } from "./dialog.ts"; import { AccountAuthApiExtension } from "./auth.ts"; import { ProfileApiExtension } from "./profile.ts"; +import { ExtrasApi } from "./extras.ts"; +import { BuiltinsApi } from "./builtins.ts"; /** * Module interface for modules to implement. @@ -103,12 +105,16 @@ export interface Api */ readonly customComponents: CustomComponentsApi; + readonly builtins: BuiltinsApi; + /** * API to navigate the application. * @public */ readonly navigation: NavigationApi; + readonly extras: ExtrasApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts index a3b073274c..063b683445 100644 --- a/packages/element-web-module-api/src/api/navigation.ts +++ b/packages/element-web-module-api/src/api/navigation.ts @@ -5,6 +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 { JSX } from "react"; + +export type LocationRenderFunction = () => JSX.Element; + /** * API methods to navigate the application. * @public @@ -16,4 +20,6 @@ export interface NavigationApi { * @param join - If true, the user will be made to attempt to join the room/space if they are not already a member. */ toMatrixToLink(link: string, join?: boolean): Promise; + + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; } diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 196db8d9fc..e89adc482d 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -11,10 +11,12 @@ export type { Config, ConfigApi } from "./api/config"; export type { I18nApi, Variables, Translations } from "./api/i18n"; export type * from "./models/event"; export type * from "./api/custom-components"; +export type * from "./api/extras"; export type * from "./api/legacy-modules"; export type * from "./api/legacy-customisations"; export type * from "./api/auth"; export type * from "./api/dialog"; export type * from "./api/profile"; export type * from "./api/navigation"; +export type * from "./api/builtins"; export * from "./api/watchable"; From 968577552866275e661643efa4fdd2f4ae811375 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Sep 2025 16:07:31 +0100 Subject: [PATCH 075/156] Maybe make api-extractor happy --- .../element-web-module-api.api.md | 35 ++++++++++++++++++- .../src/api/builtins.ts | 5 +++ .../element-web-module-api/src/api/extras.ts | 8 +++++ .../element-web-module-api/src/api/index.ts | 8 +++++ .../src/api/navigation.ts | 7 +++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 56fa2bb06b..9b2234e377 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -35,15 +35,26 @@ export interface AliasCustomisations { // // @public export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { + // @alpha + readonly builtins: BuiltinsApi; readonly config: ConfigApi; createRoot(element: Element): Root; // @alpha readonly customComponents: CustomComponentsApi; + // @alpha + readonly extras: ExtrasApi; readonly i18n: I18nApi; + // Warning: (ae-incompatible-release-tags) The symbol "navigation" is marked as @public, but its signature references "NavigationApi" which is marked as @alpha readonly navigation: NavigationApi; readonly rootNode: HTMLElement; } +// @alpha +export interface BuiltinsApi { + // (undocumented) + getRoomViewComponent(): React.ComponentType; +} + // @alpha @deprecated (undocumented) export interface ChatExportCustomisations { getForceChatExportParameters(): { @@ -140,6 +151,12 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @alpha +export interface ExtrasApi { + // (undocumented) + addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void; +} + // @public export interface I18nApi { get language(): string; @@ -186,6 +203,9 @@ export interface LifecycleCustomisations { onLoggedOutAndStorageCleared?(): void; } +// @alpha +export type LocationRenderFunction = () => JSX.Element; + // @alpha export interface MatrixEvent { content: Record; @@ -270,8 +290,10 @@ export class ModuleLoader { start(): Promise; } -// @public +// @alpha export interface NavigationApi { + // (undocumented) + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; toMatrixToLink(link: string, join?: boolean): Promise; } @@ -297,9 +319,20 @@ export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; } +// @alpha +export interface RoomViewProps { + // (undocumented) + roomId?: string; +} + // @alpha @deprecated (undocumented) export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +// Warning: (ae-forgotten-export) The symbol "SpacePanelItemProps" needs to be exported by the entry point index.d.ts +// +// @alpha +export type SpacePanelItemRenderFunction = (props: SpacePanelItemProps) => JSX.Element; + // @public export type Translations = Record; diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index c4ad313b5f..da29a61ac3 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -11,8 +11,16 @@ interface SpacePanelItemProps { isPanelCollapsed: boolean; } +/** + * The type of the function used to render a space panel item. + * @alpha + */ export type SpacePanelItemRenderFunction = (props: SpacePanelItemProps) => JSX.Element; +/** + * API for inserting extra UI into Element Web. + * @alpha Subject to change. + */ export interface ExtrasApi { addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void; } diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 9ad4fc614f..dacc7501be 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -105,6 +105,10 @@ export interface Api */ readonly customComponents: CustomComponentsApi; + /** + * Allows modules to render components that are part of Element Web. + * @alpha + */ readonly builtins: BuiltinsApi; /** @@ -113,6 +117,10 @@ export interface Api */ readonly navigation: NavigationApi; + /** + * Allows modules to insert extra UI into Element Web. + * @alpha + */ readonly extras: ExtrasApi; /** diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts index 063b683445..e5bef01287 100644 --- a/packages/element-web-module-api/src/api/navigation.ts +++ b/packages/element-web-module-api/src/api/navigation.ts @@ -7,11 +7,16 @@ Please see LICENSE files in the repository root for full details. import { JSX } from "react"; +/** + * A function called to render a component when a user navigates to the corresponding + * location. Currently renders alongside just the SpacePanel. + * @alpha + */ export type LocationRenderFunction = () => JSX.Element; /** * API methods to navigate the application. - * @public + * @alpha */ export interface NavigationApi { /** From 446830776f8fba4a6e18f9689ead8affcbf28ca2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 25 Sep 2025 11:57:13 +0100 Subject: [PATCH 076/156] New interface for adding space panel items --- .../element-web-module-api/src/api/extras.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index da29a61ac3..b2d3155d56 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -7,20 +7,31 @@ Please see LICENSE files in the repository root for full details. import { JSX } from "react"; -interface SpacePanelItemProps { - isPanelCollapsed: boolean; -} +/** + * Any kind of event that can trigger a button + * @alpha + */ +export type ButtonEvent = React.MouseEvent | React.KeyboardEvent | React.FormEvent; /** * The type of the function used to render a space panel item. * @alpha */ -export type SpacePanelItemRenderFunction = (props: SpacePanelItemProps) => JSX.Element; +export interface SpacePanelItemProps { + spaceKey?: string; + className?: string; + icon?: JSX.Element; + label: string; + contextMenuTooltip?: string; + style?: React.CSSProperties; + //notificationState?: NotificationState; + onClick?(ev?: ButtonEvent): void; +} /** * API for inserting extra UI into Element Web. * @alpha Subject to change. */ export interface ExtrasApi { - addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void; + addSpacePanelItem(props: SpacePanelItemProps): void; } From cf869c9454bb2b28e30a741780c1970e468abd1b Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 25 Sep 2025 17:33:53 +0100 Subject: [PATCH 077/156] Tweak api so it can be called multiple times to update the space item's properties. --- .../element-web-module-api.api.md | 20 ++++++++++++++----- .../element-web-module-api/src/api/extras.ts | 20 +++++++++---------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 9b2234e377..cbe082c791 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -153,8 +153,7 @@ export interface DirectoryCustomisations { // @alpha export interface ExtrasApi { - // (undocumented) - addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void; + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; } // @public @@ -328,10 +327,21 @@ export interface RoomViewProps { // @alpha @deprecated (undocumented) export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; -// Warning: (ae-forgotten-export) The symbol "SpacePanelItemProps" needs to be exported by the entry point index.d.ts -// // @alpha -export type SpacePanelItemRenderFunction = (props: SpacePanelItemProps) => JSX.Element; +export interface SpacePanelItemProps { + // (undocumented) + className?: string; + // (undocumented) + contextMenuTooltip?: string; + // (undocumented) + icon?: JSX.Element; + // (undocumented) + label: string; + // (undocumented) + onSelected?(): void; + // (undocumented) + style?: React.CSSProperties; +} // @public export type Translations = Record | React.KeyboardEvent | React.FormEvent; - -/** - * The type of the function used to render a space panel item. + * Properties of an item added to the Space panel * @alpha */ export interface SpacePanelItemProps { - spaceKey?: string; className?: string; icon?: JSX.Element; label: string; contextMenuTooltip?: string; style?: React.CSSProperties; //notificationState?: NotificationState; - onClick?(ev?: ButtonEvent): void; + onSelected?(): void; } /** @@ -33,5 +26,12 @@ export interface SpacePanelItemProps { * @alpha Subject to change. */ export interface ExtrasApi { - addSpacePanelItem(props: SpacePanelItemProps): void; + /** + * Inserts an item into the space panel as if it were a space button, below + * buttons for other spaces. + * If called again with the same spaceKey, will update the existing item. + * @param spaceKey - A key to identify this space-like item. + * @param props - Properties of the item to add. + */ + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; } From 6d0a715ac0d6ceb2f96f6c146cf912183c386946 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Oct 2025 11:56:48 +0100 Subject: [PATCH 078/156] Tweak doc --- packages/element-web-module-api/src/api/builtins.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index 701ed1f684..ee75fad50e 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -14,9 +14,10 @@ export interface RoomViewProps { } /** - * Exposes components that are part of Element Web to allow modules to render them - * as part of their custom components (because they can't import the components from - * Element Web since it would cause a dependency cycle) + * Exposes components and classes that are part of Element Web to allow modules to + * render the components as part of their custom components or use the classes + * (because they can't import the components from Element Web since it would cause + * a dependency cycle) * @alpha */ export interface BuiltinsApi { From b83b5f4ad6e028c4b35f0fadcc575bc4fcc25b46 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Oct 2025 12:10:07 +0100 Subject: [PATCH 079/156] Add more docs --- .../element-web-module-api.api.md | 7 ------ .../src/api/builtins.ts | 7 ++++++ .../element-web-module-api/src/api/extras.ts | 24 ++++++++++++++++++- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index cbe082c791..c01f2e90fa 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -51,7 +51,6 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha export interface BuiltinsApi { - // (undocumented) getRoomViewComponent(): React.ComponentType; } @@ -329,17 +328,11 @@ export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; // @alpha export interface SpacePanelItemProps { - // (undocumented) className?: string; - // (undocumented) contextMenuTooltip?: string; - // (undocumented) icon?: JSX.Element; - // (undocumented) label: string; - // (undocumented) onSelected?(): void; - // (undocumented) style?: React.CSSProperties; } diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index ee75fad50e..68ab7a39e2 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -21,5 +21,12 @@ export interface RoomViewProps { * @alpha */ export interface BuiltinsApi { + /** + * Returns the RoomView component used by Element Web to render a room such that + * modules can render it as part of their own custom room views. + * + * @alpha + * @returns The RoomView component. + */ getRoomViewComponent(): React.ComponentType; } diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index 6da5aa7848..ddef7b7c20 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -12,12 +12,34 @@ import { JSX } from "react"; * @alpha */ export interface SpacePanelItemProps { + /** + * A CSS class name for the item + */ className?: string; + + /** + * An icon to show in the item + */ icon?: JSX.Element; + + /** + * The label to show in the item + */ label: string; + + /** + * A tooltip to show when hovering over the item + */ contextMenuTooltip?: string; + + /** + * Styles to apply to the item + */ style?: React.CSSProperties; - //notificationState?: NotificationState; + + /** + * Callback when the item is selected + */ onSelected?(): void; } From e1f7a798a20ef5dcc55bbf016500848e70e0e00e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Oct 2025 12:11:39 +0100 Subject: [PATCH 080/156] More doc --- packages/element-web-module-api/element-web-module-api.api.md | 1 - packages/element-web-module-api/src/api/builtins.ts | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index c01f2e90fa..38e1e073a4 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -319,7 +319,6 @@ export interface RoomListCustomisations { // @alpha export interface RoomViewProps { - // (undocumented) roomId?: string; } diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index 68ab7a39e2..99d60e031f 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -10,6 +10,9 @@ Please see LICENSE files in the repository root for full details. * @alpha Subject to change. */ export interface RoomViewProps { + /** + * The ID of the room to render. + */ roomId?: string; } From b5639c25a42e2c23e580234a551bf19c77c1b5f8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Oct 2025 12:14:20 +0100 Subject: [PATCH 081/156] Fix public/alpha statuses --- .../element-web-module-api/element-web-module-api.api.md | 5 ++--- packages/element-web-module-api/src/api/navigation.ts | 8 +++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 38e1e073a4..d617845bf0 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -44,7 +44,6 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha readonly extras: ExtrasApi; readonly i18n: I18nApi; - // Warning: (ae-incompatible-release-tags) The symbol "navigation" is marked as @public, but its signature references "NavigationApi" which is marked as @alpha readonly navigation: NavigationApi; readonly rootNode: HTMLElement; } @@ -288,9 +287,9 @@ export class ModuleLoader { start(): Promise; } -// @alpha +// @public export interface NavigationApi { - // (undocumented) + // @alpha registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; toMatrixToLink(link: string, join?: boolean): Promise; } diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts index e5bef01287..f7d60b4263 100644 --- a/packages/element-web-module-api/src/api/navigation.ts +++ b/packages/element-web-module-api/src/api/navigation.ts @@ -16,7 +16,7 @@ export type LocationRenderFunction = () => JSX.Element; /** * API methods to navigate the application. - * @alpha + * @public */ export interface NavigationApi { /** @@ -26,5 +26,11 @@ export interface NavigationApi { */ toMatrixToLink(link: string, join?: boolean): Promise; + /** + * Register a renderer for a given location path. + * @param path - The location path to register the renderer for. + * @param renderer - The function that will render the component for the location. + * @alpha + */ registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; } From c938dbe519d94163c6f0af26e7dc175b0ad9d002 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 20 Oct 2025 16:34:58 +0100 Subject: [PATCH 082/156] Rename contextMenuTooltip to just tooltip because it has nothing to do with context menus afaics --- packages/element-web-module-api/src/api/extras.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index ddef7b7c20..a626f67491 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -30,7 +30,7 @@ export interface SpacePanelItemProps { /** * A tooltip to show when hovering over the item */ - contextMenuTooltip?: string; + tooltip?: string; /** * Styles to apply to the item From 79344ffd31596b035968825175b3b711bd2d32b9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 20 Oct 2025 16:36:48 +0100 Subject: [PATCH 083/156] Update docs --- packages/element-web-module-api/element-web-module-api.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index d617845bf0..54ae469ca0 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -327,11 +327,11 @@ export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; // @alpha export interface SpacePanelItemProps { className?: string; - contextMenuTooltip?: string; icon?: JSX.Element; label: string; onSelected?(): void; style?: React.CSSProperties; + tooltip?: string; } // @public From a41a7faa757558c28a1deda13288d124f35c1515 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 20 Oct 2025 17:33:09 +0100 Subject: [PATCH 084/156] docs --- packages/element-web-module-api/src/api/extras.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index a626f67491..e1004ccc7b 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -18,7 +18,7 @@ export interface SpacePanelItemProps { className?: string; /** - * An icon to show in the item + * An icon to show in the item. If not provided, no icon will be shown. */ icon?: JSX.Element; From 824f262ac9fa7cf35503486b62c9933d60b113be Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 20 Oct 2025 17:44:15 +0100 Subject: [PATCH 085/156] Make onSelected non-optional --- packages/element-web-module-api/element-web-module-api.api.md | 2 +- packages/element-web-module-api/src/api/extras.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 54ae469ca0..2d341fde03 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -329,7 +329,7 @@ export interface SpacePanelItemProps { className?: string; icon?: JSX.Element; label: string; - onSelected?(): void; + onSelected: () => void; style?: React.CSSProperties; tooltip?: string; } diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index e1004ccc7b..1c8d355b26 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -40,7 +40,7 @@ export interface SpacePanelItemProps { /** * Callback when the item is selected */ - onSelected?(): void; + onSelected: () => void; } /** From c24cfb63110a3382a5df9d4754aadb8c936ab0e9 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:18:33 +0530 Subject: [PATCH 086/156] Introduce an abstraction for Room All APIs that need to return a room will use this type. --- packages/element-web-module-api/src/index.ts | 1 + .../element-web-module-api/src/models/Room.ts | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 packages/element-web-module-api/src/models/Room.ts diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index e89adc482d..4935d10681 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -10,6 +10,7 @@ export type { Api, Module, ModuleFactory } from "./api"; export type { Config, ConfigApi } from "./api/config"; export type { I18nApi, Variables, Translations } from "./api/i18n"; export type * from "./models/event"; +export type * from "./models/Room"; export type * from "./api/custom-components"; export type * from "./api/extras"; export type * from "./api/legacy-modules"; diff --git a/packages/element-web-module-api/src/models/Room.ts b/packages/element-web-module-api/src/models/Room.ts new file mode 100644 index 0000000000..df2f1c8643 --- /dev/null +++ b/packages/element-web-module-api/src/models/Room.ts @@ -0,0 +1,28 @@ +/* +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 { Watchable } from "../api/watchable"; + +/** + * Represents a room from element-web. + * @public + */ +export interface Room { + /** + * Id of this room. + */ + id: string; + /** + * {@link Watchable} holding the name for this room. + */ + name: Watchable; + /** + * Get the timestamp of the last message in this room. + * @returns last active timestamp + */ + getLastActiveTimestamp: () => number; +} From 949e64c7b526c9c5a81fbbf2a1ba990a92981949 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:19:22 +0530 Subject: [PATCH 087/156] Make field protected so that Watchable can be extended So that you can have custom watchables that have very specific behaviour, think `class NameWatchable extends Watchable`. Useful to have access to `listeners` when you do this. --- packages/element-web-module-api/src/api/watchable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/element-web-module-api/src/api/watchable.ts index 2f82a9b732..3fda595d2c 100644 --- a/packages/element-web-module-api/src/api/watchable.ts +++ b/packages/element-web-module-api/src/api/watchable.ts @@ -26,7 +26,7 @@ function isObject(value: unknown): value is object { * @public */ export class Watchable { - private readonly listeners = new Set>(); + protected readonly listeners = new Set>(); public constructor(private currentValue: T) {} From 00f9283262b01935da40d06c4dc23fa6130e6f3b Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:21:22 +0530 Subject: [PATCH 088/156] Add client API --- .../element-web-module-api/src/api/client.ts | 45 +++++++++++++++++++ .../element-web-module-api/src/api/index.ts | 6 +++ packages/element-web-module-api/src/index.ts | 1 + 3 files changed, 52 insertions(+) create mode 100644 packages/element-web-module-api/src/api/client.ts diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/element-web-module-api/src/api/client.ts new file mode 100644 index 0000000000..1b9bed97dd --- /dev/null +++ b/packages/element-web-module-api/src/api/client.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 type { Room } from "../models/Room"; + +/** + * Modify account data stored on the homeserver. + * @public + */ +export interface AccountDataApi { + /** + * Fetch account data stored from homeserver. + */ + get(eventType: string): unknown; + /** + * Sett account data on the homeserver. + */ + set(eventType: string, content: unknown): Promise; + /** + * Delete account data from homeserver. + */ + delete(eventType: string): Promise; +} + +/** + * Access some limited functionality from the SDK. + * @public + */ +export interface ClientApi { + /** + * Use this to modify account data on the homeserver. + */ + getAccountDataApi: () => AccountDataApi; + + /** + * Fetch room by id from SDK. + * @param id - Id of the room to get + * @returns Room object from SDK + */ + getRoom: (id: string) => Room | null; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index dacc7501be..f95479012c 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -17,6 +17,7 @@ import { AccountAuthApiExtension } from "./auth.ts"; import { ProfileApiExtension } from "./profile.ts"; import { ExtrasApi } from "./extras.ts"; import { BuiltinsApi } from "./builtins.ts"; +import { ClientApi } from "./client.ts"; /** * Module interface for modules to implement. @@ -123,6 +124,11 @@ export interface Api */ readonly extras: ExtrasApi; + /** + * Access some very specific functionality from the client. + */ + readonly client: ClientApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 4935d10681..64f2002a27 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -20,4 +20,5 @@ export type * from "./api/dialog"; export type * from "./api/profile"; export type * from "./api/navigation"; export type * from "./api/builtins"; +export type * from "./api/client"; export * from "./api/watchable"; From d0923c7b76f72d3bf01a75b55c48fe472098b6d7 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:22:39 +0530 Subject: [PATCH 089/156] Update api doc --- .../element-web-module-api.api.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 2d341fde03..042f83601b 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -24,6 +24,13 @@ export interface AccountAuthInfo { userId: string; } +// @public +export interface AccountDataApi { + delete(eventType: string): Promise; + get(eventType: string): unknown; + set(eventType: string, content: unknown): Promise; +} + // @alpha @deprecated (undocumented) export interface AliasCustomisations { // (undocumented) @@ -37,6 +44,7 @@ export interface AliasCustomisations { export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { // @alpha readonly builtins: BuiltinsApi; + readonly client: ClientApi; readonly config: ConfigApi; createRoot(element: Element): Root; // @alpha @@ -64,6 +72,12 @@ export interface ChatExportCustomisations { }; } +// @public +export interface ClientApi { + getAccountDataApi: () => AccountDataApi; + getRoom: (id: string) => Room | null; +} + // @alpha @deprecated (undocumented) export interface ComponentVisibilityCustomisations { shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; @@ -311,6 +325,13 @@ export interface ProfileApiExtension { readonly profile: Watchable; } +// @public +export interface Room { + getLastActiveTimestamp: () => number; + id: string; + name: Watchable; +} + // @alpha @deprecated (undocumented) export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; @@ -359,6 +380,10 @@ export type Variables = { // @public export class Watchable { constructor(currentValue: T); + // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly listeners: Set>; // (undocumented) unwatch(listener: (value: T) => void): void; // (undocumented) From b9a90b2bfc89d8378a36e0d2f8d2dba58bb60434 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:38:36 +0530 Subject: [PATCH 090/156] Render RoomView instead of simply returning it --- packages/element-web-module-api/src/api/builtins.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index 99d60e031f..625329e812 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -25,11 +25,10 @@ export interface RoomViewProps { */ export interface BuiltinsApi { /** - * Returns the RoomView component used by Element Web to render a room such that - * modules can render it as part of their own custom room views. + * Render room view component from element-web. * * @alpha - * @returns The RoomView component. + * @param roomId - Id of the room */ - getRoomViewComponent(): React.ComponentType; + renderRoomView(roomId: string): React.ReactNode; } From 051fd6e8fe6bd6809b8154f7327f9c8b46387bbd Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:39:11 +0530 Subject: [PATCH 091/156] Add method to render room avatar --- packages/element-web-module-api/src/api/builtins.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index 625329e812..cf43795576 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -24,6 +24,15 @@ export interface RoomViewProps { * @alpha */ export interface BuiltinsApi { + /** + * Render room avatar component from element-web. + * + * @alpha + * @param roomId - Id of the room + * @param size - Size of the avatar to render + */ + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + /** * Render room view component from element-web. * From 4930da1e97432174408e2439b59d0430f9cfccf9 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:43:10 +0530 Subject: [PATCH 092/156] Update api doc --- packages/element-web-module-api/element-web-module-api.api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 2d341fde03..5aeb3eb2ed 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -50,7 +50,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha export interface BuiltinsApi { - getRoomViewComponent(): React.ComponentType; + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + renderRoomView(roomId: string): React.ReactNode; } // @alpha @deprecated (undocumented) From 7c40be90546a73b26ae618b3f552c7acd68db73e Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 16:07:07 +0530 Subject: [PATCH 093/156] Change method to property --- packages/element-web-module-api/src/api/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/element-web-module-api/src/api/client.ts index 1b9bed97dd..b7e70abaf9 100644 --- a/packages/element-web-module-api/src/api/client.ts +++ b/packages/element-web-module-api/src/api/client.ts @@ -34,7 +34,7 @@ export interface ClientApi { /** * Use this to modify account data on the homeserver. */ - getAccountDataApi: () => AccountDataApi; + accountData: AccountDataApi; /** * Fetch room by id from SDK. From 17f1a54a1facaf6e751d80374374cddda395789b Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 16:11:50 +0530 Subject: [PATCH 094/156] Fix comment --- packages/element-web-module-api/src/api/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/element-web-module-api/src/api/client.ts index b7e70abaf9..e670f38432 100644 --- a/packages/element-web-module-api/src/api/client.ts +++ b/packages/element-web-module-api/src/api/client.ts @@ -17,11 +17,11 @@ export interface AccountDataApi { */ get(eventType: string): unknown; /** - * Sett account data on the homeserver. + * Set account data on the homeserver. */ set(eventType: string, content: unknown): Promise; /** - * Delete account data from homeserver. + * Changes the content of this event to be empty. */ delete(eventType: string): Promise; } From cd9a21ac9391e2c45cdf2b2a7de2392c09aac803 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 23:38:31 +0530 Subject: [PATCH 095/156] Add onFirstWatch and onLastWatch to watchable So that we can create custom watchable objects that can add/remove event listeners as necessary. --- .../src/api/watchable.test.ts | 43 ++++++++++++++++++- .../src/api/watchable.ts | 26 ++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/src/api/watchable.test.ts b/packages/element-web-module-api/src/api/watchable.test.ts index c045286e2c..e55695c29c 100644 --- a/packages/element-web-module-api/src/api/watchable.test.ts +++ b/packages/element-web-module-api/src/api/watchable.test.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 { expect, test, vitest } from "vitest"; +import { expect, test, vi, vitest } from "vitest"; import { Watchable } from "./watchable"; @@ -56,3 +56,44 @@ test("when value is an object, shallow comparison works", () => { watchable.unwatch(listener); // Clean up after the test }); + +test("onFirstWatch and onLastWatch are called when appropriate", () => { + const onFirstWatch = vi.fn(); + const onLastWatch = vi.fn(); + class CustomWatchable extends Watchable { + protected onFirstWatch(): void { + onFirstWatch(); + } + protected onLastWatch(): void { + onLastWatch(); + } + } + + const watchable = new CustomWatchable(10); + // No listeners yet, so expect no calls + expect(onFirstWatch).not.toHaveBeenCalled(); + expect(onLastWatch).not.toHaveBeenCalled(); + + // Let's say that we have three listeners + const listeners = [vi.fn(), vi.fn(), vi.fn()]; + + // Let's add all of them via watch + for (const listener of listeners) { + watchable.watch(listener); + } + + // Only expect onFirstWatch() to have been called once + expect(onFirstWatch).toHaveBeenCalledOnce(); + + // Let's remove all the listeners + for (const listener of listeners) { + watchable.unwatch(listener); + } + + // Only expect onLastWatch to have been called once + expect(onLastWatch).toHaveBeenCalledOnce(); + + // Should call onFirstWatch again once we have more listeners + watchable.watch(vi.fn()); + expect(onFirstWatch).toHaveBeenCalledTimes(2); +}); diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/element-web-module-api/src/api/watchable.ts index 3fda595d2c..69296571c8 100644 --- a/packages/element-web-module-api/src/api/watchable.ts +++ b/packages/element-web-module-api/src/api/watchable.ts @@ -30,6 +30,10 @@ export class Watchable { public constructor(private currentValue: T) {} + /** + * The value stored in this watchable. + * Warning: Could potentially return stale data if you haven't called {@link Watchable#watch}. + */ public get value(): T { return this.currentValue; } @@ -50,12 +54,32 @@ export class Watchable { } public watch(listener: (value: T) => void): void { + // Call onFirstWatch if there was no listener before. + if (this.listeners.size === 0) { + this.onFirstWatch(); + } this.listeners.add(listener); } public unwatch(listener: (value: T) => void): void { - this.listeners.delete(listener); + const hasDeleted = this.listeners.delete(listener); + // Call onLastWatch if every listener has been removed. + if (hasDeleted && this.listeners.size === 0) { + this.onLastWatch(); + } } + + /** + * This is called when the number of listeners go from zero to one. + * Could be used to add external event listeners. + */ + protected onFirstWatch(): void {} + + /** + * This is called when the number of listeners go from one to zero. + * Could be used to remove external event listeners. + */ + protected onLastWatch(): void {} } /** From 04379cc0897534a3c49b1953ec2597dd2c33c560 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 23:41:08 +0530 Subject: [PATCH 096/156] Return watchable in account data api --- packages/element-web-module-api/src/api/client.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/element-web-module-api/src/api/client.ts index e670f38432..45f8e77e30 100644 --- a/packages/element-web-module-api/src/api/client.ts +++ b/packages/element-web-module-api/src/api/client.ts @@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { Room } from "../models/Room"; +import { Watchable } from "./watchable"; /** * Modify account data stored on the homeserver. @@ -13,9 +14,9 @@ import type { Room } from "../models/Room"; */ export interface AccountDataApi { /** - * Fetch account data stored from homeserver. + * Returns a watchable with account data for this event type. */ - get(eventType: string): unknown; + get(eventType: string): Watchable; /** * Set account data on the homeserver. */ From 6b4f33bee1760d06463049830ba52813c8ecf573 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 23:45:36 +0530 Subject: [PATCH 097/156] Update API doc --- .../element-web-module-api/element-web-module-api.api.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 042f83601b..4fcbd748a9 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -27,7 +27,7 @@ export interface AccountAuthInfo { // @public export interface AccountDataApi { delete(eventType: string): Promise; - get(eventType: string): unknown; + get(eventType: string): Watchable; set(eventType: string, content: unknown): Promise; } @@ -74,7 +74,7 @@ export interface ChatExportCustomisations { // @public export interface ClientApi { - getAccountDataApi: () => AccountDataApi; + accountData: AccountDataApi; getRoom: (id: string) => Room | null; } @@ -384,9 +384,10 @@ export class Watchable { // // (undocumented) protected readonly listeners: Set>; + protected onFirstWatch(): void; + protected onLastWatch(): void; // (undocumented) unwatch(listener: (value: T) => void): void; - // (undocumented) get value(): T; set value(value: T); // (undocumented) From 200d52a1621ecdc5790cd80d4a5d94762a2ed32c Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:32:01 +0530 Subject: [PATCH 098/156] Add Stores API --- .../element-web-module-api/src/api/index.ts | 6 ++++ .../element-web-module-api/src/api/stores.ts | 35 +++++++++++++++++++ packages/element-web-module-api/src/index.ts | 1 + 3 files changed, 42 insertions(+) create mode 100644 packages/element-web-module-api/src/api/stores.ts diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index f95479012c..f99a8bbbe8 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -17,6 +17,7 @@ import { AccountAuthApiExtension } from "./auth.ts"; import { ProfileApiExtension } from "./profile.ts"; import { ExtrasApi } from "./extras.ts"; import { BuiltinsApi } from "./builtins.ts"; +import { StoresApi } from "./stores.ts"; import { ClientApi } from "./client.ts"; /** @@ -124,6 +125,11 @@ export interface Api */ readonly extras: ExtrasApi; + /** + * Allows modules to access a limited functionality of certain stores from Element Web. + */ + readonly stores: StoresApi; + /** * Access some very specific functionality from the client. */ diff --git a/packages/element-web-module-api/src/api/stores.ts b/packages/element-web-module-api/src/api/stores.ts new file mode 100644 index 0000000000..469a52bdd2 --- /dev/null +++ b/packages/element-web-module-api/src/api/stores.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 type { Room } from "../models/Room"; + +/** + * Provides some basic functionality of the Room List Store from element-web. + * @public + */ +export interface RoomListStoreApi { + /** + * Get a flat list of sorted room from the RLS. + */ + getRooms(): Room[]; + + /** + * Returns a promise that resolves when RLS is ready. + */ + waitForReady(): Promise; +} + +/** + * Provides access to certain stores from element-web. + * @public + */ +export interface StoresApi { + /** + * Use this to access limited functionality of the RLS from element-web. + */ + getRoomListStore(): RoomListStoreApi; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 64f2002a27..52a0593bc5 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -20,5 +20,6 @@ export type * from "./api/dialog"; export type * from "./api/profile"; export type * from "./api/navigation"; export type * from "./api/builtins"; +export type * from "./api/stores"; export type * from "./api/client"; export * from "./api/watchable"; From 97e146247492b969139d75d894428c182bc49948 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 23 Oct 2025 12:32:48 +0530 Subject: [PATCH 099/156] Updat api doc --- .../element-web-module-api.api.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 4fcbd748a9..48b781fa2d 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -54,6 +54,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx readonly i18n: I18nApi; readonly navigation: NavigationApi; readonly rootNode: HTMLElement; + readonly stores: StoresApi; } // @alpha @@ -337,6 +338,12 @@ export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; } +// @public +export interface RoomListStoreApi { + getRooms(): Room[]; + waitForReady(): Promise; +} + // @alpha export interface RoomViewProps { roomId?: string; @@ -355,6 +362,11 @@ export interface SpacePanelItemProps { tooltip?: string; } +// @public +export interface StoresApi { + getRoomListStore(): RoomListStoreApi; +} + // @public export type Translations = Record Date: Fri, 24 Oct 2025 00:28:54 +0530 Subject: [PATCH 100/156] Convert to property --- packages/element-web-module-api/src/api/stores.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/stores.ts b/packages/element-web-module-api/src/api/stores.ts index 469a52bdd2..e27a72266d 100644 --- a/packages/element-web-module-api/src/api/stores.ts +++ b/packages/element-web-module-api/src/api/stores.ts @@ -31,5 +31,5 @@ export interface StoresApi { /** * Use this to access limited functionality of the RLS from element-web. */ - getRoomListStore(): RoomListStoreApi; + roomListStore: RoomListStoreApi; } From 4ee9b05608aa632ddeaf165d31d1bf0c0fb45f1a Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Fri, 24 Oct 2025 00:29:18 +0530 Subject: [PATCH 101/156] Return a watchable --- packages/element-web-module-api/src/api/stores.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/src/api/stores.ts b/packages/element-web-module-api/src/api/stores.ts index e27a72266d..057caec87c 100644 --- a/packages/element-web-module-api/src/api/stores.ts +++ b/packages/element-web-module-api/src/api/stores.ts @@ -6,6 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { Room } from "../models/Room"; +import { Watchable } from "./watchable"; /** * Provides some basic functionality of the Room List Store from element-web. @@ -13,9 +14,9 @@ import type { Room } from "../models/Room"; */ export interface RoomListStoreApi { /** - * Get a flat list of sorted room from the RLS. + * Returns a watchable holding a flat list of sorted room. */ - getRooms(): Room[]; + getRooms(): Watchable; /** * Returns a promise that resolves when RLS is ready. From 4381032d63a5fdc5c81aa308a2f4d121acd2d8c5 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Fri, 24 Oct 2025 00:30:01 +0530 Subject: [PATCH 102/156] Update API doc --- packages/element-web-module-api/element-web-module-api.api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 48b781fa2d..0b836bd551 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -340,7 +340,7 @@ export interface RoomListCustomisations { // @public export interface RoomListStoreApi { - getRooms(): Room[]; + getRooms(): Watchable; waitForReady(): Promise; } @@ -364,7 +364,7 @@ export interface SpacePanelItemProps { // @public export interface StoresApi { - getRoomListStore(): RoomListStoreApi; + roomListStore: RoomListStoreApi; } // @public From 10d73fa0eab5897ceb4250f0bd3348a434128a7a 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 103/156] 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] --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 7f4c20eef8..a477688be8 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -42,7 +42,7 @@ "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", - "vite": "^7.0.0", + "vite": "^7.1.11", "vite-plugin-dts": "^4.5.0", "vitest": "^3.2.4", "vitest-sonar-reporter": "^2.0.0" From 74acfb84aa45687681bb16d241e29f497d4e1ba6 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 28 Oct 2025 16:36:01 +0530 Subject: [PATCH 104/156] Add openRoom to Navigation --- .../src/api/navigation.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts index f7d60b4263..1d8345c577 100644 --- a/packages/element-web-module-api/src/api/navigation.ts +++ b/packages/element-web-module-api/src/api/navigation.ts @@ -14,6 +14,22 @@ import { JSX } from "react"; */ export type LocationRenderFunction = () => JSX.Element; +/** + * The options available for changing the open behaviour. + * @public + */ +export interface OpenRoomOptions { + /** + * The list of servers to join via. + */ + viaServers?: string[]; + + /** + * Whether to automatically join the room if we are not already in it. + */ + autoJoin?: boolean; +} + /** * API methods to navigate the application. * @public @@ -33,4 +49,11 @@ export interface NavigationApi { * @alpha */ registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; + + /** + * Open a room in element-web. + * @param roomIdOrAlias - id/alias of the room to open + * @param opts - Options to control the open action, see {@link OpenRoomOptions} + */ + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; } From 879c5c47f6479397ce8428d037e1532f76a3ceb0 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 28 Oct 2025 16:36:39 +0530 Subject: [PATCH 105/156] Update doc --- .../element-web-module-api/element-web-module-api.api.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 2d341fde03..258573dab3 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -289,11 +289,18 @@ export class ModuleLoader { // @public export interface NavigationApi { + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; // @alpha registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; toMatrixToLink(link: string, join?: boolean): Promise; } +// @public +export interface OpenRoomOptions { + autoJoin?: boolean; + viaServers?: string[]; +} + // @alpha export type OriginalMessageComponentProps = { showUrlPreview?: boolean; From 81212a3c16515930f1d6486c92d13a2674189942 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 28 Oct 2025 16:06:28 +0100 Subject: [PATCH 106/156] feat(builtins): add `hideHeader` and `hideComposer` props to `RoomViewProps` and `renderRoomView` --- packages/element-web-module-api/src/api/builtins.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index cf43795576..aabba34e4e 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -14,6 +14,14 @@ export interface RoomViewProps { * The ID of the room to render. */ roomId?: string; + /* + * If true, the room header will be hidden. + */ + hideHeader?: boolean; + /* + * If true, the message composer will be hidden. + */ + hideComposer?: boolean; } /** @@ -38,6 +46,7 @@ export interface BuiltinsApi { * * @alpha * @param roomId - Id of the room + * @param props - Additional props to pass to the room view */ - renderRoomView(roomId: string): React.ReactNode; + renderRoomView(roomId: string, props?: Omit): React.ReactNode; } From ca6443e4a2e91485f5bd1d8298ef9d7a9beb39c2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 28 Oct 2025 16:18:10 +0100 Subject: [PATCH 107/156] chore: update api.md --- .../element-web-module-api/element-web-module-api.api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index a6cb726991..dd46184224 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -60,7 +60,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha export interface BuiltinsApi { renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string): React.ReactNode; + renderRoomView(roomId: string, props?: Omit): React.ReactNode; } // @alpha @deprecated (undocumented) @@ -347,6 +347,10 @@ export interface RoomListStoreApi { // @alpha export interface RoomViewProps { + // (undocumented) + hideComposer?: boolean; + // (undocumented) + hideHeader?: boolean; roomId?: string; } From afa2984645ceb4576e8e6b49a7eace8d3b284ae5 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 29 Oct 2025 10:36:55 +0100 Subject: [PATCH 108/156] refactor(builtins): remove `roomId` from `RoomViewProps` --- .../element-web-module-api/element-web-module-api.api.md | 3 +-- packages/element-web-module-api/src/api/builtins.ts | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index dd46184224..db5b2ca235 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -60,7 +60,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha export interface BuiltinsApi { renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string, props?: Omit): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; } // @alpha @deprecated (undocumented) @@ -351,7 +351,6 @@ export interface RoomViewProps { hideComposer?: boolean; // (undocumented) hideHeader?: boolean; - roomId?: string; } // @alpha @deprecated (undocumented) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index aabba34e4e..bfdb293a38 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -10,10 +10,6 @@ Please see LICENSE files in the repository root for full details. * @alpha Subject to change. */ export interface RoomViewProps { - /** - * The ID of the room to render. - */ - roomId?: string; /* * If true, the room header will be hidden. */ @@ -48,5 +44,5 @@ export interface BuiltinsApi { * @param roomId - Id of the room * @param props - Additional props to pass to the room view */ - renderRoomView(roomId: string, props?: Omit): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; } From b7385d8936e9d281e3d185ba3d201b9a8915255d Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Wed, 29 Oct 2025 20:21:27 +0530 Subject: [PATCH 109/156] Update package.json --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 7f4c20eef8..6c366f8cf6 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.4.1", + "version": "1.5.1", "description": "Module API surface for element-web", "repository": { "type": "git", From a1cd21244f92b8e247498596632c1b07d86c7763 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Wed, 29 Oct 2025 20:35:53 +0530 Subject: [PATCH 110/156] Reset patch version as well --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 6c366f8cf6..2bd8ea5756 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.5.1", + "version": "1.5.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 4125ebea79ef4e82ae60f9ca7686a33324a982ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:46:24 +0000 Subject: [PATCH 111/156] Update dependency vitest-sonar-reporter to v3 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index e402d6c267..b5306b85d9 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -45,7 +45,7 @@ "vite": "^7.1.11", "vite-plugin-dts": "^4.5.0", "vitest": "^3.2.4", - "vitest-sonar-reporter": "^2.0.0" + "vitest-sonar-reporter": "^3.0.0" }, "peerDependencies": { "@matrix-org/react-sdk-module-api": "*", From e197f0ec4bdf746348d761aa1ab886489a931a66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:00:07 +0000 Subject: [PATCH 112/156] Update vitest monorepo to v4 (#128) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/element-web-module-api/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index e402d6c267..00f432d47e 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -37,14 +37,14 @@ "@types/react": "^19", "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", - "@vitest/coverage-v8": "^3.0.4", + "@vitest/coverage-v8": "^4.0.0", "matrix-web-i18n": "^3.3.0", "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", "vite": "^7.1.11", "vite-plugin-dts": "^4.5.0", - "vitest": "^3.2.4", + "vitest": "^4.0.0", "vitest-sonar-reporter": "^2.0.0" }, "peerDependencies": { From 6a4c529083de939813ce1e7dc487778ccbc51ce4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Nov 2025 12:16:31 +0000 Subject: [PATCH 113/156] Delint --- .../element-web-module-api/src/api/client.ts | 2 +- .../element-web-module-api/src/api/dialog.ts | 2 +- .../element-web-module-api/src/api/extras.ts | 2 +- .../src/api/index.test.ts | 2 +- .../element-web-module-api/src/api/index.ts | 26 +++++++++---------- .../src/api/navigation.ts | 2 +- .../element-web-module-api/src/api/profile.ts | 2 +- .../element-web-module-api/src/api/stores.ts | 2 +- .../element-web-module-api/src/loader.test.ts | 2 +- packages/element-web-module-api/src/loader.ts | 2 +- .../element-web-module-api/src/models/Room.ts | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/element-web-module-api/src/api/client.ts index 45f8e77e30..64e2274e89 100644 --- a/packages/element-web-module-api/src/api/client.ts +++ b/packages/element-web-module-api/src/api/client.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { Room } from "../models/Room"; -import { Watchable } from "./watchable"; +import { type Watchable } from "./watchable"; /** * Modify account data stored on the homeserver. diff --git a/packages/element-web-module-api/src/api/dialog.ts b/packages/element-web-module-api/src/api/dialog.ts index 2361d6dace..cd9a2c867c 100644 --- a/packages/element-web-module-api/src/api/dialog.ts +++ b/packages/element-web-module-api/src/api/dialog.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 { ComponentType } from "react"; +import { type ComponentType } from "react"; /** * Options for {@link Api#openDialog}. diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index 1c8d355b26..135c1f9b56 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.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 { JSX } from "react"; +import { type JSX } from "react"; /** * Properties of an item added to the Space panel diff --git a/packages/element-web-module-api/src/api/index.test.ts b/packages/element-web-module-api/src/api/index.test.ts index 25f34768a8..8cd7a879fb 100644 --- a/packages/element-web-module-api/src/api/index.test.ts +++ b/packages/element-web-module-api/src/api/index.test.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { expect, test } from "vitest"; -import { Api, isModule } from "."; +import { type Api, isModule } from "."; const TestModule = { default: class TestModule { diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index f99a8bbbe8..9f1f59db0c 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -6,19 +6,19 @@ Please see LICENSE files in the repository root for full details. */ import type { Root } from "react-dom/client"; -import { LegacyModuleApiExtension } from "./legacy-modules"; -import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; -import { ConfigApi } from "./config"; -import { I18nApi } from "./i18n"; -import { CustomComponentsApi } from "./custom-components"; -import { NavigationApi } from "./navigation.ts"; -import { DialogApiExtension } from "./dialog.ts"; -import { AccountAuthApiExtension } from "./auth.ts"; -import { ProfileApiExtension } from "./profile.ts"; -import { ExtrasApi } from "./extras.ts"; -import { BuiltinsApi } from "./builtins.ts"; -import { StoresApi } from "./stores.ts"; -import { ClientApi } from "./client.ts"; +import { type LegacyModuleApiExtension } from "./legacy-modules"; +import { type LegacyCustomisationsApiExtension } from "./legacy-customisations"; +import { type ConfigApi } from "./config"; +import { type I18nApi } from "./i18n"; +import { type CustomComponentsApi } from "./custom-components"; +import { type NavigationApi } from "./navigation.ts"; +import { type DialogApiExtension } from "./dialog.ts"; +import { type AccountAuthApiExtension } from "./auth.ts"; +import { type ProfileApiExtension } from "./profile.ts"; +import { type ExtrasApi } from "./extras.ts"; +import { type BuiltinsApi } from "./builtins.ts"; +import { type StoresApi } from "./stores.ts"; +import { type ClientApi } from "./client.ts"; /** * Module interface for modules to implement. diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/element-web-module-api/src/api/navigation.ts index 1d8345c577..76ff59f1c3 100644 --- a/packages/element-web-module-api/src/api/navigation.ts +++ b/packages/element-web-module-api/src/api/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 { JSX } from "react"; +import { type JSX } from "react"; /** * A function called to render a component when a user navigates to the corresponding diff --git a/packages/element-web-module-api/src/api/profile.ts b/packages/element-web-module-api/src/api/profile.ts index 5a09537587..6be444f05f 100644 --- a/packages/element-web-module-api/src/api/profile.ts +++ b/packages/element-web-module-api/src/api/profile.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 { Watchable } from "./watchable.ts"; +import { type Watchable } from "./watchable.ts"; /** * The profile of the user currently logged in. diff --git a/packages/element-web-module-api/src/api/stores.ts b/packages/element-web-module-api/src/api/stores.ts index 057caec87c..9e5bfa252d 100644 --- a/packages/element-web-module-api/src/api/stores.ts +++ b/packages/element-web-module-api/src/api/stores.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import type { Room } from "../models/Room"; -import { Watchable } from "./watchable"; +import { type Watchable } from "./watchable"; /** * Provides some basic functionality of the Room List Store from element-web. diff --git a/packages/element-web-module-api/src/loader.test.ts b/packages/element-web-module-api/src/loader.test.ts index 853b6d3eb6..8010c72af4 100644 --- a/packages/element-web-module-api/src/loader.test.ts +++ b/packages/element-web-module-api/src/loader.test.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { expect, test, describe, vi, beforeEach } from "vitest"; -import { Api, ModuleIncompatibleError, ModuleLoader } from "."; +import { type Api, ModuleIncompatibleError, ModuleLoader } from "."; describe("ModuleIncompatibleError", () => { test("should extend Error", () => { diff --git a/packages/element-web-module-api/src/loader.ts b/packages/element-web-module-api/src/loader.ts index 0cc2141ca2..c057109f28 100644 --- a/packages/element-web-module-api/src/loader.ts +++ b/packages/element-web-module-api/src/loader.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { satisfies } from "semver"; -import { Api, isModule, Module, ModuleExport } from "./api"; +import { type Api, isModule, type Module, type ModuleExport } from "./api"; /** * Error thrown when a module is incompatible with the engine version. diff --git a/packages/element-web-module-api/src/models/Room.ts b/packages/element-web-module-api/src/models/Room.ts index df2f1c8643..eb67d8054e 100644 --- a/packages/element-web-module-api/src/models/Room.ts +++ b/packages/element-web-module-api/src/models/Room.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 { Watchable } from "../api/watchable"; +import { type Watchable } from "../api/watchable"; /** * Represents a room from element-web. From a66fd6e9760281873e0d6eb8b927547c76604889 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 17 Nov 2025 15:06:23 +0100 Subject: [PATCH 114/156] feat: add option to add the right panel of a room view --- packages/element-web-module-api/src/api/builtins.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index bfdb293a38..84b7d62a7e 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -18,6 +18,10 @@ export interface RoomViewProps { * If true, the message composer will be hidden. */ hideComposer?: boolean; + /* + * If true, the right panel will be hidden. + */ + hideRightPanel?: boolean; } /** From 17066c03fd6ce23d19685261215121ac5c5fe8a1 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 17 Nov 2025 15:57:28 +0100 Subject: [PATCH 115/156] chore: update element-web-module-api.api.md --- packages/element-web-module-api/element-web-module-api.api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index af18dc2d4b..b31cee299d 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -358,6 +358,8 @@ export interface RoomViewProps { hideComposer?: boolean; // (undocumented) hideHeader?: boolean; + // (undocumented) + hideRightPanel?: boolean; } // @alpha @deprecated (undocumented) From 99ee1f95ad63281fd674f162ae2a1640cba5c669 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 116/156] Pin dependencies (#63) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/element-web-module-api/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 00d0e42fac..2caff17d2b 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,6 +1,6 @@ -ARG ELEMENT_VERSION=latest +ARG ELEMENT_VERSION=latest@sha256:e2099ff363200f1e972a651170f7151c416acdbe3c1aa7715cd6c11f80ef1268 -FROM --platform=$BUILDPLATFORM node:lts-alpine AS builder +FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:2867d550cf9d8bb50059a0fff528741f11a84d985c732e60e19e8e75c7239c43 AS builder ARG BUILD_CONTEXT From 7518d1c068140ba4117a3d6cbba495afb76909f0 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 21 Nov 2025 15:31:17 +0100 Subject: [PATCH 117/156] Add `ExtraApi#getVisibleRoomBySpaceKey` (#134) --- .../element-web-module-api.api.md | 1 + packages/element-web-module-api/src/api/extras.ts | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index b31cee299d..3ebf946d32 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -167,6 +167,7 @@ export interface DirectoryCustomisations { // @alpha export interface ExtrasApi { + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; } diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index 135c1f9b56..5ffc9c4f16 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -56,4 +56,15 @@ export interface ExtrasApi { * @param props - Properties of the item to add. */ setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; + + /** + * Registers a callback to get the list of visible rooms for a given space. + * + * Element Web will call this callback when checking if a room is displayed for the given space. For example in case of message editing or replying. + * If the space added by the module displays a room view and doesn't provide this callback, Element Web won't be able to determine if a room is visible in that space and will redirect to display the room in its vanilla space/metaspace. + * + * @param spaceKey - The space key to get visible rooms for. + * @param cb - A callback that returns the list of visible room IDs. + */ + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; } From c570936d33ccdc3c3e8c1022fc083c31086dd45f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 21 Nov 2025 15:38:33 +0100 Subject: [PATCH 118/156] Merge pull request #135 from element-hq/florianduros/rooms-hide-pinned-message-banner --- .../element-web-module-api.api.md | 4 +--- packages/element-web-module-api/src/api/builtins.ts | 10 +++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 3ebf946d32..bd4ccfc45e 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -355,11 +355,9 @@ export interface RoomListStoreApi { // @alpha export interface RoomViewProps { - // (undocumented) hideComposer?: boolean; - // (undocumented) hideHeader?: boolean; - // (undocumented) + hidePinnedMessageBanner?: boolean; hideRightPanel?: boolean; } diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index 84b7d62a7e..d5d66d1c4f 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -10,18 +10,22 @@ Please see LICENSE files in the repository root for full details. * @alpha Subject to change. */ export interface RoomViewProps { - /* + /** * If true, the room header will be hidden. */ hideHeader?: boolean; - /* + /** * If true, the message composer will be hidden. */ hideComposer?: boolean; - /* + /** * If true, the right panel will be hidden. */ hideRightPanel?: boolean; + /** + * If true, the pinned message banner will be hidden. + */ + hidePinnedMessageBanner?: boolean; } /** From ef180bf89d886e455793b5613f56630d72626ed6 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 21 Nov 2025 16:30:14 +0100 Subject: [PATCH 119/156] chore(EW module): bump to v1.6.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 6e5ba9ce1c..b503dab243 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.5.0", + "version": "1.6.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 56662c53e98b08434e64b3194e9d1e013e98131a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 26 Nov 2025 16:47:58 +0000 Subject: [PATCH 120/156] Add humanizeTime to the module i18n API So modules can access it simply with the right context etc --- .../element-web-module-api/element-web-module-api.api.md | 1 + packages/element-web-module-api/src/api/i18n.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index b31cee299d..45a41ddb97 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -172,6 +172,7 @@ export interface ExtrasApi { // @public export interface I18nApi { + humanizeTime(timeMillis: number): string; get language(): string; register(translations: Partial): void; translate(key: keyof Translations, variables?: Variables): string; diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts index a93c247d4b..af44b698a9 100644 --- a/packages/element-web-module-api/src/api/i18n.ts +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -49,4 +49,10 @@ export interface I18nApi { * @param variables - Optional variables to interpolate into the translation */ translate(key: keyof Translations, variables?: Variables): string; + + /** + * Convert a timestamp into a human-readable time string + * @param timeMillis - The time in milliseconds since epoch + */ + humanizeTime(timeMillis: number): string; } From 65edff067a6472dd79106677fa4581b5f2c0c55d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 28 Nov 2025 15:20:51 +0000 Subject: [PATCH 121/156] Better doc --- packages/element-web-module-api/src/api/i18n.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts index af44b698a9..1e8ac2f64a 100644 --- a/packages/element-web-module-api/src/api/i18n.ts +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -51,7 +51,8 @@ export interface I18nApi { translate(key: keyof Translations, variables?: Variables): string; /** - * Convert a timestamp into a human-readable time string + * Convert a timestamp into a translated, human-readable time, + * using the current system time as a reference, eg. "5 minutes ago". * @param timeMillis - The time in milliseconds since epoch */ humanizeTime(timeMillis: number): string; From 3d0ad97667298cc1d367cac6e57c525de4668b6b Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 1 Dec 2025 09:22:18 +0000 Subject: [PATCH 122/156] module API 1.7.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index b503dab243..d561b2d47b 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.6.0", + "version": "1.7.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 8f40ea5c0a520e3563c4e6812764a12e7c2ea377 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 2 Dec 2025 13:56:53 +0530 Subject: [PATCH 123/156] Add API to render notification decoration --- packages/element-web-module-api/src/api/builtins.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index d5d66d1c4f..f88e852936 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -53,4 +53,12 @@ export interface BuiltinsApi { * @param props - Additional props to pass to the room view */ renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; + + /** + * Render notification decoration component from element-web. + * + * @alpha + * @param roomId - Id of the room + */ + renderNotificationDecoration(roomId: string): React.ReactNode; } From 64a63bdbb039a12ab6bf3e26a10cfdfc1c876862 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 2 Dec 2025 13:59:21 +0530 Subject: [PATCH 124/156] Update docs --- packages/element-web-module-api/element-web-module-api.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index bd4ccfc45e..8b7ea3fd93 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -59,6 +59,7 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha export interface BuiltinsApi { + renderNotificationDecoration(roomId: string): React.ReactNode; renderRoomAvatar(roomId: string, size?: string): React.ReactNode; renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; } From 2d3b55464997b8d1f0e582820e3c8a726a6fe5aa Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Tue, 2 Dec 2025 14:43:48 +0530 Subject: [PATCH 125/156] Increment package version --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index d561b2d47b..4472bd8ee1 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.7.0", + "version": "1.8.0", "description": "Module API surface for element-web", "repository": { "type": "git", From f35357d3b3aa6c58c179b939770df26484e765b1 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 3 Dec 2025 09:54:33 +0000 Subject: [PATCH 126/156] Add api to hide widgets --- packages/element-web-module-api/element-web-module-api.api.md | 1 + packages/element-web-module-api/src/api/builtins.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 47bf5e2c87..6a6f6db490 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -361,6 +361,7 @@ export interface RoomViewProps { hideHeader?: boolean; hidePinnedMessageBanner?: boolean; hideRightPanel?: boolean; + hideWidgets?: boolean; } // @alpha @deprecated (undocumented) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index f88e852936..f257de7c20 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -26,6 +26,10 @@ export interface RoomViewProps { * If true, the pinned message banner will be hidden. */ hidePinnedMessageBanner?: boolean; + /** + * If true, the widgets will be hidden. + */ + hideWidgets?: boolean; } /** From 94aba63a1d904fe01e19c6eda7d2363d0f09135f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 27 Nov 2025 18:04:26 +0100 Subject: [PATCH 127/156] feat(ew-api): add `builtins#RoomViewProps#enableReadReceiptsAndMarkersOnActivity` For the multiroom module, we display several room views at the same time. In order to avoid all the rooms to send read receipts and markers automatically when we are interacting with the UI, we add `enableReadReceiptsAndMarkersOnActivity` props. When at false, the timeline doesn't listen to user activity to send these receipts. Only when the room is focused, marker and read receipts are updated. --- packages/element-web-module-api/src/api/builtins.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/element-web-module-api/src/api/builtins.ts index f257de7c20..d4c461d098 100644 --- a/packages/element-web-module-api/src/api/builtins.ts +++ b/packages/element-web-module-api/src/api/builtins.ts @@ -30,6 +30,12 @@ export interface RoomViewProps { * If true, the widgets will be hidden. */ hideWidgets?: boolean; + /** + * If true, enable sending read receipts and markers on user activity in the room view. When the user interacts with the room view, read receipts and markers are sent. + * If false, the read receipts and markers are only send when the room view is focused. The user has to focus the room view in order to clear any unreads and to move the unread marker to the bottom of the view. + * @defaultValue true + */ + enableReadReceiptsAndMarkersOnActivity?: boolean; } /** From 0126cded9eaa72c6b81868fc01a71e35e5ce4db5 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 4 Dec 2025 12:22:45 +0100 Subject: [PATCH 128/156] doc: update api md doc --- packages/element-web-module-api/element-web-module-api.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 6a6f6db490..d7590281ea 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -357,6 +357,7 @@ export interface RoomListStoreApi { // @alpha export interface RoomViewProps { + enableReadReceiptsAndMarkersOnActivity?: boolean; hideComposer?: boolean; hideHeader?: boolean; hidePinnedMessageBanner?: boolean; From 7b4894d444e02e4f242534bab559a8dbd9410984 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 5 Dec 2025 09:45:48 +0100 Subject: [PATCH 129/156] build(ew-api): update to 1.9.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 4472bd8ee1..7357a22773 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.8.0", + "version": "1.9.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 9ea40d14a516f84d5f003c70308d2d7137faf433 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:31:13 +0000 Subject: [PATCH 130/156] Update Node.js to 682368d --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 2caff17d2b..1025d84203 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,6 +1,6 @@ ARG ELEMENT_VERSION=latest@sha256:e2099ff363200f1e972a651170f7151c416acdbe3c1aa7715cd6c11f80ef1268 -FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:2867d550cf9d8bb50059a0fff528741f11a84d985c732e60e19e8e75c7239c43 AS builder +FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:682368d8253e0c3364b803956085c456a612d738bd635926d73fa24db3ce53d7 AS builder ARG BUILD_CONTEXT From 9b570e5cea5494a9114fe5e1d071fc4a3b598e48 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:06:25 +0000 Subject: [PATCH 131/156] Update ghcr.io/element-hq/element-web:latest Docker digest to a84f294 --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index 1025d84203..d732340278 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,4 +1,4 @@ -ARG ELEMENT_VERSION=latest@sha256:e2099ff363200f1e972a651170f7151c416acdbe3c1aa7715cd6c11f80ef1268 +ARG ELEMENT_VERSION=latest@sha256:a84f294ce46e4327ebacecb78bfc94cf6a45c7ffa5104a28f06b5ac69d0b2548 FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:682368d8253e0c3364b803956085c456a612d738bd635926d73fa24db3ce53d7 AS builder From 174c35b874e9fc026d2ce482f5425f6e9f9fbd43 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 11 Dec 2025 11:26:13 +0000 Subject: [PATCH 132/156] Iterate --- packages/element-web-module-api/src/api/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 9f1f59db0c..1401e6fc6d 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -78,7 +78,8 @@ export function isModule(module: unknown): module is ModuleExport { * @public */ export interface Api - extends LegacyModuleApiExtension, + extends + LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, From 5bb1d3d461e1fec44b7ebf9beeaf2019da79899e Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 18 Feb 2026 13:17:53 +0000 Subject: [PATCH 133/156] Add widget permissions module api --- .../element-web-module-api/src/api/index.ts | 7 ++ .../src/api/widget-lifecycle.ts | 82 +++++++++++++++++++ packages/element-web-module-api/src/index.ts | 1 + 3 files changed, 90 insertions(+) create mode 100644 packages/element-web-module-api/src/api/widget-lifecycle.ts diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 1401e6fc6d..9c5e59ed57 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -19,6 +19,7 @@ import { type ExtrasApi } from "./extras.ts"; import { type BuiltinsApi } from "./builtins.ts"; import { type StoresApi } from "./stores.ts"; import { type ClientApi } from "./client.ts"; +import { type WidgetLifecycleApi } from "./widget-lifecycle.ts"; /** * Module interface for modules to implement. @@ -136,6 +137,12 @@ export interface Api */ readonly client: ClientApi; + /** + * API for modules to auto-approve widget preloading, identity token requests, and capability requests. + * @alpha Subject to change. + */ + readonly widgetLifecycle: WidgetLifecycleApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/api/widget-lifecycle.ts b/packages/element-web-module-api/src/api/widget-lifecycle.ts new file mode 100644 index 0000000000..dd8125e397 --- /dev/null +++ b/packages/element-web-module-api/src/api/widget-lifecycle.ts @@ -0,0 +1,82 @@ +/* +Copyright 2026 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. +*/ + +/** + * The scope of a widget: room-scoped, user-scoped (account), or an ephemeral modal overlay. + * @alpha Subject to change. + */ +export type WidgetKind = "room" | "account" | "modal"; + +/** + * A description of a widget passed to approver callbacks. + * Contains the information needed to make approval decisions. + * @alpha Subject to change. + */ +export type WidgetDescriptor = { + /** The unique identifier of the widget. */ + id: string; + /** The template URL of the widget, which may contain `$matrix_*` placeholder variables. */ + templateUrl: string; + /** The Matrix user ID of the user who created the widget. */ + creatorUserId: string; + /** The widget type, e.g. `m.custom`, `m.jitsi`, `m.stickerpicker`. */ + type: string; + /** The origin of the widget URL. */ + origin: string; + /** The room ID the widget belongs to, if it is a room widget. */ + roomId?: string; + /** The scope of the widget. */ + kind: WidgetKind; +}; + +/** + * Callback that decides whether a widget should be auto-approved for preloading + * (i.e. loaded without the user clicking "Continue"). + * Return `true` to auto-approve, or any other value to defer to the default consent flow. + * @alpha Subject to change. + */ +export type PreloadApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +/** + * Callback that decides whether a widget should be auto-approved to receive + * the user's OpenID identity token. + * Return `true` to auto-approve, or any other value to defer to the default consent flow. + * @alpha Subject to change. + */ +export type IdentityApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +/** + * Callback that decides which of a widget's requested capabilities should be auto-approved. + * Return a `Set` of approved capability strings, or `undefined` to defer to the default consent flow. + * @alpha Subject to change. + */ +export type CapabilitiesApprover = ( + widget: WidgetDescriptor, + requestedCapabilities: Set, +) => Set | Promise | undefined> | undefined; + +/** + * API for modules to auto-approve widget preloading, identity token requests, and capability requests. + * @alpha Subject to change. + */ +export interface WidgetLifecycleApi { + /** + * Register a handler that can auto-approve widget preloading. + * Returning true auto-approves; any other value results in no auto-approval. + */ + registerPreloadApprover(approver: PreloadApprover): void; + + /** + * Register a handler that can auto-approve identity token requests. + * Returning true auto-approves; any other value results in no auto-approval. + */ + registerIdentityApprover(approver: IdentityApprover): void; + + /** + * Register a handler that can auto-approve widget capabilities. + * Return a set containing the capabilities to approve. + */ + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 52a0593bc5..da9c3669d9 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -22,4 +22,5 @@ export type * from "./api/navigation"; export type * from "./api/builtins"; export type * from "./api/stores"; export type * from "./api/client"; +export type * from "./api/widget-lifecycle"; export * from "./api/watchable"; From a5ec21bd063c06e90c41ab5f694885983c6937aa Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 18 Feb 2026 13:18:03 +0000 Subject: [PATCH 134/156] Update element-web-module-api.api.md --- .../element-web-module-api.api.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index d7590281ea..b75e57e9c9 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -55,6 +55,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx readonly navigation: NavigationApi; readonly rootNode: HTMLElement; readonly stores: StoresApi; + // @alpha + readonly widgetLifecycle: WidgetLifecycleApi; } // @alpha @@ -64,6 +66,9 @@ export interface BuiltinsApi { renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; } +// @alpha +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => Set | Promise | undefined> | undefined; + // @alpha @deprecated (undocumented) export interface ChatExportCustomisations { getForceChatExportParameters(): { @@ -180,6 +185,9 @@ export interface I18nApi { translate(key: keyof Translations, variables?: Variables): string; } +// @alpha +export type IdentityApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; + // @alpha @deprecated (undocumented) export type LegacyCustomisations = (customisations: T) => void; @@ -325,6 +333,9 @@ export type OriginalMessageComponentProps = { showUrlPreview?: boolean; }; +// @alpha +export type PreloadApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; + // @public export interface Profile { displayName?: string; @@ -422,6 +433,27 @@ export class Watchable { watch(listener: (value: T) => void): void; } +// @alpha +export type WidgetDescriptor = { + id: string; + templateUrl: string; + creatorUserId: string; + type: string; + origin: string; + roomId?: string; + kind: WidgetKind; +}; + +// @alpha +export type WidgetKind = "room" | "account" | "modal"; + +// @alpha +export interface WidgetLifecycleApi { + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; + registerIdentityApprover(approver: IdentityApprover): void; + registerPreloadApprover(approver: PreloadApprover): void; +} + // @alpha @deprecated (undocumented) export interface WidgetPermissionsCustomisations { preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; From 4614d2f395a328ec501499683baafed5c4c81beb Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 18 Feb 2026 18:43:26 +0000 Subject: [PATCH 135/156] Remove WidgetKind and fix header --- .../element-web-module-api.api.md | 4 ---- .../element-web-module-api/src/api/widget-lifecycle.ts | 10 +--------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index b75e57e9c9..bf95140227 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -441,12 +441,8 @@ export type WidgetDescriptor = { type: string; origin: string; roomId?: string; - kind: WidgetKind; }; -// @alpha -export type WidgetKind = "room" | "account" | "modal"; - // @alpha export interface WidgetLifecycleApi { registerCapabilitiesApprover(approver: CapabilitiesApprover): void; diff --git a/packages/element-web-module-api/src/api/widget-lifecycle.ts b/packages/element-web-module-api/src/api/widget-lifecycle.ts index dd8125e397..9638a1096b 100644 --- a/packages/element-web-module-api/src/api/widget-lifecycle.ts +++ b/packages/element-web-module-api/src/api/widget-lifecycle.ts @@ -1,16 +1,10 @@ /* -Copyright 2026 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. */ -/** - * The scope of a widget: room-scoped, user-scoped (account), or an ephemeral modal overlay. - * @alpha Subject to change. - */ -export type WidgetKind = "room" | "account" | "modal"; - /** * A description of a widget passed to approver callbacks. * Contains the information needed to make approval decisions. @@ -29,8 +23,6 @@ export type WidgetDescriptor = { origin: string; /** The room ID the widget belongs to, if it is a room widget. */ roomId?: string; - /** The scope of the widget. */ - kind: WidgetKind; }; /** From d51eb5dfd3e0a4f7add45527913fda06cf2565be Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Feb 2026 09:32:58 +0000 Subject: [PATCH 136/156] Specify ThisType on i18nApi --- packages/element-web-module-api/src/api/i18n.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts index 1e8ac2f64a..564390c7f0 100644 --- a/packages/element-web-module-api/src/api/i18n.ts +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -41,19 +41,19 @@ export interface I18nApi { /** * Register translations for the module, may override app's existing translations */ - register(translations: Partial): void; + register(this: void, translations: Partial): void; /** * Perform a translation, with optional variables * @param key - The key to translate * @param variables - Optional variables to interpolate into the translation */ - translate(key: keyof Translations, variables?: Variables): string; + translate(this: void, key: keyof Translations, variables?: Variables): string; /** * Convert a timestamp into a translated, human-readable time, * using the current system time as a reference, eg. "5 minutes ago". * @param timeMillis - The time in milliseconds since epoch */ - humanizeTime(timeMillis: number): string; + humanizeTime(this: void, timeMillis: number): string; } From 30c27af1703fe94ee881925a8f5b13da20f87258 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Feb 2026 09:33:28 +0000 Subject: [PATCH 137/156] Update API report for @element-hq/element-web-module-api --- .../element-web-module-api/element-web-module-api.api.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index d7590281ea..f0d04c4df6 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -174,10 +174,10 @@ export interface ExtrasApi { // @public export interface I18nApi { - humanizeTime(timeMillis: number): string; + humanizeTime(this: void, timeMillis: number): string; get language(): string; - register(translations: Partial): void; - translate(key: keyof Translations, variables?: Variables): string; + register(this: void, translations: Partial): void; + translate(this: void, key: keyof Translations, variables?: Variables): string; } // @alpha @deprecated (undocumented) @@ -445,3 +445,4 @@ export interface WidgetVariablesCustomisations { // (No @packageDocumentation comment for this package) ``` + From 5a3559a9bbfcb9b156fda04f437e9b42d0113a0b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Feb 2026 12:43:06 +0000 Subject: [PATCH 138/156] Bump version from 1.9.0 to 1.9.1 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 7357a22773..6e63a29d64 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.9.0", + "version": "1.9.1", "description": "Module API surface for element-web", "repository": { "type": "git", From 83d752cfd7ed9debd0c32932942eb916a32d138e Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 25 Feb 2026 09:33:48 +0000 Subject: [PATCH 139/156] Add MaybePromise --- .../element-web-module-api.api.md | 9 ++++++--- .../src/api/widget-lifecycle.ts | 8 +++++--- packages/element-web-module-api/src/index.ts | 1 + packages/element-web-module-api/src/utils.ts | 13 +++++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 packages/element-web-module-api/src/utils.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index bf95140227..100ed0e0fb 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -67,7 +67,7 @@ export interface BuiltinsApi { } // @alpha -export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => Set | Promise | undefined> | undefined; +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; // @alpha @deprecated (undocumented) export interface ChatExportCustomisations { @@ -186,7 +186,7 @@ export interface I18nApi { } // @alpha -export type IdentityApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; // @alpha @deprecated (undocumented) export type LegacyCustomisations = (customisations: T) => void; @@ -242,6 +242,9 @@ export interface MatrixEvent { unsigned: Record; } +// @public +export type MaybePromise = T | PromiseLike; + // @alpha @deprecated (undocumented) export interface Media { // (undocumented) @@ -334,7 +337,7 @@ export type OriginalMessageComponentProps = { }; // @alpha -export type PreloadApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; // @public export interface Profile { diff --git a/packages/element-web-module-api/src/api/widget-lifecycle.ts b/packages/element-web-module-api/src/api/widget-lifecycle.ts index 9638a1096b..6258629eab 100644 --- a/packages/element-web-module-api/src/api/widget-lifecycle.ts +++ b/packages/element-web-module-api/src/api/widget-lifecycle.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 type { MaybePromise } from "../utils"; + /** * A description of a widget passed to approver callbacks. * Contains the information needed to make approval decisions. @@ -31,14 +33,14 @@ export type WidgetDescriptor = { * Return `true` to auto-approve, or any other value to defer to the default consent flow. * @alpha Subject to change. */ -export type PreloadApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; /** * Callback that decides whether a widget should be auto-approved to receive * the user's OpenID identity token. * Return `true` to auto-approve, or any other value to defer to the default consent flow. * @alpha Subject to change. */ -export type IdentityApprover = (widget: WidgetDescriptor) => boolean | Promise | undefined; +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; /** * Callback that decides which of a widget's requested capabilities should be auto-approved. * Return a `Set` of approved capability strings, or `undefined` to defer to the default consent flow. @@ -47,7 +49,7 @@ export type IdentityApprover = (widget: WidgetDescriptor) => boolean | Promise, -) => Set | Promise | undefined> | undefined; +) => MaybePromise | undefined>; /** * API for modules to auto-approve widget preloading, identity token requests, and capability requests. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index da9c3669d9..89c8b24dbd 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -24,3 +24,4 @@ export type * from "./api/stores"; export type * from "./api/client"; export type * from "./api/widget-lifecycle"; export * from "./api/watchable"; +export type * from "./utils"; diff --git a/packages/element-web-module-api/src/utils.ts b/packages/element-web-module-api/src/utils.ts new file mode 100644 index 0000000000..8692b180c8 --- /dev/null +++ b/packages/element-web-module-api/src/utils.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. +*/ + +/** + * A value that may be a direct value or a Promise resolving to that value. + * Useful for callback APIs that can operate synchronously or asynchronously. + * @public + */ +export type MaybePromise = T | PromiseLike; From 3ac2b3665776f085163db867847da934dd5344f4 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 25 Feb 2026 13:08:11 +0000 Subject: [PATCH 140/156] bump to 1.10.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 6e63a29d64..6950765e7b 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.9.1", + "version": "1.10.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 7206db8243859a25d7a0752fe279fc69ddf7168f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 1 Mar 2026 16:05:08 +0000 Subject: [PATCH 141/156] Update Node.js to 7fddd9d --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index d732340278..b2a61f917d 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,6 +1,6 @@ ARG ELEMENT_VERSION=latest@sha256:a84f294ce46e4327ebacecb78bfc94cf6a45c7ffa5104a28f06b5ac69d0b2548 -FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:682368d8253e0c3364b803956085c456a612d738bd635926d73fa24db3ce53d7 AS builder +FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:7fddd9ddeae8196abf4a3ef2de34e11f7b1a722119f91f28ddf1e99dcafdf114 AS builder ARG BUILD_CONTEXT From 1a9ce45038d8c73c4cb46c518d31d00c83bdd9bd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 2 Mar 2026 14:50:09 +0000 Subject: [PATCH 142/156] Adopt shouldShowComponent customisations from legacy Module API --- .../element-web-module-api.api.md | 974 +++++++++--------- .../src/api/customisations.ts | 65 ++ .../element-web-module-api/src/api/index.ts | 7 + packages/element-web-module-api/src/index.ts | 2 + 4 files changed, 569 insertions(+), 479 deletions(-) create mode 100644 packages/element-web-module-api/src/api/customisations.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 86cce8dfec..7fc749c2e3 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -1,479 +1,495 @@ -## API Report File for "@element-hq/element-web-module-api" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ComponentType } from 'react'; -import { JSX } from 'react'; -import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { Root } from 'react-dom/client'; -import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; - -// @public -export interface AccountAuthApiExtension { - overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; -} - -// @public -export interface AccountAuthInfo { - accessToken: string; - deviceId: string; - homeserverUrl: string; - refreshToken?: string; - userId: string; -} - -// @public -export interface AccountDataApi { - delete(eventType: string): Promise; - get(eventType: string): Watchable; - set(eventType: string, content: unknown): Promise; -} - -// @alpha @deprecated (undocumented) -export interface AliasCustomisations { - // (undocumented) - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha -// -// @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { - // @alpha - readonly builtins: BuiltinsApi; - readonly client: ClientApi; - readonly config: ConfigApi; - createRoot(element: Element): Root; - // @alpha - readonly customComponents: CustomComponentsApi; - // @alpha - readonly extras: ExtrasApi; - readonly i18n: I18nApi; - readonly navigation: NavigationApi; - readonly rootNode: HTMLElement; - readonly stores: StoresApi; - // @alpha - readonly widgetLifecycle: WidgetLifecycleApi; -} - -// @alpha -export interface BuiltinsApi { - renderNotificationDecoration(roomId: string): React.ReactNode; - renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; -} - -// @alpha -export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; - -// @alpha @deprecated (undocumented) -export interface ChatExportCustomisations { - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - numberOfMessages?: number; - includeAttachments?: boolean; - sizeMb?: number; - }; -} - -// @public -export interface ClientApi { - accountData: AccountDataApi; - getRoom: (id: string) => Room | null; -} - -// @alpha @deprecated (undocumented) -export interface ComponentVisibilityCustomisations { - shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; -} - -// @public -export interface Config { - // (undocumented) - brand: string; -} - -// @public -export interface ConfigApi { - // (undocumented) - get(): Config; - // (undocumented) - get(key: K): Config[K]; - // (undocumented) - get(key?: K): Config | Config[K]; -} - -// @alpha -export interface CustomComponentsApi { - registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; - registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; -} - -// @alpha -export type CustomMessageComponentProps = { - mxEvent: MatrixEvent; -}; - -// @alpha -export type CustomMessageRenderFunction = ( -props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; - -// @alpha -export type CustomMessageRenderHints = { - allowEditingEvent?: boolean; - allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; -}; - -// @alpha -export type CustomRoomPreviewBarComponentProps = { - roomId?: string; - roomAlias?: string; -}; - -// @alpha -export type CustomRoomPreviewBarRenderFunction = ( -props: CustomRoomPreviewBarComponentProps, -originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; - -// @public -export interface DialogApiExtension { - openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; -} - -// @public -export type DialogHandle = { - finished: Promise<{ - ok: boolean; - model: M | null; - }>; - close(): void; -}; - -// @public -export interface DialogOptions { - title: string; -} - -// @public -export type DialogProps = { - onSubmit(model: M): void; - onCancel(): void; -}; - -// @alpha @deprecated (undocumented) -export interface DirectoryCustomisations { - // (undocumented) - requireCanonicalAliasAccessToPublish?(): boolean; -} - -// @alpha -export interface ExtrasApi { - getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; - setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; -} - -// @public -export interface I18nApi { - humanizeTime(this: void, timeMillis: number): string; - get language(): string; - register(this: void, translations: Partial): void; - translate(this: void, key: keyof Translations, variables?: Variables): string; -} - -// @alpha -export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @alpha @deprecated (undocumented) -export type LegacyCustomisations = (customisations: T) => void; - -// @alpha @deprecated (undocumented) -export interface LegacyCustomisationsApiExtension { - // @deprecated (undocumented) - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} - -// @alpha @deprecated (undocumented) -export interface LegacyModuleApiExtension { - // @deprecated - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} - -// @alpha @deprecated (undocumented) -export interface LifecycleCustomisations { - // (undocumented) - onLoggedOutAndStorageCleared?(): void; -} - -// @alpha -export type LocationRenderFunction = () => JSX.Element; - -// @alpha -export interface MatrixEvent { - content: Record; - eventId: string; - originServerTs: number; - roomId: string; - sender: string; - stateKey?: string; - type: string; - unsigned: Record; -} - -// @public -export type MaybePromise = T | PromiseLike; - -// @alpha @deprecated (undocumented) -export interface Media { - // (undocumented) - downloadSource(): Promise; - // (undocumented) - getSquareThumbnailHttp(dim: number): string | null; - // (undocumented) - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - readonly hasThumbnail: boolean; - // (undocumented) - readonly isEncrypted: boolean; - // (undocumented) - readonly srcHttp: string | null; - // (undocumented) - readonly srcMxc: string; - // (undocumented) - readonly thumbnailHttp: string | null; - // (undocumented) - readonly thumbnailMxc: string | null | undefined; -} - -// @alpha @deprecated (undocumented) -export interface MediaContructable { - // (undocumented) - new (prepared: PreparedMedia): Media; -} - -// @alpha @deprecated (undocumented) -export interface MediaCustomisations { - // (undocumented) - readonly Media: MediaContructable; - // (undocumented) - mediaFromContent(content: Content, client?: Client): Media; - // (undocumented) - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -// @public -export interface Module { - // (undocumented) - load(): Promise; -} - -// @public -export interface ModuleFactory { - // (undocumented) - new (api: Api): Module; - // (undocumented) - readonly moduleApiVersion: string; - // (undocumented) - readonly prototype: Module; -} - -// @public -export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string); -} - -// @public -export class ModuleLoader { - constructor(api: Api); - // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts - // - // (undocumented) - load(moduleExport: ModuleExport): Promise; - // (undocumented) - start(): Promise; -} - -// @public -export interface NavigationApi { - openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; - // @alpha - registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; - toMatrixToLink(link: string, join?: boolean): Promise; -} - -// @public -export interface OpenRoomOptions { - autoJoin?: boolean; - viaServers?: string[]; -} - -// @alpha -export type OriginalMessageComponentProps = { - showUrlPreview?: boolean; -}; - -// @alpha -export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @public -export interface Profile { - displayName?: string; - isGuest?: boolean; - userId?: string; -} - -// @public -export interface ProfileApiExtension { - readonly profile: Watchable; -} - -// @public -export interface Room { - getLastActiveTimestamp: () => number; - id: string; - name: Watchable; -} - -// @alpha @deprecated (undocumented) -export interface RoomListCustomisations { - isRoomVisible?(room: Room): boolean; -} - -// @public -export interface RoomListStoreApi { - getRooms(): Watchable; - waitForReady(): Promise; -} - -// @alpha -export interface RoomViewProps { - enableReadReceiptsAndMarkersOnActivity?: boolean; - hideComposer?: boolean; - hideHeader?: boolean; - hidePinnedMessageBanner?: boolean; - hideRightPanel?: boolean; - hideWidgets?: boolean; -} - -// @alpha @deprecated (undocumented) -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -// @alpha -export interface SpacePanelItemProps { - className?: string; - icon?: JSX.Element; - label: string; - onSelected: () => void; - style?: React.CSSProperties; - tooltip?: string; -} - -// @public -export interface StoresApi { - roomListStore: RoomListStoreApi; -} - -// @public -export type Translations = Record; - -// @alpha @deprecated (undocumented) -export interface UserIdentifierCustomisations { - getDisplayUserIdentifier(userId: string, opts: { - roomId?: string; - withDisplayName?: boolean; - }): string | null; -} - -// @public -export function useWatchable(watchable: Watchable): T; - -// @public -export type Variables = { - count?: number; - [key: string]: number | string | undefined; -}; - -// @public -export class Watchable { - constructor(currentValue: T); - // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts - // - // (undocumented) - protected readonly listeners: Set>; - protected onFirstWatch(): void; - protected onLastWatch(): void; - // (undocumented) - unwatch(listener: (value: T) => void): void; - get value(): T; - set value(value: T); - // (undocumented) - watch(listener: (value: T) => void): void; -} - -// @alpha -export type WidgetDescriptor = { - id: string; - templateUrl: string; - creatorUserId: string; - type: string; - origin: string; - roomId?: string; -}; - -// @alpha -export interface WidgetLifecycleApi { - registerCapabilitiesApprover(approver: CapabilitiesApprover): void; - registerIdentityApprover(approver: IdentityApprover): void; - registerPreloadApprover(approver: PreloadApprover): void; -} - -// @alpha @deprecated (undocumented) -export interface WidgetPermissionsCustomisations { - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -// @alpha @deprecated (undocumented) -export interface WidgetVariablesCustomisations { - isReady?(): Promise; - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; -} - -// (No @packageDocumentation comment for this package) - -``` - +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ComponentType } from 'react'; +import { JSX } from 'react'; +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { Root } from 'react-dom/client'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; + +// @public +export interface AccountAuthApiExtension { + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} + +// @public +export interface AccountAuthInfo { + accessToken: string; + deviceId: string; + homeserverUrl: string; + refreshToken?: string; + userId: string; +} + +// @public +export interface AccountDataApi { + delete(eventType: string): Promise; + get(eventType: string): Watchable; + set(eventType: string, content: unknown): Promise; +} + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { + // @alpha + readonly builtins: BuiltinsApi; + readonly client: ClientApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + // @alpha + readonly customComponents: CustomComponentsApi; + // @alpha + readonly customisations: CustomisationsApi; + // @alpha + readonly extras: ExtrasApi; + readonly i18n: I18nApi; + readonly navigation: NavigationApi; + readonly rootNode: HTMLElement; + readonly stores: StoresApi; + // @alpha + readonly widgetLifecycle: WidgetLifecycleApi; +} + +// @alpha +export interface BuiltinsApi { + renderNotificationDecoration(roomId: string): React.ReactNode; + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; +} + +// @alpha +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @public +export interface ClientApi { + accountData: AccountDataApi; + getRoom: (id: string) => Room | null; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @alpha +export interface CustomComponentsApi { + registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; +} + +// @alpha +export interface CustomisationsApi { + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} +// @alpha +export type CustomMessageComponentProps = { + mxEvent: MatrixEvent; +}; + +// @alpha +export type CustomMessageRenderFunction = ( +props: CustomMessageComponentProps, +originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; + +// @alpha +export type CustomMessageRenderHints = { + allowEditingEvent?: boolean; + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; +}; + +// @alpha +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +// @alpha +export type CustomRoomPreviewBarRenderFunction = ( +props: CustomRoomPreviewBarComponentProps, +originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; + +// @public +export interface DialogApiExtension { + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; +} + +// @public +export type DialogHandle = { + finished: Promise<{ + ok: boolean; + model: M | null; + }>; + close(): void; +}; + +// @public +export interface DialogOptions { + title: string; +} + +// @public +export type DialogProps = { + onSubmit(model: M): void; + onCancel(): void; +}; + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @alpha +export interface ExtrasApi { + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; +} + +// @public +export interface I18nApi { + humanizeTime(this: void, timeMillis: number): string; + get language(): string; + register(this: void, translations: Partial): void; + translate(this: void, key: keyof Translations, variables?: Variables): string; +} + +// @alpha +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha +export type LocationRenderFunction = () => JSX.Element; + +// @alpha +export interface MatrixEvent { + content: Record; + eventId: string; + originServerTs: number; + roomId: string; + sender: string; + stateKey?: string; + type: string; + unsigned: Record; +} + +// @public +export type MaybePromise = T | PromiseLike; + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @public +export interface NavigationApi { + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; + // @alpha + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; + toMatrixToLink(link: string, join?: boolean): Promise; +} + +// @public +export interface OpenRoomOptions { + autoJoin?: boolean; + viaServers?: string[]; +} + +// @alpha +export type OriginalMessageComponentProps = { + showUrlPreview?: boolean; +}; + +// @alpha +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @public +export interface Profile { + displayName?: string; + isGuest?: boolean; + userId?: string; +} + +// @public +export interface ProfileApiExtension { + readonly profile: Watchable; +} + +// @public +export interface Room { + getLastActiveTimestamp: () => number; + id: string; + name: Watchable; +} + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @public +export interface RoomListStoreApi { + getRooms(): Watchable; + waitForReady(): Promise; +} + +// @alpha +export interface RoomViewProps { + enableReadReceiptsAndMarkersOnActivity?: boolean; + hideComposer?: boolean; + hideHeader?: boolean; + hidePinnedMessageBanner?: boolean; + hideRightPanel?: boolean; + hideWidgets?: boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @alpha +export interface SpacePanelItemProps { + className?: string; + icon?: JSX.Element; + label: string; + onSelected: () => void; + style?: React.CSSProperties; + tooltip?: string; +} + +// @public +export interface StoresApi { + roomListStore: RoomListStoreApi; +} + +// @public +export type Translations = Record; + +// @alpha +export const enum UIComponent { + AddIntegrations = "UIComponent.addIntegrations", + CreateRooms = "UIComponent.roomCreation", + CreateSpaces = "UIComponent.spaceCreation", + ExploreRooms = "UIComponent.exploreRooms", + FilterContainer = "UIComponent.filterContainer", + InviteUsers = "UIComponent.sendInvites", + RoomOptionsMenu = "UIComponent.roomOptionsMenu" +} + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; +} + +// @public +export function useWatchable(watchable: Watchable): T; + +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + +// @public +export class Watchable { + constructor(currentValue: T); + // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly listeners: Set>; + protected onFirstWatch(): void; + protected onLastWatch(): void; + // (undocumented) + unwatch(listener: (value: T) => void): void; + get value(): T; + set value(value: T); + // (undocumented) + watch(listener: (value: T) => void): void; +} + +// @alpha +export type WidgetDescriptor = { + id: string; + templateUrl: string; + creatorUserId: string; + type: string; + origin: string; + roomId?: string; +}; + +// @alpha +export interface WidgetLifecycleApi { + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; + registerIdentityApprover(approver: IdentityApprover): void; + registerPreloadApprover(approver: PreloadApprover): void; +} + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/element-web-module-api/src/api/customisations.ts b/packages/element-web-module-api/src/api/customisations.ts new file mode 100644 index 0000000000..1a02c68ed0 --- /dev/null +++ b/packages/element-web-module-api/src/api/customisations.ts @@ -0,0 +1,65 @@ +/* +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. +*/ + +/** + * Enum of UI components which can have their behaviour tweaked + * @alpha + */ +export const enum UIComponent { + /** + * Components that lead to a user being invited. + */ + InviteUsers = "UIComponent.sendInvites", + + /** + * Components that lead to a room being created that aren't already + * guarded by some other condition (ie: "only if you can edit this + * space" is *not* guarded by this component, but "start DM" is). + */ + CreateRooms = "UIComponent.roomCreation", + + /** + * Components that lead to a Space being created that aren't already + * guarded by some other condition (ie: "only if you can add subspaces" + * is *not* guarded by this component, but "create new space" is). + */ + CreateSpaces = "UIComponent.spaceCreation", + + /** + * Components that lead to the public room directory. + */ + ExploreRooms = "UIComponent.exploreRooms", + + /** + * Components that lead to the user being able to easily add widgets + * and integrations to the room, such as from the room information card. + */ + AddIntegrations = "UIComponent.addIntegrations", + + /** + * Component that lead to the user being able to search, dial, explore rooms + */ + FilterContainer = "UIComponent.filterContainer", + + /** + * Components that lead the user to room options menu. + */ + RoomOptionsMenu = "UIComponent.roomOptionsMenu", +} + +/** + * API for customising Element Web's components + * @alpha Subject to change. + */ +export interface CustomisationsApi { + /** + * Method to register a callback which can affect whether a given component is drawn or not. + * @param fn - the callback, if it returns true the component will be rendered, if false it will not be. + * If undefined will defer to next callback, ultimately falling through to `true` if none return false. + */ + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 9c5e59ed57..3015892f65 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -20,6 +20,7 @@ import { type BuiltinsApi } from "./builtins.ts"; import { type StoresApi } from "./stores.ts"; import { type ClientApi } from "./client.ts"; import { type WidgetLifecycleApi } from "./widget-lifecycle.ts"; +import { type CustomisationsApi } from "./customisations.ts"; /** * Module interface for modules to implement. @@ -143,6 +144,12 @@ export interface Api */ readonly widgetLifecycle: WidgetLifecycleApi; + /** + * Allows modules to customise behaviour of app's components. + * @alpha Subject to change. + */ + readonly customisations: CustomisationsApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 89c8b24dbd..e009f2641c 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -23,5 +23,7 @@ export type * from "./api/builtins"; export type * from "./api/stores"; export type * from "./api/client"; export type * from "./api/widget-lifecycle"; +export type * from "./api/customisations"; +export { UIComponent } from "./api/customisations"; export * from "./api/watchable"; export type * from "./utils"; From ccf3fcf4af41758dcfead69da6a16359e6a7ef08 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 2 Mar 2026 16:54:23 +0000 Subject: [PATCH 143/156] Add API to replace Login component renderer --- .../element-web-module-api.api.md | 26 +++++- .../src/api/custom-components.ts | 83 ++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 86cce8dfec..43a36a6ca2 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -7,6 +7,7 @@ import { ComponentType } from 'react'; import { JSX } from 'react'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { ReactNode } from 'react'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -109,10 +110,29 @@ export interface ConfigApi { // @alpha export interface CustomComponentsApi { + registerLoginComponent(renderer: CustomLoginRenderFunction): void; registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; } +// @alpha +export type CustomLoginComponentProps = { + serverConfig: CustomLoginComponentPropsServerConfig; + fragmentAfterLogin?: string; + children?: ReactNode; + onLoggedIn(data: AccountAuthInfo): void; + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +// @alpha +export interface CustomLoginComponentPropsServerConfig { + hsName: string; + hsUrl: string; +} + +// @alpha +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + // @alpha export type CustomMessageComponentProps = { mxEvent: MatrixEvent; @@ -171,6 +191,11 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @alpha +export type ExtendablePropsRenderFunction =

( +props: P, +originalComponent: (props: P) => JSX.Element) => JSX.Element; + // @alpha export interface ExtrasApi { getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; @@ -476,4 +501,3 @@ export interface WidgetVariablesCustomisations { // (No @packageDocumentation comment for this package) ``` - diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index faba9c02cb..0265c9c63d 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -5,8 +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 type { JSX } from "react"; +import type { JSX, ReactNode } from "react"; import type { MatrixEvent } from "../models/event"; +import type { AccountAuthInfo } from "./auth.ts"; /** * Properties for all message components. @@ -91,6 +92,71 @@ export type CustomRoomPreviewBarRenderFunction = ( originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element, ) => JSX.Element; +/** + * Authentication server config object. + * @alpha Subject to change. + */ +export interface CustomLoginComponentPropsServerConfig { + /** + * The URL of the homeserver's client-server API + */ + hsUrl: string; + /** + * The name of the homeserver to present to the user + */ + hsName: string; +} + +/** + * Properties for login component. + * @alpha Subject to change. + */ +export type CustomLoginComponentProps = { + /** + * The details of the currently chosen Matrix homeserver + */ + serverConfig: CustomLoginComponentPropsServerConfig; + /** + * The URL fragment to send the user to after authentication is complete + */ + fragmentAfterLogin?: string; + /** + * Additional components to render as children + */ + children?: ReactNode; + /** + * Function to complete login + * @param data - the data to authenticate the user with + */ + onLoggedIn(data: AccountAuthInfo): void; + /** + * Function to change the selected server + * @param config - new server configuration details + */ + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +/** + * Function used to render a component with a superset of the known props. + * @alpha Unlikely to change + */ +export type ExtendablePropsRenderFunction =

( + /** + * Properties for the component to be rendered. + */ + props: P, + /** + * Render function for the original component. + */ + originalComponent: (props: P) => JSX.Element, +) => JSX.Element; + +/** + * Function used to render a login component. + * @alpha Unlikely to change + */ +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + /** * API for inserting custom components into Element. * @alpha Subject to change. @@ -143,4 +209,19 @@ export interface CustomComponentsApi { * ``` */ registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; + + /** + * Register a renderer for the login component. + * + * The render function should return a rendered component. + * + * @param renderer - The render function for the login component. + * @example + * ``` + * customComponents.registerLoginComponent((props, OriginalComponent) => { + * return ; + * }); + * ``` + */ + registerLoginComponent(renderer: CustomLoginRenderFunction): void; } From bf69ddf2b457c04c438da80658ad2ffbd9606179 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 6 Mar 2026 10:58:00 +0000 Subject: [PATCH 144/156] Add module APIs to enable widget toggle buttons --- .../element-web-module-api.api.md | 19 +++++- packages/element-web-module-api/package.json | 3 + .../element-web-module-api/src/api/extras.ts | 16 +++++ .../element-web-module-api/src/api/index.ts | 9 +++ .../element-web-module-api/src/api/widget.ts | 68 +++++++++++++++++++ packages/element-web-module-api/src/index.ts | 1 + 6 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 packages/element-web-module-api/src/api/widget.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 86cce8dfec..aff9156165 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,6 +5,7 @@ ```ts import { ComponentType } from 'react'; +import { IWidget } from 'matrix-widget-api'; import { JSX } from 'react'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; import { Root } from 'react-dom/client'; @@ -56,6 +57,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx readonly rootNode: HTMLElement; readonly stores: StoresApi; // @alpha + readonly widget: WidgetApi; + // @alpha readonly widgetLifecycle: WidgetLifecycleApi; } @@ -107,6 +110,9 @@ export interface ConfigApi { get(key?: K): Config | Config[K]; } +// @alpha +export type Container = "top" | "right" | "center"; + // @alpha export interface CustomComponentsApi { registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; @@ -174,6 +180,7 @@ export interface DirectoryCustomisations { // @alpha export interface ExtrasApi { getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; } @@ -358,6 +365,9 @@ export interface Room { name: Watchable; } +// @alpha +export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; + // @alpha @deprecated (undocumented) export interface RoomListCustomisations { isRoomVisible?(room: Room): boolean; @@ -436,6 +446,14 @@ export class Watchable { watch(listener: (value: T) => void): void; } +// @alpha +export interface WidgetApi { + getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; + getWidgetsInRoom(roomId: string): IWidget[]; + isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; + moveAppToContainer(app: IWidget, container: Container, roomId: string): void; +} + // @alpha export type WidgetDescriptor = { id: string; @@ -476,4 +494,3 @@ export interface WidgetVariablesCustomisations { // (No @packageDocumentation comment for this package) ``` - diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 6950765e7b..63a87ad3b2 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -61,5 +61,8 @@ "matrix-web-i18n": { "optional": true } + }, + "dependencies": { + "matrix-widget-api": "^1.17.0" } } diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index 5ffc9c4f16..1f6f9818c6 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -43,6 +43,15 @@ export interface SpacePanelItemProps { onSelected: () => void; } +/** + * A callback that returns a JSX element representing the buttons. + * + * @alpha + * @param roomId - The ID of the room for which the header is being rendered. + * @returns A JSX element representing the buttons to be rendered in the room header, or undefined if no buttons should be rendered. + */ +export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; + /** * API for inserting extra UI into Element Web. * @alpha Subject to change. @@ -67,4 +76,11 @@ export interface ExtrasApi { * @param cb - A callback that returns the list of visible room IDs. */ getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + + /** + * Sets the callback to get extra buttons in the room header (which can vary depending on the room being displayed). + * + * @param cb - A callback that returns a JSX element representing the buttons (see {@link RoomHeaderButtonsCallback}). + */ + setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; } diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 9c5e59ed57..840eea6eb2 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -1,5 +1,6 @@ /* 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. @@ -20,6 +21,7 @@ import { type BuiltinsApi } from "./builtins.ts"; import { type StoresApi } from "./stores.ts"; import { type ClientApi } from "./client.ts"; import { type WidgetLifecycleApi } from "./widget-lifecycle.ts"; +import { type WidgetApi } from "./widget.ts"; /** * Module interface for modules to implement. @@ -143,6 +145,13 @@ export interface Api */ readonly widgetLifecycle: WidgetLifecycleApi; + /** + * API for modules to interact with widgets in Element Web, including getting what widgets + * are active in a given room. + * @alpha Subject to change. + */ + readonly widget: WidgetApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/api/widget.ts b/packages/element-web-module-api/src/api/widget.ts new file mode 100644 index 0000000000..66c8b31e73 --- /dev/null +++ b/packages/element-web-module-api/src/api/widget.ts @@ -0,0 +1,68 @@ +/* +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 IWidget } from "matrix-widget-api"; + +/** + * Containers that control where a widget is displayed on the screen. + * + * "top" is the app drawer, and currently the only sensible value. + * + * "right" is the right panel, and the default for widgets. Setting + * this as a container on a widget is essentially like saying "no + * changes needed", though this may change in the future. + * + * "center" was uncodumented at time of porting this from an enum. + * Possibly when a widget replaces the main chat view like element call. + * + * @alpha Subject to change. + */ +export type Container = "top" | "right" | "center"; + +/** + * An API for interfacing with widgets in Element Web, including getting what widgets + * are active in a given room. + * @alpha Subject to change. + */ +export interface WidgetApi { + /** + * Gets the widgets active in a given room. + * + * @param roomId - The room to get the widgets for. + */ + getWidgetsInRoom(roomId: string): IWidget[]; + + /** + * Gets the URL of a widget's avatar, if it has one. + * + * @param app - The widget to get the avatar URL for. + * @param width - Optional width to resize the avatar to. + * @param height - Optional height to resize the avatar to. + * @param resizeMethod - Optional method to use when resizing the avatar. + * @returns The URL of the widget's avatar, or null if it doesn't have one. + */ + getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; + + /** + * Checks if a widget is in a specific container in a given room. + * + * @param app - The widget to check. + * @param container - The container to check. + * @param roomId - The room to check in. + * @returns True if the widget is in the specified container, false otherwise. + */ + isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; + + /** + * Moves a widget to a specific container in a given room. + * + * @param app - The widget to move. + * @param container - The container to move the widget to. + * @param roomId - The room to move the widget in. + */ + moveAppToContainer(app: IWidget, container: Container, roomId: string): void; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 89c8b24dbd..33b8b92c58 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -23,5 +23,6 @@ export type * from "./api/builtins"; export type * from "./api/stores"; export type * from "./api/client"; export type * from "./api/widget-lifecycle"; +export type * from "./api/widget"; export * from "./api/watchable"; export type * from "./utils"; From e322627d3e0903661ff88ade37b5f7442a2fbaa2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 6 Mar 2026 16:16:52 +0000 Subject: [PATCH 145/156] Bump Module API to 1.11.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 6950765e7b..c8e1b4aafa 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.10.0", + "version": "1.11.0", "description": "Module API surface for element-web", "repository": { "type": "git", From a96327b691ff3bcecac2a0710e51f99909ffb184 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 9 Mar 2026 15:48:14 +0000 Subject: [PATCH 146/156] Make matrix-widget-api a devDependency as it's only for the type --- packages/element-web-module-api/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 63a87ad3b2..c00b38855e 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -39,6 +39,7 @@ "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^4.0.0", "matrix-web-i18n": "^3.3.0", + "matrix-widget-api": "^1.17.0", "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", @@ -62,7 +63,5 @@ "optional": true } }, - "dependencies": { - "matrix-widget-api": "^1.17.0" - } + "dependencies": {} } From 4591a72d3ac1530f38b67ff84a3488a01131d35e Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 9 Mar 2026 15:51:42 +0000 Subject: [PATCH 147/156] Change to add callback --- packages/element-web-module-api/src/api/extras.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/element-web-module-api/src/api/extras.ts index 1f6f9818c6..ad437c4986 100644 --- a/packages/element-web-module-api/src/api/extras.ts +++ b/packages/element-web-module-api/src/api/extras.ts @@ -78,9 +78,9 @@ export interface ExtrasApi { getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; /** - * Sets the callback to get extra buttons in the room header (which can vary depending on the room being displayed). + * Adds a callback to get extra buttons in the room header (which can vary depending on the room being displayed). * * @param cb - A callback that returns a JSX element representing the buttons (see {@link RoomHeaderButtonsCallback}). */ - setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; + addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; } From 90ab1534c93102847e78cbc74a952e8da07be4b7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 9 Mar 2026 18:19:05 +0000 Subject: [PATCH 148/156] change back to unix line endings --- .../element-web-module-api.api.md | 1078 ++++++++--------- 1 file changed, 539 insertions(+), 539 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index d64e8bffda..5e32792e81 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -1,539 +1,539 @@ -## API Report File for "@element-hq/element-web-module-api" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ComponentType } from 'react'; -import { IWidget } from 'matrix-widget-api'; -import { JSX } from 'react'; -import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { ReactNode } from 'react'; -import { Root } from 'react-dom/client'; -import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; - -// @public -export interface AccountAuthApiExtension { - overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; -} - -// @public -export interface AccountAuthInfo { - accessToken: string; - deviceId: string; - homeserverUrl: string; - refreshToken?: string; - userId: string; -} - -// @public -export interface AccountDataApi { - delete(eventType: string): Promise; - get(eventType: string): Watchable; - set(eventType: string, content: unknown): Promise; -} - -// @alpha @deprecated (undocumented) -export interface AliasCustomisations { - // (undocumented) - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha -// -// @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { - // @alpha - readonly builtins: BuiltinsApi; - readonly client: ClientApi; - readonly config: ConfigApi; - createRoot(element: Element): Root; - // @alpha - readonly customComponents: CustomComponentsApi; - // @alpha - readonly customisations: CustomisationsApi; - // @alpha - readonly extras: ExtrasApi; - readonly i18n: I18nApi; - readonly navigation: NavigationApi; - readonly rootNode: HTMLElement; - readonly stores: StoresApi; - // @alpha - readonly widget: WidgetApi; - // @alpha - readonly widgetLifecycle: WidgetLifecycleApi; -} - -// @alpha -export interface BuiltinsApi { - renderNotificationDecoration(roomId: string): React.ReactNode; - renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; -} - -// @alpha -export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; - -// @alpha @deprecated (undocumented) -export interface ChatExportCustomisations { - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - numberOfMessages?: number; - includeAttachments?: boolean; - sizeMb?: number; - }; -} - -// @public -export interface ClientApi { - accountData: AccountDataApi; - getRoom: (id: string) => Room | null; -} - -// @alpha @deprecated (undocumented) -export interface ComponentVisibilityCustomisations { - shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; -} - -// @public -export interface Config { - // (undocumented) - brand: string; -} - -// @public -export interface ConfigApi { - // (undocumented) - get(): Config; - // (undocumented) - get(key: K): Config[K]; - // (undocumented) - get(key?: K): Config | Config[K]; -} - -// @alpha -export type Container = "top" | "right" | "center"; - -// @alpha -export interface CustomComponentsApi { - registerLoginComponent(renderer: CustomLoginRenderFunction): void; - registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; - registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; -} - -// @alpha -export interface CustomisationsApi { - registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; -} - -// @alpha -export type CustomLoginComponentProps = { - serverConfig: CustomLoginComponentPropsServerConfig; - fragmentAfterLogin?: string; - children?: ReactNode; - onLoggedIn(data: AccountAuthInfo): void; - onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; -}; - -// @alpha -export interface CustomLoginComponentPropsServerConfig { - hsName: string; - hsUrl: string; -} - -// @alpha -export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; - -// @alpha -export type CustomMessageComponentProps = { - mxEvent: MatrixEvent; -}; - -// @alpha -export type CustomMessageRenderFunction = ( -props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; - -// @alpha -export type CustomMessageRenderHints = { - allowEditingEvent?: boolean; - allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; -}; - -// @alpha -export type CustomRoomPreviewBarComponentProps = { - roomId?: string; - roomAlias?: string; -}; - -// @alpha -export type CustomRoomPreviewBarRenderFunction = ( -props: CustomRoomPreviewBarComponentProps, -originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; - -// @public -export interface DialogApiExtension { - openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; -} - -// @public -export type DialogHandle = { - finished: Promise<{ - ok: boolean; - model: M | null; - }>; - close(): void; -}; - -// @public -export interface DialogOptions { - title: string; -} - -// @public -export type DialogProps = { - onSubmit(model: M): void; - onCancel(): void; -}; - -// @alpha @deprecated (undocumented) -export interface DirectoryCustomisations { - // (undocumented) - requireCanonicalAliasAccessToPublish?(): boolean; -} - -// @alpha -export type ExtendablePropsRenderFunction =

( -props: P, -originalComponent: (props: P) => JSX.Element) => JSX.Element; - -// @alpha -export interface ExtrasApi { - addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; - getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; - setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; -} - -// @public -export interface I18nApi { - humanizeTime(this: void, timeMillis: number): string; - get language(): string; - register(this: void, translations: Partial): void; - translate(this: void, key: keyof Translations, variables?: Variables): string; -} - -// @alpha -export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @alpha @deprecated (undocumented) -export type LegacyCustomisations = (customisations: T) => void; - -// @alpha @deprecated (undocumented) -export interface LegacyCustomisationsApiExtension { - // @deprecated (undocumented) - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} - -// @alpha @deprecated (undocumented) -export interface LegacyModuleApiExtension { - // @deprecated - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} - -// @alpha @deprecated (undocumented) -export interface LifecycleCustomisations { - // (undocumented) - onLoggedOutAndStorageCleared?(): void; -} - -// @alpha -export type LocationRenderFunction = () => JSX.Element; - -// @alpha -export interface MatrixEvent { - content: Record; - eventId: string; - originServerTs: number; - roomId: string; - sender: string; - stateKey?: string; - type: string; - unsigned: Record; -} - -// @public -export type MaybePromise = T | PromiseLike; - -// @alpha @deprecated (undocumented) -export interface Media { - // (undocumented) - downloadSource(): Promise; - // (undocumented) - getSquareThumbnailHttp(dim: number): string | null; - // (undocumented) - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - readonly hasThumbnail: boolean; - // (undocumented) - readonly isEncrypted: boolean; - // (undocumented) - readonly srcHttp: string | null; - // (undocumented) - readonly srcMxc: string; - // (undocumented) - readonly thumbnailHttp: string | null; - // (undocumented) - readonly thumbnailMxc: string | null | undefined; -} - -// @alpha @deprecated (undocumented) -export interface MediaContructable { - // (undocumented) - new (prepared: PreparedMedia): Media; -} - -// @alpha @deprecated (undocumented) -export interface MediaCustomisations { - // (undocumented) - readonly Media: MediaContructable; - // (undocumented) - mediaFromContent(content: Content, client?: Client): Media; - // (undocumented) - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -// @public -export interface Module { - // (undocumented) - load(): Promise; -} - -// @public -export interface ModuleFactory { - // (undocumented) - new (api: Api): Module; - // (undocumented) - readonly moduleApiVersion: string; - // (undocumented) - readonly prototype: Module; -} - -// @public -export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string); -} - -// @public -export class ModuleLoader { - constructor(api: Api); - // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts - // - // (undocumented) - load(moduleExport: ModuleExport): Promise; - // (undocumented) - start(): Promise; -} - -// @public -export interface NavigationApi { - openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; - // @alpha - registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; - toMatrixToLink(link: string, join?: boolean): Promise; -} - -// @public -export interface OpenRoomOptions { - autoJoin?: boolean; - viaServers?: string[]; -} - -// @alpha -export type OriginalMessageComponentProps = { - showUrlPreview?: boolean; -}; - -// @alpha -export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @public -export interface Profile { - displayName?: string; - isGuest?: boolean; - userId?: string; -} - -// @public -export interface ProfileApiExtension { - readonly profile: Watchable; -} - -// @public -export interface Room { - getLastActiveTimestamp: () => number; - id: string; - name: Watchable; -} - -// @alpha -export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; - -// @alpha @deprecated (undocumented) -export interface RoomListCustomisations { - isRoomVisible?(room: Room): boolean; -} - -// @public -export interface RoomListStoreApi { - getRooms(): Watchable; - waitForReady(): Promise; -} - -// @alpha -export interface RoomViewProps { - enableReadReceiptsAndMarkersOnActivity?: boolean; - hideComposer?: boolean; - hideHeader?: boolean; - hidePinnedMessageBanner?: boolean; - hideRightPanel?: boolean; - hideWidgets?: boolean; -} - -// @alpha @deprecated (undocumented) -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -// @alpha -export interface SpacePanelItemProps { - className?: string; - icon?: JSX.Element; - label: string; - onSelected: () => void; - style?: React.CSSProperties; - tooltip?: string; -} - -// @public -export interface StoresApi { - roomListStore: RoomListStoreApi; -} - -// @public -export type Translations = Record; - -// @alpha -export const enum UIComponent { - AddIntegrations = "UIComponent.addIntegrations", - CreateRooms = "UIComponent.roomCreation", - CreateSpaces = "UIComponent.spaceCreation", - ExploreRooms = "UIComponent.exploreRooms", - FilterContainer = "UIComponent.filterContainer", - InviteUsers = "UIComponent.sendInvites", - RoomOptionsMenu = "UIComponent.roomOptionsMenu" -} - -// @alpha @deprecated (undocumented) -export interface UserIdentifierCustomisations { - getDisplayUserIdentifier(userId: string, opts: { - roomId?: string; - withDisplayName?: boolean; - }): string | null; -} - -// @public -export function useWatchable(watchable: Watchable): T; - -// @public -export type Variables = { - count?: number; - [key: string]: number | string | undefined; -}; - -// @public -export class Watchable { - constructor(currentValue: T); - // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts - // - // (undocumented) - protected readonly listeners: Set>; - protected onFirstWatch(): void; - protected onLastWatch(): void; - // (undocumented) - unwatch(listener: (value: T) => void): void; - get value(): T; - set value(value: T); - // (undocumented) - watch(listener: (value: T) => void): void; -} - -// @alpha -export interface WidgetApi { - getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; - getWidgetsInRoom(roomId: string): IWidget[]; - isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; - moveAppToContainer(app: IWidget, container: Container, roomId: string): void; -} - -// @alpha -export type WidgetDescriptor = { - id: string; - templateUrl: string; - creatorUserId: string; - type: string; - origin: string; - roomId?: string; -}; - -// @alpha -export interface WidgetLifecycleApi { - registerCapabilitiesApprover(approver: CapabilitiesApprover): void; - registerIdentityApprover(approver: IdentityApprover): void; - registerPreloadApprover(approver: PreloadApprover): void; -} - -// @alpha @deprecated (undocumented) -export interface WidgetPermissionsCustomisations { - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -// @alpha @deprecated (undocumented) -export interface WidgetVariablesCustomisations { - isReady?(): Promise; - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; -} - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ComponentType } from 'react'; +import { IWidget } from 'matrix-widget-api'; +import { JSX } from 'react'; +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { ReactNode } from 'react'; +import { Root } from 'react-dom/client'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; + +// @public +export interface AccountAuthApiExtension { + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} + +// @public +export interface AccountAuthInfo { + accessToken: string; + deviceId: string; + homeserverUrl: string; + refreshToken?: string; + userId: string; +} + +// @public +export interface AccountDataApi { + delete(eventType: string): Promise; + get(eventType: string): Watchable; + set(eventType: string, content: unknown): Promise; +} + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { + // @alpha + readonly builtins: BuiltinsApi; + readonly client: ClientApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + // @alpha + readonly customComponents: CustomComponentsApi; + // @alpha + readonly customisations: CustomisationsApi; + // @alpha + readonly extras: ExtrasApi; + readonly i18n: I18nApi; + readonly navigation: NavigationApi; + readonly rootNode: HTMLElement; + readonly stores: StoresApi; + // @alpha + readonly widget: WidgetApi; + // @alpha + readonly widgetLifecycle: WidgetLifecycleApi; +} + +// @alpha +export interface BuiltinsApi { + renderNotificationDecoration(roomId: string): React.ReactNode; + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; +} + +// @alpha +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @public +export interface ClientApi { + accountData: AccountDataApi; + getRoom: (id: string) => Room | null; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @alpha +export type Container = "top" | "right" | "center"; + +// @alpha +export interface CustomComponentsApi { + registerLoginComponent(renderer: CustomLoginRenderFunction): void; + registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; +} + +// @alpha +export interface CustomisationsApi { + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} + +// @alpha +export type CustomLoginComponentProps = { + serverConfig: CustomLoginComponentPropsServerConfig; + fragmentAfterLogin?: string; + children?: ReactNode; + onLoggedIn(data: AccountAuthInfo): void; + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +// @alpha +export interface CustomLoginComponentPropsServerConfig { + hsName: string; + hsUrl: string; +} + +// @alpha +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + +// @alpha +export type CustomMessageComponentProps = { + mxEvent: MatrixEvent; +}; + +// @alpha +export type CustomMessageRenderFunction = ( +props: CustomMessageComponentProps, +originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; + +// @alpha +export type CustomMessageRenderHints = { + allowEditingEvent?: boolean; + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; +}; + +// @alpha +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +// @alpha +export type CustomRoomPreviewBarRenderFunction = ( +props: CustomRoomPreviewBarComponentProps, +originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; + +// @public +export interface DialogApiExtension { + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; +} + +// @public +export type DialogHandle = { + finished: Promise<{ + ok: boolean; + model: M | null; + }>; + close(): void; +}; + +// @public +export interface DialogOptions { + title: string; +} + +// @public +export type DialogProps = { + onSubmit(model: M): void; + onCancel(): void; +}; + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @alpha +export type ExtendablePropsRenderFunction =

( +props: P, +originalComponent: (props: P) => JSX.Element) => JSX.Element; + +// @alpha +export interface ExtrasApi { + addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; +} + +// @public +export interface I18nApi { + humanizeTime(this: void, timeMillis: number): string; + get language(): string; + register(this: void, translations: Partial): void; + translate(this: void, key: keyof Translations, variables?: Variables): string; +} + +// @alpha +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha +export type LocationRenderFunction = () => JSX.Element; + +// @alpha +export interface MatrixEvent { + content: Record; + eventId: string; + originServerTs: number; + roomId: string; + sender: string; + stateKey?: string; + type: string; + unsigned: Record; +} + +// @public +export type MaybePromise = T | PromiseLike; + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @public +export interface NavigationApi { + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; + // @alpha + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; + toMatrixToLink(link: string, join?: boolean): Promise; +} + +// @public +export interface OpenRoomOptions { + autoJoin?: boolean; + viaServers?: string[]; +} + +// @alpha +export type OriginalMessageComponentProps = { + showUrlPreview?: boolean; +}; + +// @alpha +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @public +export interface Profile { + displayName?: string; + isGuest?: boolean; + userId?: string; +} + +// @public +export interface ProfileApiExtension { + readonly profile: Watchable; +} + +// @public +export interface Room { + getLastActiveTimestamp: () => number; + id: string; + name: Watchable; +} + +// @alpha +export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @public +export interface RoomListStoreApi { + getRooms(): Watchable; + waitForReady(): Promise; +} + +// @alpha +export interface RoomViewProps { + enableReadReceiptsAndMarkersOnActivity?: boolean; + hideComposer?: boolean; + hideHeader?: boolean; + hidePinnedMessageBanner?: boolean; + hideRightPanel?: boolean; + hideWidgets?: boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @alpha +export interface SpacePanelItemProps { + className?: string; + icon?: JSX.Element; + label: string; + onSelected: () => void; + style?: React.CSSProperties; + tooltip?: string; +} + +// @public +export interface StoresApi { + roomListStore: RoomListStoreApi; +} + +// @public +export type Translations = Record; + +// @alpha +export const enum UIComponent { + AddIntegrations = "UIComponent.addIntegrations", + CreateRooms = "UIComponent.roomCreation", + CreateSpaces = "UIComponent.spaceCreation", + ExploreRooms = "UIComponent.exploreRooms", + FilterContainer = "UIComponent.filterContainer", + InviteUsers = "UIComponent.sendInvites", + RoomOptionsMenu = "UIComponent.roomOptionsMenu" +} + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; +} + +// @public +export function useWatchable(watchable: Watchable): T; + +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + +// @public +export class Watchable { + constructor(currentValue: T); + // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly listeners: Set>; + protected onFirstWatch(): void; + protected onLastWatch(): void; + // (undocumented) + unwatch(listener: (value: T) => void): void; + get value(): T; + set value(value: T); + // (undocumented) + watch(listener: (value: T) => void): void; +} + +// @alpha +export interface WidgetApi { + getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; + getWidgetsInRoom(roomId: string): IWidget[]; + isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; + moveAppToContainer(app: IWidget, container: Container, roomId: string): void; +} + +// @alpha +export type WidgetDescriptor = { + id: string; + templateUrl: string; + creatorUserId: string; + type: string; + origin: string; + roomId?: string; +}; + +// @alpha +export interface WidgetLifecycleApi { + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; + registerIdentityApprover(approver: IdentityApprover): void; + registerPreloadApprover(approver: PreloadApprover): void; +} + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) + +``` From 196c8a082ab3c8586ffd1ed7ba2253b533743b11 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Mar 2026 14:42:06 +0000 Subject: [PATCH 149/156] Add default output for vitest Otherwise it's just mysteriously silent. --- packages/element-web-module-api/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-module-api/vite.config.ts b/packages/element-web-module-api/vite.config.ts index 01d3eebba6..441820037e 100644 --- a/packages/element-web-module-api/vite.config.ts +++ b/packages/element-web-module-api/vite.config.ts @@ -44,6 +44,7 @@ export default defineConfig({ reporter: "lcov", }, reporters: [ + ["default", { summary: false }], [ "vitest-sonar-reporter", { From a2ca504fd683b849a71542bba867d424f11b3047 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Mar 2026 12:52:08 +0000 Subject: [PATCH 150/156] Bump module API to 1.12.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index a1b1ffac3f..03dd49457b 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.11.0", + "version": "1.12.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 520dfffc95eb954934e3f4848c142661ce03cf13 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 26 Mar 2026 12:17:59 +0100 Subject: [PATCH 151/156] feat: add tags support to i18n api --- .../element-web-module-api.api.md | 1085 +++++++++-------- .../element-web-module-api/src/api/i18n.ts | 24 +- packages/element-web-module-api/src/index.ts | 2 +- 3 files changed, 570 insertions(+), 541 deletions(-) diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index 5e32792e81..74194eb5ad 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -1,539 +1,546 @@ -## API Report File for "@element-hq/element-web-module-api" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ComponentType } from 'react'; -import { IWidget } from 'matrix-widget-api'; -import { JSX } from 'react'; -import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { ReactNode } from 'react'; -import { Root } from 'react-dom/client'; -import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; - -// @public -export interface AccountAuthApiExtension { - overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; -} - -// @public -export interface AccountAuthInfo { - accessToken: string; - deviceId: string; - homeserverUrl: string; - refreshToken?: string; - userId: string; -} - -// @public -export interface AccountDataApi { - delete(eventType: string): Promise; - get(eventType: string): Watchable; - set(eventType: string, content: unknown): Promise; -} - -// @alpha @deprecated (undocumented) -export interface AliasCustomisations { - // (undocumented) - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha -// -// @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { - // @alpha - readonly builtins: BuiltinsApi; - readonly client: ClientApi; - readonly config: ConfigApi; - createRoot(element: Element): Root; - // @alpha - readonly customComponents: CustomComponentsApi; - // @alpha - readonly customisations: CustomisationsApi; - // @alpha - readonly extras: ExtrasApi; - readonly i18n: I18nApi; - readonly navigation: NavigationApi; - readonly rootNode: HTMLElement; - readonly stores: StoresApi; - // @alpha - readonly widget: WidgetApi; - // @alpha - readonly widgetLifecycle: WidgetLifecycleApi; -} - -// @alpha -export interface BuiltinsApi { - renderNotificationDecoration(roomId: string): React.ReactNode; - renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; -} - -// @alpha -export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; - -// @alpha @deprecated (undocumented) -export interface ChatExportCustomisations { - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - numberOfMessages?: number; - includeAttachments?: boolean; - sizeMb?: number; - }; -} - -// @public -export interface ClientApi { - accountData: AccountDataApi; - getRoom: (id: string) => Room | null; -} - -// @alpha @deprecated (undocumented) -export interface ComponentVisibilityCustomisations { - shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; -} - -// @public -export interface Config { - // (undocumented) - brand: string; -} - -// @public -export interface ConfigApi { - // (undocumented) - get(): Config; - // (undocumented) - get(key: K): Config[K]; - // (undocumented) - get(key?: K): Config | Config[K]; -} - -// @alpha -export type Container = "top" | "right" | "center"; - -// @alpha -export interface CustomComponentsApi { - registerLoginComponent(renderer: CustomLoginRenderFunction): void; - registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; - registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; -} - -// @alpha -export interface CustomisationsApi { - registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; -} - -// @alpha -export type CustomLoginComponentProps = { - serverConfig: CustomLoginComponentPropsServerConfig; - fragmentAfterLogin?: string; - children?: ReactNode; - onLoggedIn(data: AccountAuthInfo): void; - onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; -}; - -// @alpha -export interface CustomLoginComponentPropsServerConfig { - hsName: string; - hsUrl: string; -} - -// @alpha -export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; - -// @alpha -export type CustomMessageComponentProps = { - mxEvent: MatrixEvent; -}; - -// @alpha -export type CustomMessageRenderFunction = ( -props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; - -// @alpha -export type CustomMessageRenderHints = { - allowEditingEvent?: boolean; - allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; -}; - -// @alpha -export type CustomRoomPreviewBarComponentProps = { - roomId?: string; - roomAlias?: string; -}; - -// @alpha -export type CustomRoomPreviewBarRenderFunction = ( -props: CustomRoomPreviewBarComponentProps, -originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; - -// @public -export interface DialogApiExtension { - openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; -} - -// @public -export type DialogHandle = { - finished: Promise<{ - ok: boolean; - model: M | null; - }>; - close(): void; -}; - -// @public -export interface DialogOptions { - title: string; -} - -// @public -export type DialogProps = { - onSubmit(model: M): void; - onCancel(): void; -}; - -// @alpha @deprecated (undocumented) -export interface DirectoryCustomisations { - // (undocumented) - requireCanonicalAliasAccessToPublish?(): boolean; -} - -// @alpha -export type ExtendablePropsRenderFunction =

( -props: P, -originalComponent: (props: P) => JSX.Element) => JSX.Element; - -// @alpha -export interface ExtrasApi { - addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; - getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; - setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; -} - -// @public -export interface I18nApi { - humanizeTime(this: void, timeMillis: number): string; - get language(): string; - register(this: void, translations: Partial): void; - translate(this: void, key: keyof Translations, variables?: Variables): string; -} - -// @alpha -export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @alpha @deprecated (undocumented) -export type LegacyCustomisations = (customisations: T) => void; - -// @alpha @deprecated (undocumented) -export interface LegacyCustomisationsApiExtension { - // @deprecated (undocumented) - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} - -// @alpha @deprecated (undocumented) -export interface LegacyModuleApiExtension { - // @deprecated - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} - -// @alpha @deprecated (undocumented) -export interface LifecycleCustomisations { - // (undocumented) - onLoggedOutAndStorageCleared?(): void; -} - -// @alpha -export type LocationRenderFunction = () => JSX.Element; - -// @alpha -export interface MatrixEvent { - content: Record; - eventId: string; - originServerTs: number; - roomId: string; - sender: string; - stateKey?: string; - type: string; - unsigned: Record; -} - -// @public -export type MaybePromise = T | PromiseLike; - -// @alpha @deprecated (undocumented) -export interface Media { - // (undocumented) - downloadSource(): Promise; - // (undocumented) - getSquareThumbnailHttp(dim: number): string | null; - // (undocumented) - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - readonly hasThumbnail: boolean; - // (undocumented) - readonly isEncrypted: boolean; - // (undocumented) - readonly srcHttp: string | null; - // (undocumented) - readonly srcMxc: string; - // (undocumented) - readonly thumbnailHttp: string | null; - // (undocumented) - readonly thumbnailMxc: string | null | undefined; -} - -// @alpha @deprecated (undocumented) -export interface MediaContructable { - // (undocumented) - new (prepared: PreparedMedia): Media; -} - -// @alpha @deprecated (undocumented) -export interface MediaCustomisations { - // (undocumented) - readonly Media: MediaContructable; - // (undocumented) - mediaFromContent(content: Content, client?: Client): Media; - // (undocumented) - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -// @public -export interface Module { - // (undocumented) - load(): Promise; -} - -// @public -export interface ModuleFactory { - // (undocumented) - new (api: Api): Module; - // (undocumented) - readonly moduleApiVersion: string; - // (undocumented) - readonly prototype: Module; -} - -// @public -export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string); -} - -// @public -export class ModuleLoader { - constructor(api: Api); - // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts - // - // (undocumented) - load(moduleExport: ModuleExport): Promise; - // (undocumented) - start(): Promise; -} - -// @public -export interface NavigationApi { - openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; - // @alpha - registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; - toMatrixToLink(link: string, join?: boolean): Promise; -} - -// @public -export interface OpenRoomOptions { - autoJoin?: boolean; - viaServers?: string[]; -} - -// @alpha -export type OriginalMessageComponentProps = { - showUrlPreview?: boolean; -}; - -// @alpha -export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @public -export interface Profile { - displayName?: string; - isGuest?: boolean; - userId?: string; -} - -// @public -export interface ProfileApiExtension { - readonly profile: Watchable; -} - -// @public -export interface Room { - getLastActiveTimestamp: () => number; - id: string; - name: Watchable; -} - -// @alpha -export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; - -// @alpha @deprecated (undocumented) -export interface RoomListCustomisations { - isRoomVisible?(room: Room): boolean; -} - -// @public -export interface RoomListStoreApi { - getRooms(): Watchable; - waitForReady(): Promise; -} - -// @alpha -export interface RoomViewProps { - enableReadReceiptsAndMarkersOnActivity?: boolean; - hideComposer?: boolean; - hideHeader?: boolean; - hidePinnedMessageBanner?: boolean; - hideRightPanel?: boolean; - hideWidgets?: boolean; -} - -// @alpha @deprecated (undocumented) -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -// @alpha -export interface SpacePanelItemProps { - className?: string; - icon?: JSX.Element; - label: string; - onSelected: () => void; - style?: React.CSSProperties; - tooltip?: string; -} - -// @public -export interface StoresApi { - roomListStore: RoomListStoreApi; -} - -// @public -export type Translations = Record; - -// @alpha -export const enum UIComponent { - AddIntegrations = "UIComponent.addIntegrations", - CreateRooms = "UIComponent.roomCreation", - CreateSpaces = "UIComponent.spaceCreation", - ExploreRooms = "UIComponent.exploreRooms", - FilterContainer = "UIComponent.filterContainer", - InviteUsers = "UIComponent.sendInvites", - RoomOptionsMenu = "UIComponent.roomOptionsMenu" -} - -// @alpha @deprecated (undocumented) -export interface UserIdentifierCustomisations { - getDisplayUserIdentifier(userId: string, opts: { - roomId?: string; - withDisplayName?: boolean; - }): string | null; -} - -// @public -export function useWatchable(watchable: Watchable): T; - -// @public -export type Variables = { - count?: number; - [key: string]: number | string | undefined; -}; - -// @public -export class Watchable { - constructor(currentValue: T); - // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts - // - // (undocumented) - protected readonly listeners: Set>; - protected onFirstWatch(): void; - protected onLastWatch(): void; - // (undocumented) - unwatch(listener: (value: T) => void): void; - get value(): T; - set value(value: T); - // (undocumented) - watch(listener: (value: T) => void): void; -} - -// @alpha -export interface WidgetApi { - getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; - getWidgetsInRoom(roomId: string): IWidget[]; - isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; - moveAppToContainer(app: IWidget, container: Container, roomId: string): void; -} - -// @alpha -export type WidgetDescriptor = { - id: string; - templateUrl: string; - creatorUserId: string; - type: string; - origin: string; - roomId?: string; -}; - -// @alpha -export interface WidgetLifecycleApi { - registerCapabilitiesApprover(approver: CapabilitiesApprover): void; - registerIdentityApprover(approver: IdentityApprover): void; - registerPreloadApprover(approver: PreloadApprover): void; -} - -// @alpha @deprecated (undocumented) -export interface WidgetPermissionsCustomisations { - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -// @alpha @deprecated (undocumented) -export interface WidgetVariablesCustomisations { - isReady?(): Promise; - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; -} - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ComponentType } from 'react'; +import { IWidget } from 'matrix-widget-api'; +import { JSX } from 'react'; +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { ReactNode } from 'react'; +import { Root } from 'react-dom/client'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; + +// @public +export interface AccountAuthApiExtension { + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} + +// @public +export interface AccountAuthInfo { + accessToken: string; + deviceId: string; + homeserverUrl: string; + refreshToken?: string; + userId: string; +} + +// @public +export interface AccountDataApi { + delete(eventType: string): Promise; + get(eventType: string): Watchable; + set(eventType: string, content: unknown): Promise; +} + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { + // @alpha + readonly builtins: BuiltinsApi; + readonly client: ClientApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + // @alpha + readonly customComponents: CustomComponentsApi; + // @alpha + readonly customisations: CustomisationsApi; + // @alpha + readonly extras: ExtrasApi; + readonly i18n: I18nApi; + readonly navigation: NavigationApi; + readonly rootNode: HTMLElement; + readonly stores: StoresApi; + // @alpha + readonly widget: WidgetApi; + // @alpha + readonly widgetLifecycle: WidgetLifecycleApi; +} + +// @alpha +export interface BuiltinsApi { + renderNotificationDecoration(roomId: string): React.ReactNode; + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; +} + +// @alpha +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @public +export interface ClientApi { + accountData: AccountDataApi; + getRoom: (id: string) => Room | null; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @alpha +export type Container = "top" | "right" | "center"; + +// @alpha +export interface CustomComponentsApi { + registerLoginComponent(renderer: CustomLoginRenderFunction): void; + registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; +} + +// @alpha +export interface CustomisationsApi { + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} + +// @alpha +export type CustomLoginComponentProps = { + serverConfig: CustomLoginComponentPropsServerConfig; + fragmentAfterLogin?: string; + children?: ReactNode; + onLoggedIn(data: AccountAuthInfo): void; + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +// @alpha +export interface CustomLoginComponentPropsServerConfig { + hsName: string; + hsUrl: string; +} + +// @alpha +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + +// @alpha +export type CustomMessageComponentProps = { + mxEvent: MatrixEvent; +}; + +// @alpha +export type CustomMessageRenderFunction = ( +props: CustomMessageComponentProps, +originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; + +// @alpha +export type CustomMessageRenderHints = { + allowEditingEvent?: boolean; + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; +}; + +// @alpha +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +// @alpha +export type CustomRoomPreviewBarRenderFunction = ( +props: CustomRoomPreviewBarComponentProps, +originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; + +// @public +export interface DialogApiExtension { + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; +} + +// @public +export type DialogHandle = { + finished: Promise<{ + ok: boolean; + model: M | null; + }>; + close(): void; +}; + +// @public +export interface DialogOptions { + title: string; +} + +// @public +export type DialogProps = { + onSubmit(model: M): void; + onCancel(): void; +}; + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @alpha +export type ExtendablePropsRenderFunction =

( +props: P, +originalComponent: (props: P) => JSX.Element) => JSX.Element; + +// @alpha +export interface ExtrasApi { + addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; +} + +// @public +export interface I18nApi { + humanizeTime(this: void, timeMillis: number): string; + get language(): string; + register(this: void, translations: Partial): void; + translate(this: void, key: keyof Translations, variables?: Variables): string; + translate(this: void, key: keyof Translations, variables: Variables | undefined, tags: Tags): ReactNode; +} + +// @alpha +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha +export type LocationRenderFunction = () => JSX.Element; + +// @alpha +export interface MatrixEvent { + content: Record; + eventId: string; + originServerTs: number; + roomId: string; + sender: string; + stateKey?: string; + type: string; + unsigned: Record; +} + +// @public +export type MaybePromise = T | PromiseLike; + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @public +export interface NavigationApi { + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; + // @alpha + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; + toMatrixToLink(link: string, join?: boolean): Promise; +} + +// @public +export interface OpenRoomOptions { + autoJoin?: boolean; + viaServers?: string[]; +} + +// @alpha +export type OriginalMessageComponentProps = { + showUrlPreview?: boolean; +}; + +// @alpha +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @public +export interface Profile { + displayName?: string; + isGuest?: boolean; + userId?: string; +} + +// @public +export interface ProfileApiExtension { + readonly profile: Watchable; +} + +// @public +export interface Room { + getLastActiveTimestamp: () => number; + id: string; + name: Watchable; +} + +// @alpha +export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @public +export interface RoomListStoreApi { + getRooms(): Watchable; + waitForReady(): Promise; +} + +// @alpha +export interface RoomViewProps { + enableReadReceiptsAndMarkersOnActivity?: boolean; + hideComposer?: boolean; + hideHeader?: boolean; + hidePinnedMessageBanner?: boolean; + hideRightPanel?: boolean; + hideWidgets?: boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @alpha +export interface SpacePanelItemProps { + className?: string; + icon?: JSX.Element; + label: string; + onSelected: () => void; + style?: React.CSSProperties; + tooltip?: string; +} + +// @public +export interface StoresApi { + roomListStore: RoomListStoreApi; +} + +// @public +export type SubstitutionValue = number | string | ReactNode | ((sub: string) => ReactNode); + +// @public +export type Tags = Record; + +// @public +export type Translations = Record; + +// @alpha +export const enum UIComponent { + AddIntegrations = "UIComponent.addIntegrations", + CreateRooms = "UIComponent.roomCreation", + CreateSpaces = "UIComponent.spaceCreation", + ExploreRooms = "UIComponent.exploreRooms", + FilterContainer = "UIComponent.filterContainer", + InviteUsers = "UIComponent.sendInvites", + RoomOptionsMenu = "UIComponent.roomOptionsMenu" +} + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; +} + +// @public +export function useWatchable(watchable: Watchable): T; + +// @public +export type Variables = { + count?: number; + [key: string]: SubstitutionValue; +}; + +// @public +export class Watchable { + constructor(currentValue: T); + // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly listeners: Set>; + protected onFirstWatch(): void; + protected onLastWatch(): void; + // (undocumented) + unwatch(listener: (value: T) => void): void; + get value(): T; + set value(value: T); + // (undocumented) + watch(listener: (value: T) => void): void; +} + +// @alpha +export interface WidgetApi { + getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; + getWidgetsInRoom(roomId: string): IWidget[]; + isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; + moveAppToContainer(app: IWidget, container: Container, roomId: string): void; +} + +// @alpha +export type WidgetDescriptor = { + id: string; + templateUrl: string; + creatorUserId: string; + type: string; + origin: string; + roomId?: string; +}; + +// @alpha +export interface WidgetLifecycleApi { + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; + registerIdentityApprover(approver: IdentityApprover): void; + registerPreloadApprover(approver: PreloadApprover): void; +} + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts index 564390c7f0..c63bb807b7 100644 --- a/packages/element-web-module-api/src/api/i18n.ts +++ b/packages/element-web-module-api/src/api/i18n.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 { type ReactNode } from "react"; + /** * The translations for the module. * @public @@ -16,6 +18,12 @@ export type Translations = Record< } >; +/** + * The value a variable or tag can take for a translation interpolation. + * @public + */ +export type SubstitutionValue = number | string | ReactNode | ((sub: string) => ReactNode); + /** * Variables to interpolate into a translation. * @public @@ -25,9 +33,16 @@ export type Variables = { * The number of items to count for pluralised translations */ count?: number; - [key: string]: number | string | undefined; + [key: string]: SubstitutionValue; }; +/** + * Tags to interpolate into a translation, where the value is a ReactNode or a function that returns a ReactNode. + * This allows for more complex interpolations, such as links or formatted text. + * @public + */ +export type Tags = Record; + /** * The API for interacting with translations. * @public @@ -49,6 +64,13 @@ export interface I18nApi { * @param variables - Optional variables to interpolate into the translation */ translate(this: void, key: keyof Translations, variables?: Variables): string; + /** + * Perform a translation, with optional variables + * @param key - The key to translate + * @param variables - Optional variables to interpolate into the translation + * @param tags - Optional tags to interpolate into the translation + */ + translate(this: void, key: keyof Translations, variables: Variables | undefined, tags: Tags): ReactNode; /** * Convert a timestamp into a translated, human-readable time, diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 1f34106cfc..a246b12f9e 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. export { ModuleLoader, ModuleIncompatibleError } from "./loader"; export type { Api, Module, ModuleFactory } from "./api"; export type { Config, ConfigApi } from "./api/config"; -export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type { I18nApi, Variables, Translations, SubstitutionValue, Tags } from "./api/i18n"; export type * from "./models/event"; export type * from "./models/Room"; export type * from "./api/custom-components"; From 91d153aca3a6998579ede1fd7b25b8af2b373393 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 27 Mar 2026 09:29:13 +0100 Subject: [PATCH 152/156] doc: add for `sub` in `SubstitutionValue` --- packages/element-web-module-api/src/api/i18n.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts index c63bb807b7..030081ae9d 100644 --- a/packages/element-web-module-api/src/api/i18n.ts +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -20,6 +20,11 @@ export type Translations = Record< /** * The value a variable or tag can take for a translation interpolation. + * + * When used as a function, `sub` is the text content wrapped between the tag + * in the translation string. For example, given `"Click here"`, the + * function receives `"here"` and should return a `ReactNode` wrapping it. + * * @public */ export type SubstitutionValue = number | string | ReactNode | ((sub: string) => ReactNode); From 00a2e704ecd3650031230f7662bf9d1dd3c5679f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 30 Mar 2026 16:25:59 +0200 Subject: [PATCH 153/156] chore: bump ew module api to 1.13.0 --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index 03dd49457b..f8b648987f 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.12.0", + "version": "1.13.0", "description": "Module API surface for element-web", "repository": { "type": "git", From 86d9e32d538b890aaf6853bf3e8459bca5002829 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:27:28 +0000 Subject: [PATCH 154/156] Update Node.js to 0174333 (#235) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/element-web-module-api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/Dockerfile b/packages/element-web-module-api/Dockerfile index b2a61f917d..d79a573dd1 100644 --- a/packages/element-web-module-api/Dockerfile +++ b/packages/element-web-module-api/Dockerfile @@ -1,6 +1,6 @@ ARG ELEMENT_VERSION=latest@sha256:a84f294ce46e4327ebacecb78bfc94cf6a45c7ffa5104a28f06b5ac69d0b2548 -FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:7fddd9ddeae8196abf4a3ef2de34e11f7b1a722119f91f28ddf1e99dcafdf114 AS builder +FROM --platform=$BUILDPLATFORM node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b AS builder ARG BUILD_CONTEXT From 6d1cd514e48147d33d46e194bc106295398f5ef2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:43:24 +0000 Subject: [PATCH 155/156] 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] --- packages/element-web-module-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index f8b648987f..376f1a9ff7 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -43,7 +43,7 @@ "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^5.7.3", - "vite": "^7.1.11", + "vite": "^7.3.2", "vite-plugin-dts": "^4.5.0", "vitest": "^4.0.0", "vitest-sonar-reporter": "^3.0.0" From ad2e6d37bc5fb8edb104f08804e031f34ba91927 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Apr 2026 09:18:23 +0100 Subject: [PATCH 156/156] mv element-web-module-api module-api --- .../Dockerfile | 0 .../README.md | 0 .../api-extractor.json | 0 .../element-web-module-api.api.md | 1092 ++++++++--------- .../package.json | 0 .../src/@types/global.d.ts | 0 .../src/api/auth.ts | 0 .../src/api/builtins.ts | 0 .../src/api/client.ts | 0 .../src/api/config.ts | 0 .../src/api/custom-components.ts | 0 .../src/api/customisations.ts | 0 .../src/api/dialog.ts | 0 .../src/api/extras.ts | 0 .../src/api/i18n.ts | 0 .../src/api/index.test.ts | 0 .../src/api/index.ts | 0 .../src/api/legacy-customisations.ts | 0 .../src/api/legacy-modules.ts | 0 .../src/api/navigation.ts | 0 .../src/api/profile.ts | 0 .../src/api/stores.ts | 0 .../src/api/watchable.test.ts | 0 .../src/api/watchable.ts | 0 .../src/api/widget-lifecycle.ts | 0 .../src/api/widget.ts | 0 .../src/index.ts | 0 .../src/loader.test.ts | 0 .../src/loader.ts | 0 .../src/models/Room.ts | 0 .../src/models/event.ts | 0 .../src/utils.ts | 0 .../tsconfig.json | 0 .../vite.config.ts | 0 34 files changed, 546 insertions(+), 546 deletions(-) rename packages/{element-web-module-api => module-api}/Dockerfile (100%) rename packages/{element-web-module-api => module-api}/README.md (100%) rename packages/{element-web-module-api => module-api}/api-extractor.json (100%) rename packages/{element-web-module-api => module-api}/element-web-module-api.api.md (96%) rename packages/{element-web-module-api => module-api}/package.json (100%) rename packages/{element-web-module-api => module-api}/src/@types/global.d.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/auth.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/builtins.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/client.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/config.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/custom-components.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/customisations.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/dialog.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/extras.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/i18n.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/index.test.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/index.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/legacy-customisations.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/legacy-modules.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/navigation.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/profile.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/stores.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/watchable.test.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/watchable.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/widget-lifecycle.ts (100%) rename packages/{element-web-module-api => module-api}/src/api/widget.ts (100%) rename packages/{element-web-module-api => module-api}/src/index.ts (100%) rename packages/{element-web-module-api => module-api}/src/loader.test.ts (100%) rename packages/{element-web-module-api => module-api}/src/loader.ts (100%) rename packages/{element-web-module-api => module-api}/src/models/Room.ts (100%) rename packages/{element-web-module-api => module-api}/src/models/event.ts (100%) rename packages/{element-web-module-api => module-api}/src/utils.ts (100%) rename packages/{element-web-module-api => module-api}/tsconfig.json (100%) rename packages/{element-web-module-api => module-api}/vite.config.ts (100%) diff --git a/packages/element-web-module-api/Dockerfile b/packages/module-api/Dockerfile similarity index 100% rename from packages/element-web-module-api/Dockerfile rename to packages/module-api/Dockerfile diff --git a/packages/element-web-module-api/README.md b/packages/module-api/README.md similarity index 100% rename from packages/element-web-module-api/README.md rename to packages/module-api/README.md diff --git a/packages/element-web-module-api/api-extractor.json b/packages/module-api/api-extractor.json similarity index 100% rename from packages/element-web-module-api/api-extractor.json rename to packages/module-api/api-extractor.json diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/module-api/element-web-module-api.api.md similarity index 96% rename from packages/element-web-module-api/element-web-module-api.api.md rename to packages/module-api/element-web-module-api.api.md index 74194eb5ad..8632818f5c 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/module-api/element-web-module-api.api.md @@ -1,546 +1,546 @@ -## API Report File for "@element-hq/element-web-module-api" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ComponentType } from 'react'; -import { IWidget } from 'matrix-widget-api'; -import { JSX } from 'react'; -import { ModuleApi } from '@matrix-org/react-sdk-module-api'; -import { ReactNode } from 'react'; -import { Root } from 'react-dom/client'; -import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; - -// @public -export interface AccountAuthApiExtension { - overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; -} - -// @public -export interface AccountAuthInfo { - accessToken: string; - deviceId: string; - homeserverUrl: string; - refreshToken?: string; - userId: string; -} - -// @public -export interface AccountDataApi { - delete(eventType: string): Promise; - get(eventType: string): Watchable; - set(eventType: string, content: unknown): Promise; -} - -// @alpha @deprecated (undocumented) -export interface AliasCustomisations { - // (undocumented) - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha -// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha -// -// @public -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { - // @alpha - readonly builtins: BuiltinsApi; - readonly client: ClientApi; - readonly config: ConfigApi; - createRoot(element: Element): Root; - // @alpha - readonly customComponents: CustomComponentsApi; - // @alpha - readonly customisations: CustomisationsApi; - // @alpha - readonly extras: ExtrasApi; - readonly i18n: I18nApi; - readonly navigation: NavigationApi; - readonly rootNode: HTMLElement; - readonly stores: StoresApi; - // @alpha - readonly widget: WidgetApi; - // @alpha - readonly widgetLifecycle: WidgetLifecycleApi; -} - -// @alpha -export interface BuiltinsApi { - renderNotificationDecoration(roomId: string): React.ReactNode; - renderRoomAvatar(roomId: string, size?: string): React.ReactNode; - renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; -} - -// @alpha -export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; - -// @alpha @deprecated (undocumented) -export interface ChatExportCustomisations { - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - numberOfMessages?: number; - includeAttachments?: boolean; - sizeMb?: number; - }; -} - -// @public -export interface ClientApi { - accountData: AccountDataApi; - getRoom: (id: string) => Room | null; -} - -// @alpha @deprecated (undocumented) -export interface ComponentVisibilityCustomisations { - shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; -} - -// @public -export interface Config { - // (undocumented) - brand: string; -} - -// @public -export interface ConfigApi { - // (undocumented) - get(): Config; - // (undocumented) - get(key: K): Config[K]; - // (undocumented) - get(key?: K): Config | Config[K]; -} - -// @alpha -export type Container = "top" | "right" | "center"; - -// @alpha -export interface CustomComponentsApi { - registerLoginComponent(renderer: CustomLoginRenderFunction): void; - registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; - registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; -} - -// @alpha -export interface CustomisationsApi { - registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; -} - -// @alpha -export type CustomLoginComponentProps = { - serverConfig: CustomLoginComponentPropsServerConfig; - fragmentAfterLogin?: string; - children?: ReactNode; - onLoggedIn(data: AccountAuthInfo): void; - onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; -}; - -// @alpha -export interface CustomLoginComponentPropsServerConfig { - hsName: string; - hsUrl: string; -} - -// @alpha -export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; - -// @alpha -export type CustomMessageComponentProps = { - mxEvent: MatrixEvent; -}; - -// @alpha -export type CustomMessageRenderFunction = ( -props: CustomMessageComponentProps, -originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; - -// @alpha -export type CustomMessageRenderHints = { - allowEditingEvent?: boolean; - allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; -}; - -// @alpha -export type CustomRoomPreviewBarComponentProps = { - roomId?: string; - roomAlias?: string; -}; - -// @alpha -export type CustomRoomPreviewBarRenderFunction = ( -props: CustomRoomPreviewBarComponentProps, -originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; - -// @public -export interface DialogApiExtension { - openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; -} - -// @public -export type DialogHandle = { - finished: Promise<{ - ok: boolean; - model: M | null; - }>; - close(): void; -}; - -// @public -export interface DialogOptions { - title: string; -} - -// @public -export type DialogProps = { - onSubmit(model: M): void; - onCancel(): void; -}; - -// @alpha @deprecated (undocumented) -export interface DirectoryCustomisations { - // (undocumented) - requireCanonicalAliasAccessToPublish?(): boolean; -} - -// @alpha -export type ExtendablePropsRenderFunction =

( -props: P, -originalComponent: (props: P) => JSX.Element) => JSX.Element; - -// @alpha -export interface ExtrasApi { - addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; - getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; - setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; -} - -// @public -export interface I18nApi { - humanizeTime(this: void, timeMillis: number): string; - get language(): string; - register(this: void, translations: Partial): void; - translate(this: void, key: keyof Translations, variables?: Variables): string; - translate(this: void, key: keyof Translations, variables: Variables | undefined, tags: Tags): ReactNode; -} - -// @alpha -export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @alpha @deprecated (undocumented) -export type LegacyCustomisations = (customisations: T) => void; - -// @alpha @deprecated (undocumented) -export interface LegacyCustomisationsApiExtension { - // @deprecated (undocumented) - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - // @deprecated (undocumented) - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; - // @deprecated (undocumented) - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} - -// @alpha @deprecated (undocumented) -export interface LegacyModuleApiExtension { - // @deprecated - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -} - -// @alpha @deprecated (undocumented) -export interface LifecycleCustomisations { - // (undocumented) - onLoggedOutAndStorageCleared?(): void; -} - -// @alpha -export type LocationRenderFunction = () => JSX.Element; - -// @alpha -export interface MatrixEvent { - content: Record; - eventId: string; - originServerTs: number; - roomId: string; - sender: string; - stateKey?: string; - type: string; - unsigned: Record; -} - -// @public -export type MaybePromise = T | PromiseLike; - -// @alpha @deprecated (undocumented) -export interface Media { - // (undocumented) - downloadSource(): Promise; - // (undocumented) - getSquareThumbnailHttp(dim: number): string | null; - // (undocumented) - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - // (undocumented) - readonly hasThumbnail: boolean; - // (undocumented) - readonly isEncrypted: boolean; - // (undocumented) - readonly srcHttp: string | null; - // (undocumented) - readonly srcMxc: string; - // (undocumented) - readonly thumbnailHttp: string | null; - // (undocumented) - readonly thumbnailMxc: string | null | undefined; -} - -// @alpha @deprecated (undocumented) -export interface MediaContructable { - // (undocumented) - new (prepared: PreparedMedia): Media; -} - -// @alpha @deprecated (undocumented) -export interface MediaCustomisations { - // (undocumented) - readonly Media: MediaContructable; - // (undocumented) - mediaFromContent(content: Content, client?: Client): Media; - // (undocumented) - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -// @public -export interface Module { - // (undocumented) - load(): Promise; -} - -// @public -export interface ModuleFactory { - // (undocumented) - new (api: Api): Module; - // (undocumented) - readonly moduleApiVersion: string; - // (undocumented) - readonly prototype: Module; -} - -// @public -export class ModuleIncompatibleError extends Error { - constructor(pluginVersion: string); -} - -// @public -export class ModuleLoader { - constructor(api: Api); - // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts - // - // (undocumented) - load(moduleExport: ModuleExport): Promise; - // (undocumented) - start(): Promise; -} - -// @public -export interface NavigationApi { - openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; - // @alpha - registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; - toMatrixToLink(link: string, join?: boolean): Promise; -} - -// @public -export interface OpenRoomOptions { - autoJoin?: boolean; - viaServers?: string[]; -} - -// @alpha -export type OriginalMessageComponentProps = { - showUrlPreview?: boolean; -}; - -// @alpha -export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; - -// @public -export interface Profile { - displayName?: string; - isGuest?: boolean; - userId?: string; -} - -// @public -export interface ProfileApiExtension { - readonly profile: Watchable; -} - -// @public -export interface Room { - getLastActiveTimestamp: () => number; - id: string; - name: Watchable; -} - -// @alpha -export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; - -// @alpha @deprecated (undocumented) -export interface RoomListCustomisations { - isRoomVisible?(room: Room): boolean; -} - -// @public -export interface RoomListStoreApi { - getRooms(): Watchable; - waitForReady(): Promise; -} - -// @alpha -export interface RoomViewProps { - enableReadReceiptsAndMarkersOnActivity?: boolean; - hideComposer?: boolean; - hideHeader?: boolean; - hidePinnedMessageBanner?: boolean; - hideRightPanel?: boolean; - hideWidgets?: boolean; -} - -// @alpha @deprecated (undocumented) -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -// @alpha -export interface SpacePanelItemProps { - className?: string; - icon?: JSX.Element; - label: string; - onSelected: () => void; - style?: React.CSSProperties; - tooltip?: string; -} - -// @public -export interface StoresApi { - roomListStore: RoomListStoreApi; -} - -// @public -export type SubstitutionValue = number | string | ReactNode | ((sub: string) => ReactNode); - -// @public -export type Tags = Record; - -// @public -export type Translations = Record; - -// @alpha -export const enum UIComponent { - AddIntegrations = "UIComponent.addIntegrations", - CreateRooms = "UIComponent.roomCreation", - CreateSpaces = "UIComponent.spaceCreation", - ExploreRooms = "UIComponent.exploreRooms", - FilterContainer = "UIComponent.filterContainer", - InviteUsers = "UIComponent.sendInvites", - RoomOptionsMenu = "UIComponent.roomOptionsMenu" -} - -// @alpha @deprecated (undocumented) -export interface UserIdentifierCustomisations { - getDisplayUserIdentifier(userId: string, opts: { - roomId?: string; - withDisplayName?: boolean; - }): string | null; -} - -// @public -export function useWatchable(watchable: Watchable): T; - -// @public -export type Variables = { - count?: number; - [key: string]: SubstitutionValue; -}; - -// @public -export class Watchable { - constructor(currentValue: T); - // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts - // - // (undocumented) - protected readonly listeners: Set>; - protected onFirstWatch(): void; - protected onLastWatch(): void; - // (undocumented) - unwatch(listener: (value: T) => void): void; - get value(): T; - set value(value: T); - // (undocumented) - watch(listener: (value: T) => void): void; -} - -// @alpha -export interface WidgetApi { - getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; - getWidgetsInRoom(roomId: string): IWidget[]; - isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; - moveAppToContainer(app: IWidget, container: Container, roomId: string): void; -} - -// @alpha -export type WidgetDescriptor = { - id: string; - templateUrl: string; - creatorUserId: string; - type: string; - origin: string; - roomId?: string; -}; - -// @alpha -export interface WidgetLifecycleApi { - registerCapabilitiesApprover(approver: CapabilitiesApprover): void; - registerIdentityApprover(approver: IdentityApprover): void; - registerPreloadApprover(approver: PreloadApprover): void; -} - -// @alpha @deprecated (undocumented) -export interface WidgetPermissionsCustomisations { - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -// @alpha @deprecated (undocumented) -export interface WidgetVariablesCustomisations { - isReady?(): Promise; - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; -} - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@element-hq/element-web-module-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ComponentType } from 'react'; +import { IWidget } from 'matrix-widget-api'; +import { JSX } from 'react'; +import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { ReactNode } from 'react'; +import { Root } from 'react-dom/client'; +import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; + +// @public +export interface AccountAuthApiExtension { + overwriteAccountAuth(accountInfo: AccountAuthInfo): Promise; +} + +// @public +export interface AccountAuthInfo { + accessToken: string; + deviceId: string; + homeserverUrl: string; + refreshToken?: string; + userId: string; +} + +// @public +export interface AccountDataApi { + delete(eventType: string): Promise; + get(eventType: string): Watchable; + set(eventType: string, content: unknown): Promise; +} + +// @alpha @deprecated (undocumented) +export interface AliasCustomisations { + // (undocumented) + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyModuleApiExtension" which is marked as @alpha +// Warning: (ae-incompatible-release-tags) The symbol "Api" is marked as @public, but its signature references "LegacyCustomisationsApiExtension" which is marked as @alpha +// +// @public +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension, DialogApiExtension, AccountAuthApiExtension, ProfileApiExtension { + // @alpha + readonly builtins: BuiltinsApi; + readonly client: ClientApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + // @alpha + readonly customComponents: CustomComponentsApi; + // @alpha + readonly customisations: CustomisationsApi; + // @alpha + readonly extras: ExtrasApi; + readonly i18n: I18nApi; + readonly navigation: NavigationApi; + readonly rootNode: HTMLElement; + readonly stores: StoresApi; + // @alpha + readonly widget: WidgetApi; + // @alpha + readonly widgetLifecycle: WidgetLifecycleApi; +} + +// @alpha +export interface BuiltinsApi { + renderNotificationDecoration(roomId: string): React.ReactNode; + renderRoomAvatar(roomId: string, size?: string): React.ReactNode; + renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode; +} + +// @alpha +export type CapabilitiesApprover = (widget: WidgetDescriptor, requestedCapabilities: Set) => MaybePromise | undefined>; + +// @alpha @deprecated (undocumented) +export interface ChatExportCustomisations { + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + numberOfMessages?: number; + includeAttachments?: boolean; + sizeMb?: number; + }; +} + +// @public +export interface ClientApi { + accountData: AccountDataApi; + getRoom: (id: string) => Room | null; +} + +// @alpha @deprecated (undocumented) +export interface ComponentVisibilityCustomisations { + shouldShowComponent?(component: "UIComponent.sendInvites" | "UIComponent.roomCreation" | "UIComponent.spaceCreation" | "UIComponent.exploreRooms" | "UIComponent.addIntegrations" | "UIComponent.filterContainer" | "UIComponent.roomOptionsMenu"): boolean; +} + +// @public +export interface Config { + // (undocumented) + brand: string; +} + +// @public +export interface ConfigApi { + // (undocumented) + get(): Config; + // (undocumented) + get(key: K): Config[K]; + // (undocumented) + get(key?: K): Config | Config[K]; +} + +// @alpha +export type Container = "top" | "right" | "center"; + +// @alpha +export interface CustomComponentsApi { + registerLoginComponent(renderer: CustomLoginRenderFunction): void; + registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; + registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; +} + +// @alpha +export interface CustomisationsApi { + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} + +// @alpha +export type CustomLoginComponentProps = { + serverConfig: CustomLoginComponentPropsServerConfig; + fragmentAfterLogin?: string; + children?: ReactNode; + onLoggedIn(data: AccountAuthInfo): void; + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +// @alpha +export interface CustomLoginComponentPropsServerConfig { + hsName: string; + hsUrl: string; +} + +// @alpha +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + +// @alpha +export type CustomMessageComponentProps = { + mxEvent: MatrixEvent; +}; + +// @alpha +export type CustomMessageRenderFunction = ( +props: CustomMessageComponentProps, +originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element) => JSX.Element; + +// @alpha +export type CustomMessageRenderHints = { + allowEditingEvent?: boolean; + allowDownloadingMedia?: (mxEvent: MatrixEvent) => Promise; +}; + +// @alpha +export type CustomRoomPreviewBarComponentProps = { + roomId?: string; + roomAlias?: string; +}; + +// @alpha +export type CustomRoomPreviewBarRenderFunction = ( +props: CustomRoomPreviewBarComponentProps, +originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element) => JSX.Element; + +// @public +export interface DialogApiExtension { + openDialog(initialOptions: DialogOptions, dialog: ComponentType

>, props: P): DialogHandle; +} + +// @public +export type DialogHandle = { + finished: Promise<{ + ok: boolean; + model: M | null; + }>; + close(): void; +}; + +// @public +export interface DialogOptions { + title: string; +} + +// @public +export type DialogProps = { + onSubmit(model: M): void; + onCancel(): void; +}; + +// @alpha @deprecated (undocumented) +export interface DirectoryCustomisations { + // (undocumented) + requireCanonicalAliasAccessToPublish?(): boolean; +} + +// @alpha +export type ExtendablePropsRenderFunction =

( +props: P, +originalComponent: (props: P) => JSX.Element) => JSX.Element; + +// @alpha +export interface ExtrasApi { + addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; + getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; + setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; +} + +// @public +export interface I18nApi { + humanizeTime(this: void, timeMillis: number): string; + get language(): string; + register(this: void, translations: Partial): void; + translate(this: void, key: keyof Translations, variables?: Variables): string; + translate(this: void, key: keyof Translations, variables: Variables | undefined, tags: Tags): ReactNode; +} + +// @alpha +export type IdentityApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @alpha @deprecated (undocumented) +export type LegacyCustomisations = (customisations: T) => void; + +// @alpha @deprecated (undocumented) +export interface LegacyCustomisationsApiExtension { + // @deprecated (undocumented) + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + // @deprecated (undocumented) + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations>; + // @deprecated (undocumented) + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} + +// @alpha @deprecated (undocumented) +export interface LegacyModuleApiExtension { + // @deprecated + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} + +// @alpha @deprecated (undocumented) +export interface LifecycleCustomisations { + // (undocumented) + onLoggedOutAndStorageCleared?(): void; +} + +// @alpha +export type LocationRenderFunction = () => JSX.Element; + +// @alpha +export interface MatrixEvent { + content: Record; + eventId: string; + originServerTs: number; + roomId: string; + sender: string; + stateKey?: string; + type: string; + unsigned: Record; +} + +// @public +export type MaybePromise = T | PromiseLike; + +// @alpha @deprecated (undocumented) +export interface Media { + // (undocumented) + downloadSource(): Promise; + // (undocumented) + getSquareThumbnailHttp(dim: number): string | null; + // (undocumented) + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + // (undocumented) + readonly hasThumbnail: boolean; + // (undocumented) + readonly isEncrypted: boolean; + // (undocumented) + readonly srcHttp: string | null; + // (undocumented) + readonly srcMxc: string; + // (undocumented) + readonly thumbnailHttp: string | null; + // (undocumented) + readonly thumbnailMxc: string | null | undefined; +} + +// @alpha @deprecated (undocumented) +export interface MediaContructable { + // (undocumented) + new (prepared: PreparedMedia): Media; +} + +// @alpha @deprecated (undocumented) +export interface MediaCustomisations { + // (undocumented) + readonly Media: MediaContructable; + // (undocumented) + mediaFromContent(content: Content, client?: Client): Media; + // (undocumented) + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +// @public +export interface Module { + // (undocumented) + load(): Promise; +} + +// @public +export interface ModuleFactory { + // (undocumented) + new (api: Api): Module; + // (undocumented) + readonly moduleApiVersion: string; + // (undocumented) + readonly prototype: Module; +} + +// @public +export class ModuleIncompatibleError extends Error { + constructor(pluginVersion: string); +} + +// @public +export class ModuleLoader { + constructor(api: Api); + // Warning: (ae-forgotten-export) The symbol "ModuleExport" needs to be exported by the entry point index.d.ts + // + // (undocumented) + load(moduleExport: ModuleExport): Promise; + // (undocumented) + start(): Promise; +} + +// @public +export interface NavigationApi { + openRoom(roomIdOrAlias: string, opts?: OpenRoomOptions): void; + // @alpha + registerLocationRenderer(path: string, renderer: LocationRenderFunction): void; + toMatrixToLink(link: string, join?: boolean): Promise; +} + +// @public +export interface OpenRoomOptions { + autoJoin?: boolean; + viaServers?: string[]; +} + +// @alpha +export type OriginalMessageComponentProps = { + showUrlPreview?: boolean; +}; + +// @alpha +export type PreloadApprover = (widget: WidgetDescriptor) => MaybePromise; + +// @public +export interface Profile { + displayName?: string; + isGuest?: boolean; + userId?: string; +} + +// @public +export interface ProfileApiExtension { + readonly profile: Watchable; +} + +// @public +export interface Room { + getLastActiveTimestamp: () => number; + id: string; + name: Watchable; +} + +// @alpha +export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined; + +// @alpha @deprecated (undocumented) +export interface RoomListCustomisations { + isRoomVisible?(room: Room): boolean; +} + +// @public +export interface RoomListStoreApi { + getRooms(): Watchable; + waitForReady(): Promise; +} + +// @alpha +export interface RoomViewProps { + enableReadReceiptsAndMarkersOnActivity?: boolean; + hideComposer?: boolean; + hideHeader?: boolean; + hidePinnedMessageBanner?: boolean; + hideRightPanel?: boolean; + hideWidgets?: boolean; +} + +// @alpha @deprecated (undocumented) +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +// @alpha +export interface SpacePanelItemProps { + className?: string; + icon?: JSX.Element; + label: string; + onSelected: () => void; + style?: React.CSSProperties; + tooltip?: string; +} + +// @public +export interface StoresApi { + roomListStore: RoomListStoreApi; +} + +// @public +export type SubstitutionValue = number | string | ReactNode | ((sub: string) => ReactNode); + +// @public +export type Tags = Record; + +// @public +export type Translations = Record; + +// @alpha +export const enum UIComponent { + AddIntegrations = "UIComponent.addIntegrations", + CreateRooms = "UIComponent.roomCreation", + CreateSpaces = "UIComponent.spaceCreation", + ExploreRooms = "UIComponent.exploreRooms", + FilterContainer = "UIComponent.filterContainer", + InviteUsers = "UIComponent.sendInvites", + RoomOptionsMenu = "UIComponent.roomOptionsMenu" +} + +// @alpha @deprecated (undocumented) +export interface UserIdentifierCustomisations { + getDisplayUserIdentifier(userId: string, opts: { + roomId?: string; + withDisplayName?: boolean; + }): string | null; +} + +// @public +export function useWatchable(watchable: Watchable): T; + +// @public +export type Variables = { + count?: number; + [key: string]: SubstitutionValue; +}; + +// @public +export class Watchable { + constructor(currentValue: T); + // Warning: (ae-forgotten-export) The symbol "WatchFn" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly listeners: Set>; + protected onFirstWatch(): void; + protected onLastWatch(): void; + // (undocumented) + unwatch(listener: (value: T) => void): void; + get value(): T; + set value(value: T); + // (undocumented) + watch(listener: (value: T) => void): void; +} + +// @alpha +export interface WidgetApi { + getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null; + getWidgetsInRoom(roomId: string): IWidget[]; + isAppInContainer(app: IWidget, container: Container, roomId: string): boolean; + moveAppToContainer(app: IWidget, container: Container, roomId: string): void; +} + +// @alpha +export type WidgetDescriptor = { + id: string; + templateUrl: string; + creatorUserId: string; + type: string; + origin: string; + roomId?: string; +}; + +// @alpha +export interface WidgetLifecycleApi { + registerCapabilitiesApprover(approver: CapabilitiesApprover): void; + registerIdentityApprover(approver: IdentityApprover): void; + registerPreloadApprover(approver: PreloadApprover): void; +} + +// @alpha @deprecated (undocumented) +export interface WidgetPermissionsCustomisations { + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +// @alpha @deprecated (undocumented) +export interface WidgetVariablesCustomisations { + isReady?(): Promise; + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/element-web-module-api/package.json b/packages/module-api/package.json similarity index 100% rename from packages/element-web-module-api/package.json rename to packages/module-api/package.json diff --git a/packages/element-web-module-api/src/@types/global.d.ts b/packages/module-api/src/@types/global.d.ts similarity index 100% rename from packages/element-web-module-api/src/@types/global.d.ts rename to packages/module-api/src/@types/global.d.ts diff --git a/packages/element-web-module-api/src/api/auth.ts b/packages/module-api/src/api/auth.ts similarity index 100% rename from packages/element-web-module-api/src/api/auth.ts rename to packages/module-api/src/api/auth.ts diff --git a/packages/element-web-module-api/src/api/builtins.ts b/packages/module-api/src/api/builtins.ts similarity index 100% rename from packages/element-web-module-api/src/api/builtins.ts rename to packages/module-api/src/api/builtins.ts diff --git a/packages/element-web-module-api/src/api/client.ts b/packages/module-api/src/api/client.ts similarity index 100% rename from packages/element-web-module-api/src/api/client.ts rename to packages/module-api/src/api/client.ts diff --git a/packages/element-web-module-api/src/api/config.ts b/packages/module-api/src/api/config.ts similarity index 100% rename from packages/element-web-module-api/src/api/config.ts rename to packages/module-api/src/api/config.ts diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/module-api/src/api/custom-components.ts similarity index 100% rename from packages/element-web-module-api/src/api/custom-components.ts rename to packages/module-api/src/api/custom-components.ts diff --git a/packages/element-web-module-api/src/api/customisations.ts b/packages/module-api/src/api/customisations.ts similarity index 100% rename from packages/element-web-module-api/src/api/customisations.ts rename to packages/module-api/src/api/customisations.ts diff --git a/packages/element-web-module-api/src/api/dialog.ts b/packages/module-api/src/api/dialog.ts similarity index 100% rename from packages/element-web-module-api/src/api/dialog.ts rename to packages/module-api/src/api/dialog.ts diff --git a/packages/element-web-module-api/src/api/extras.ts b/packages/module-api/src/api/extras.ts similarity index 100% rename from packages/element-web-module-api/src/api/extras.ts rename to packages/module-api/src/api/extras.ts diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/module-api/src/api/i18n.ts similarity index 100% rename from packages/element-web-module-api/src/api/i18n.ts rename to packages/module-api/src/api/i18n.ts diff --git a/packages/element-web-module-api/src/api/index.test.ts b/packages/module-api/src/api/index.test.ts similarity index 100% rename from packages/element-web-module-api/src/api/index.test.ts rename to packages/module-api/src/api/index.test.ts diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/module-api/src/api/index.ts similarity index 100% rename from packages/element-web-module-api/src/api/index.ts rename to packages/module-api/src/api/index.ts diff --git a/packages/element-web-module-api/src/api/legacy-customisations.ts b/packages/module-api/src/api/legacy-customisations.ts similarity index 100% rename from packages/element-web-module-api/src/api/legacy-customisations.ts rename to packages/module-api/src/api/legacy-customisations.ts diff --git a/packages/element-web-module-api/src/api/legacy-modules.ts b/packages/module-api/src/api/legacy-modules.ts similarity index 100% rename from packages/element-web-module-api/src/api/legacy-modules.ts rename to packages/module-api/src/api/legacy-modules.ts diff --git a/packages/element-web-module-api/src/api/navigation.ts b/packages/module-api/src/api/navigation.ts similarity index 100% rename from packages/element-web-module-api/src/api/navigation.ts rename to packages/module-api/src/api/navigation.ts diff --git a/packages/element-web-module-api/src/api/profile.ts b/packages/module-api/src/api/profile.ts similarity index 100% rename from packages/element-web-module-api/src/api/profile.ts rename to packages/module-api/src/api/profile.ts diff --git a/packages/element-web-module-api/src/api/stores.ts b/packages/module-api/src/api/stores.ts similarity index 100% rename from packages/element-web-module-api/src/api/stores.ts rename to packages/module-api/src/api/stores.ts diff --git a/packages/element-web-module-api/src/api/watchable.test.ts b/packages/module-api/src/api/watchable.test.ts similarity index 100% rename from packages/element-web-module-api/src/api/watchable.test.ts rename to packages/module-api/src/api/watchable.test.ts diff --git a/packages/element-web-module-api/src/api/watchable.ts b/packages/module-api/src/api/watchable.ts similarity index 100% rename from packages/element-web-module-api/src/api/watchable.ts rename to packages/module-api/src/api/watchable.ts diff --git a/packages/element-web-module-api/src/api/widget-lifecycle.ts b/packages/module-api/src/api/widget-lifecycle.ts similarity index 100% rename from packages/element-web-module-api/src/api/widget-lifecycle.ts rename to packages/module-api/src/api/widget-lifecycle.ts diff --git a/packages/element-web-module-api/src/api/widget.ts b/packages/module-api/src/api/widget.ts similarity index 100% rename from packages/element-web-module-api/src/api/widget.ts rename to packages/module-api/src/api/widget.ts diff --git a/packages/element-web-module-api/src/index.ts b/packages/module-api/src/index.ts similarity index 100% rename from packages/element-web-module-api/src/index.ts rename to packages/module-api/src/index.ts diff --git a/packages/element-web-module-api/src/loader.test.ts b/packages/module-api/src/loader.test.ts similarity index 100% rename from packages/element-web-module-api/src/loader.test.ts rename to packages/module-api/src/loader.test.ts diff --git a/packages/element-web-module-api/src/loader.ts b/packages/module-api/src/loader.ts similarity index 100% rename from packages/element-web-module-api/src/loader.ts rename to packages/module-api/src/loader.ts diff --git a/packages/element-web-module-api/src/models/Room.ts b/packages/module-api/src/models/Room.ts similarity index 100% rename from packages/element-web-module-api/src/models/Room.ts rename to packages/module-api/src/models/Room.ts diff --git a/packages/element-web-module-api/src/models/event.ts b/packages/module-api/src/models/event.ts similarity index 100% rename from packages/element-web-module-api/src/models/event.ts rename to packages/module-api/src/models/event.ts diff --git a/packages/element-web-module-api/src/utils.ts b/packages/module-api/src/utils.ts similarity index 100% rename from packages/element-web-module-api/src/utils.ts rename to packages/module-api/src/utils.ts diff --git a/packages/element-web-module-api/tsconfig.json b/packages/module-api/tsconfig.json similarity index 100% rename from packages/element-web-module-api/tsconfig.json rename to packages/module-api/tsconfig.json diff --git a/packages/element-web-module-api/vite.config.ts b/packages/module-api/vite.config.ts similarity index 100% rename from packages/element-web-module-api/vite.config.ts rename to packages/module-api/vite.config.ts